added support for "Class.member" syntax
[swftools.git] / lib / as3 / parser.y
index a4385a9..08900d1 100644 (file)
@@ -39,7 +39,8 @@
 %error-verbose
 
 %union tokenunion {
-    tokenptr_t token;
+    enum yytokentype token;
+    int flags;
 
     classinfo_t*classinfo;
     classinfo_list_t*classinfo_list;
     typedcode_list_t*value_list;
     param_t* param;
     params_t params;
-    char*string;
+    string_t str;
+    char*id;
+    constant_t*constant;
 }
 
 
-%token<token> T_IDENTIFIER
-%token<string> T_STRING
+%token<id> T_IDENTIFIER
+%token<str> T_STRING
 %token<token> T_REGEXP
 %token<token> T_EMPTY
 %token<number_int> T_INT
 %token<token> T_GE ">="
 %token<token> T_DIVBY "/=" 
 %token<token> T_MODBY "%="
+%token<token> T_MULBY "*="
 %token<token> T_PLUSBY "+=" 
 %token<token> T_MINUSBY "-="
 %token<token> T_SHRBY ">>="
 %token<token> T_STAR '*'
 %token<token> T_DOT '.'
 
-%type <token> X_IDENTIFIER VARCONST
+%type <id> X_IDENTIFIER PACKAGE
+%type <token> VARCONST
 %type <code> CODE
 %type <code> CODEPIECE
 %type <code> CODEBLOCK MAYBECODE
 %type <param> PARAM
 %type <params> PARAM_LIST
 %type <params> MAYBE_PARAM_LIST
-%type <token> MODIFIERS
-%type <token> MODIFIER_LIST
+%type <flags> MAYBE_MODIFIERS
+%type <flags> MODIFIER_LIST
+%type <constant> STATICCONSTANT MAYBESTATICCONSTANT
 %type <classinfo_list> IMPLEMENTS_LIST
 %type <classinfo> EXTENDS
 %type <classinfo_list> EXTENDS_LIST
 %type <value> NEW
 //%type <token> T_IDENTIFIER
 %type <token> MODIFIER
-%type <token> PACKAGE
 %type <value> FUNCTIONCALL
 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES
 
 
 %left prec_none
 %right '?' ':'
-%nonassoc '='
-%nonassoc "/=" "%="
-%nonassoc "+=" "-="
-%nonassoc ">>="
-%nonassoc "<<="
-%nonassoc ">>>="
+%right '=' "/=" "%=" "*=" "+=" "-=" ">>=" "<<=" ">>>="
 %left "||"
 %left "&&"
 %nonassoc '|'
@@ -227,30 +227,6 @@ static int yyerror(char*s)
 {
    syntaxerror("%s", s); 
 }
-static token_t* concat2(token_t* t1, token_t* t2)
-{
-    NEW(token_t,t);
-    int l1 = strlen(t1->text);
-    int l2 = strlen(t2->text);
-    t->text = malloc(l1+l2+1);
-    memcpy(t->text   , t1->text, l1);
-    memcpy(t->text+l1, t2->text, l2);
-    t->text[l1+l2] = 0;
-    return t;
-}
-static token_t* concat3(token_t* t1, token_t* t2, token_t* t3)
-{
-    NEW(token_t,t);
-    int l1 = strlen(t1->text);
-    int l2 = strlen(t2->text);
-    int l3 = strlen(t3->text);
-    t->text = malloc(l1+l2+l3+1);
-    memcpy(t->text   , t1->text, l1);
-    memcpy(t->text+l1, t2->text, l2);
-    memcpy(t->text+l1+l2, t3->text, l3);
-    t->text[l1+l2+l3] = 0;
-    return t;
-}
 static char* concat3str(const char* t1, const char* t2, const char* t3)
 {
     int l1 = strlen(t1);
@@ -294,13 +270,17 @@ typedef struct _state {
     
     /* method data */
     memberinfo_t*minfo;
-    abc_method_body_t*m;
+    abc_method_t*m;
 
-    array_t*vars;
-    int local_var_base;
+    dict_t*vars;
     char late_binding;
 } state_t;
 
+typedef struct _global {
+    int variable_count;
+} global_t;
+
+static global_t*global = 0;
 static state_t* state = 0;
 
 DECLARE_LIST(state);
@@ -317,7 +297,22 @@ DECLARE_LIST(state);
     m##_l.namespace = &m##_ns; \
     multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
 
+static namespace_t ns1 = {ACCESS_PRIVATE, ""};
+static namespace_t ns2 = {ACCESS_PROTECTED, ""};
+static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
+static namespace_t ns4 = {ACCESS_PACKAGE, ""};
+static namespace_list_t nl4 = {&ns4,0};
+static namespace_list_t nl3 = {&ns3,&nl4};
+static namespace_list_t nl2 = {&ns2,&nl3};
+static namespace_list_t nl1 = {&ns1,&nl2};
+static namespace_set_t nopackage_namespace_set = {&nl1};
+
 static state_list_t*state_stack=0;
+    
+static void init_globals()
+{
+    global = rfx_calloc(sizeof(global_t));
+}
 
 static void new_state()
 {
@@ -329,16 +324,13 @@ static void new_state()
         memcpy(s, state, sizeof(state_t)); //shallow copy
     sl->next = state_stack;
     sl->state = s;
-    if(oldstate) {
-        s->local_var_base = array_length(oldstate->vars) + oldstate->local_var_base;
-    }
     if(!s->imports) {
         s->imports = dict_new();
     }
     state_stack = sl;
     state = s;
     state->level++;
-    state->vars = array_new();
+    state->vars = dict_new();
     state->initcode = 0;
     state->has_own_imports = 0;
 }
@@ -370,12 +362,13 @@ static void old_state()
 }
 void initialize_state()
 {
+    init_globals();
     new_state();
 
     state->file = abc_file_new();
     state->file->flags &= ~ABCFILE_LAZY;
     
-    state->init = abc_initscript(state->file, 0, 0);
+    state->init = abc_initscript(state->file, 0);
     code_t*c = state->init->method->body->code;
 
     c = abc_getlocal_0(c);
@@ -429,13 +422,12 @@ void* finalize_state()
 }
 
 
-static void startpackage(token_t*t) 
+static void startpackage(char*name)
 {
     if(state->package) {
         syntaxerror("Packages can not be nested."); 
     } 
     new_state();
-    char*name = t?t->text:"";
     /*printf("entering package \"%s\"\n", name);*/
     state->package = name;
 }
@@ -446,18 +438,17 @@ static void endpackage()
 }
 
 char*globalclass=0;
-static void startclass(token_t*modifiers, token_t*name, classinfo_t*extends, classinfo_list_t*implements, char interface)
+static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
 {
     if(state->cls) {
         syntaxerror("inner classes now allowed"); 
     }
     new_state();
-    char*classname = name->text;
 
     token_list_t*t=0;
     classinfo_list_t*mlist=0;
-    /*printf("entering class %s\n", name->text);
-    printf("  modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token->text);printf("\n");
+    /*printf("entering class %s\n", name);
+    printf("  modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token);printf("\n");
     if(extends) 
         printf("  extends: %s.%s\n", extends->package, extends->name);
     printf("  implements (%d): ", list_length(implements));
@@ -467,30 +458,19 @@ static void startclass(token_t*modifiers, token_t*name, classinfo_t*extends, cla
     printf("\n");
     */
 
-    char public=0,internal=0,final=0,sealed=1;
-    for(t=modifiers->tokens;t;t=t->next) {
-        if(t->token->type == KW_INTERNAL) {
-            /* the programmer is being explicit- 
-               being internal is the default anyway */
-            internal = 1;
-        } else if(t->token->type == KW_PUBLIC) {
-            public = 1;
-        } else if(t->token->type == KW_FINAL) {
-            final = 1;
-        } else {
-            syntaxerror("modifier \"%s\" not supported in class declaration", t->token->text);
-        }
-    }
-    if(public&&internal)
+    if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL))
+        syntaxerror("invalid modifier(s)");
+
+    if((flags&(FLAG_PUBLIC|FLAG_INTERNAL)) == (FLAG_PUBLIC|FLAG_INTERNAL))
         syntaxerror("public and internal not supported at the same time.");
 
     /* create the class name, together with the proper attributes */
     int access=0;
     char*package=0;
 
-    if(!public && !state->package) {
+    if(!(flags&FLAG_PUBLIC) && !state->package) {
         access = ACCESS_PRIVATE; package = current_filename;
-    } else if(!public && state->package) {
+    } else if(!(flags&FLAG_PUBLIC) && state->package) {
         access = ACCESS_PACKAGEINTERNAL; package = state->package;
     } else if(state->package) {
         access = ACCESS_PACKAGE; package = state->package;
@@ -501,8 +481,16 @@ static void startclass(token_t*modifiers, token_t*name, classinfo_t*extends, cla
     if(registry_findclass(package, classname)) {
         syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
     }
-    
-    state->clsinfo = classinfo_register(access, package, classname);
+   
+    /* build info struct */
+    int num_interfaces = (list_length(implements));
+    state->clsinfo = classinfo_register(access, package, classname, num_interfaces);
+    state->clsinfo->superclass = extends;
+    int pos = 0;
+    classinfo_list_t*l = implements;
+    for(l=implements;l;l=l->next) {
+        state->clsinfo->interfaces[pos++] = l->classinfo;
+    }
 
     MULTINAME(classname2,state->clsinfo);
     
@@ -514,8 +502,8 @@ static void startclass(token_t*modifiers, token_t*name, classinfo_t*extends, cla
     }*/
 
     state->cls = abc_class_new(state->file, &classname2, extends2);
-    if(final) abc_class_final(state->cls);
-    if(sealed) abc_class_sealed(state->cls);
+    if(flags&FLAG_FINAL) abc_class_final(state->cls);
+    if(flags&FLAG_DYNAMIC) abc_class_sealed(state->cls);
     if(interface) abc_class_interface(state->cls);
 
     for(mlist=implements;mlist;mlist=mlist->next) {
@@ -572,7 +560,7 @@ static void startclass(token_t*modifiers, token_t*name, classinfo_t*extends, cla
     __ setslot(m, slotindex);
 
     /* flash.display.MovieClip handling */
-    if(!globalclass && public && classinfo_equals(registry_getMovieClip(),extends)) {
+    if(!globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
         if(state->package && state->package[0]) {
             globalclass = concat3str(state->package, ".", classname);
         } else {
@@ -586,9 +574,9 @@ static void endclass()
 {
     if(state->cls_init) {
         if(!state->cls->constructor) {
-            abc_method_body_t*m = abc_class_constructor(state->cls, 0, 0);
-            m->code = code_append(m->code, state->cls_init);
-            m->code = abc_returnvoid(m->code);
+            abc_method_t*m = abc_class_constructor(state->cls, 0);
+            m->body->code = code_append(m->body->code, state->cls_init);
+            m->body->code = abc_returnvoid(m->body->code);
         } else {
             code_t*c = state->cls->constructor->body->code;
             c = code_append(state->cls_init, c);
@@ -598,8 +586,9 @@ static void endclass()
     }
     if(state->cls_static_init) {
         if(!state->cls->static_constructor) {
-            abc_method_body_t*m = abc_class_staticconstructor(state->cls, 0, 0);
-            m->code = state->cls_static_init;
+            abc_method_t*m = abc_class_staticconstructor(state->cls, 0);
+            m->body->code = code_append(m->body->code, state->cls_static_init);
+            m->body->code = abc_returnvoid(m->body->code);
         } else {
             state->cls->static_constructor->body->code = 
                 code_append(state->cls_static_init, state->cls->static_constructor->body->code);
@@ -608,111 +597,22 @@ static void endclass()
 
     old_state();
 }
-static void startfunction(token_t*ns, token_t*mod, token_t*getset, token_t*name,
-                          params_t*params, classinfo_t*type)
-{
-    token_list_t*t;
-    new_state();
-    state->function = name->text;
-    
-    if(state->m) {
-        syntaxerror("not able to start another method scope");
-    }
-
-    multiname_t*type2 = sig2mname(type);
-    if(!strcmp(state->clsinfo->name,name->text)) {
-        state->m = abc_class_constructor(state->cls, type2, 0);
-    } else {
-        state->minfo = memberinfo_register(state->clsinfo, name->text, MEMBER_METHOD);
-        state->m = abc_class_method(state->cls, type2, name->text, 0);
-        // getslot on a member slot only returns "undefined", so no need
-        // to actually store these
-        //state->minfo->slot = state->m->method->trait->slot_id;
-    }
-    if(getset->type == KW_GET) {
-        state->m->method->trait->kind = TRAIT_GETTER;
-    }
-    if(getset->type == KW_SET) {
-        state->m->method->trait->kind = TRAIT_SETTER;
-    }
-    if(params->varargs) {
-        state->m->method->flags |= METHOD_NEED_REST;
-    }
 
-    param_list_t*p;
-    for(p=params->list;p;p=p->next) {
-        if(params->varargs && !p->next) {
-            break; //varargs: omit last parameter in function signature
-        }
-        multiname_t*m = sig2mname(p->param->type);
-       list_append(state->m->method->parameters, m);
-    }
-
-    /* state->vars is initialized by state_new */
-    array_append(state->vars, "this", state->clsinfo);
-    for(p=params->list;p;p=p->next) {
-        array_append(state->vars, p->param->name, 0);
-    }
-}
-static void endfunction(code_t*body)
-{
-    code_t*c = 0;
-    if(state->late_binding) {
-        c = abc_getlocal_0(c);
-        c = abc_pushscope(c);
-    }
-    c = code_append(c, state->initcode);
-    c = code_append(c, body);
-
-    /* append return if necessary */
-    if(!c || c->opcode != OPCODE_RETURNVOID && 
-             c->opcode != OPCODE_RETURNVALUE)
-        c = abc_returnvoid(c);
-    
-    if(state->m->code) syntaxerror("internal error");
-    state->m->code = c;
-    old_state();
-}
-
-
-static token_t* empty_token()
-{
-    NEW(token_t,t);
-    t->type=T_EMPTY;
-    t->text=0;
-    return t;
-}
-
-void extend(token_t*list, token_t*add) {
-    list_append(list->tokens,add);
-    if(!list->text)
-        list->text = add->text;
-}
-void extend_s(token_t*list, char*seperator, token_t*add) {
-    list_append(list->tokens,add);
-    char*t1 = list->text;
-    char*t2 = seperator;
-    char*t3 = add->text;
-    int l1 = strlen(t1);
-    int l2 = strlen(t2);
-    int l3 = strlen(t3);
-    list->text = malloc(l1+l2+l3+1);
-    strcpy(list->text, t1);
-    strcpy(list->text+l1, t2);
-    strcpy(list->text+l1+l2, t3);
-    list->text[l1+l2+l3]=0;
-}
+typedef struct _variable {
+    int index;
+    classinfo_t*type;
+} variable_t;
 
 static int find_variable(char*name, classinfo_t**m)
 {
     state_list_t* s = state_stack;
     while(s) {
-        int i = array_find(s->state->vars, name);
-        if(i>=0) {
+        variable_t*v = dict_lookup(s->state->vars, name);
+        if(v) {
             if(m) {
-                *m = array_getvalue(s->state->vars, i);
+                *m = v->type;
             }
-            return i + s->state->local_var_base;
+            return v->index;
         }
         s = s->next;
     }
@@ -727,11 +627,15 @@ static int find_variable_safe(char*name, classinfo_t**m)
 }
 static char variable_exists(char*name) 
 {
-    return array_contains(state->vars, name);
+    return dict_lookup(state->vars, name)!=0;
 }
 static int new_variable(char*name, classinfo_t*type)
 {
-    return array_append(state->vars, name, type) + state->local_var_base;
+    NEW(variable_t, v);
+    v->index = global->variable_count;
+    v->type = type;
+    dict_put(state->vars, name, v);
+    return global->variable_count++;
 }
 #define TEMPVARNAME "__as3_temp__"
 static int gettempvar()
@@ -747,17 +651,184 @@ static int gettempvar()
 code_t* killvars(code_t*c) 
 {
     int t;
-    for(t=0;t<state->vars->num;t++) {
-        classinfo_t*type = array_getvalue(state->vars, t);
-        //do this always, otherwise register types don't match
-        //in the verifier when doing nested loops
-        //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
-            c = abc_kill(c, t+state->local_var_base);
-        //}
+    for(t=0;t<state->vars->hashsize;t++) {
+        dictentry_t*e =state->vars->slots[t];
+        while(e) {
+            variable_t*v = (variable_t*)e->data;
+            //do this always, otherwise register types don't match
+            //in the verifier when doing nested loops
+            //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
+            c = abc_kill(c, v->index);
+            e = e->next;
+        }
     }
     return c;
 }
 
+
+static void check_constant_against_type(classinfo_t*t, constant_t*c)
+{
+#define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
+   if(TYPE_IS_NUMBER(t)) {
+        xassert(c->type == CONSTANT_FLOAT
+             || c->type == CONSTANT_INT
+             || c->type == CONSTANT_UINT);
+   } else if(TYPE_IS_UINT(t)) {
+        xassert(c->type == CONSTANT_UINT ||
+               (c->type == CONSTANT_INT && c->i>0));
+   } else if(TYPE_IS_INT(t)) {
+        xassert(c->type == CONSTANT_INT);
+   } else if(TYPE_IS_BOOLEAN(t)) {
+        xassert(c->type == CONSTANT_TRUE
+             || c->type == CONSTANT_FALSE);
+   }
+}
+
+static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
+{
+    memberinfo_t*minfo = 0;
+    if(getset != KW_GET && getset != KW_SET) {
+        if(registry_findmember(state->clsinfo, name)) {
+            syntaxerror("class already contains a member/method called '%s'", name);
+        }
+        minfo = memberinfo_register(state->clsinfo, name, MEMBER_METHOD);
+        minfo->return_type = return_type;
+        // getslot on a member slot only returns "undefined", so no need
+        // to actually store these
+        //state->minfo->slot = state->m->method->trait->slot_id;
+    } else {
+        int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
+        classinfo_t*type=0;
+        if(getset == KW_GET)
+            type = return_type;
+        else if(params->list)
+            type = params->list->param->type;
+        if((minfo=registry_findmember(state->clsinfo, name))) {
+            if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
+                syntaxerror("class already contains a member or method called '%s'", name);
+            if(minfo->kind & gs)
+                syntaxerror("getter/setter for '%s' already defined", name);
+            /* make a setter or getter into a getset */
+            minfo->kind |= gs;
+            if(!minfo->type) 
+                minfo->type = type;
+            else
+                if(type && minfo->type != type)
+                    syntaxerror("different type in getter and setter");
+        } else {
+            minfo = memberinfo_register(state->clsinfo, name, gs);
+            minfo->type = type;
+        }
+        /* can't assign a slot as getter and setter might have different slots */
+        //minfo->slot = slot;
+    }
+    if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
+    if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
+    if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
+    if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
+    if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL;
+    return minfo;
+}
+
+static int flags2access(int flags)
+{
+    int access = 0;
+    if(flags&FLAG_PUBLIC)  {
+        if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
+        access = ACCESS_PACKAGE;
+    } else if(flags&FLAG_PRIVATE) {
+        if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
+        access = ACCESS_PRIVATE;
+    } else if(flags&FLAG_PROTECTED) {
+        if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
+        access = ACCESS_PROTECTED;
+    } else {
+        access = ACCESS_PACKAGEINTERNAL;
+    }
+    return access;
+}
+
+static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
+                          params_t*params, classinfo_t*return_type)
+{
+    token_list_t*t;
+    new_state();
+    global->variable_count = 0;
+    state->function = name;
+    
+    if(state->m) {
+        syntaxerror("not able to start another method scope");
+    }
+
+    namespace_t mname_ns = {flags2access(flags), ""};
+    multiname_t mname = {QNAME, &mname_ns, 0, name};
+
+    multiname_t*type2 = sig2mname(return_type);
+    int slot = 0;
+    if(!strcmp(state->clsinfo->name,name)) {
+        state->m = abc_class_constructor(state->cls, type2);
+        name = "__as3_constructor__";
+    } else {
+        if(flags&FLAG_STATIC)
+            state->m = abc_class_staticmethod(state->cls, type2, &mname);
+        else
+            state->m = abc_class_method(state->cls, type2, &mname);
+        slot = state->m->trait->slot_id;
+    }
+    state->minfo = registerfunction(getset, flags, name, params, return_type, slot);
+
+    if(getset == KW_GET) state->m->trait->kind = TRAIT_GETTER;
+    if(getset == KW_SET) state->m->trait->kind = TRAIT_SETTER;
+    if(params->varargs) state->m->flags |= METHOD_NEED_REST;
+
+    char opt=0;
+    param_list_t*p=0;
+    for(p=params->list;p;p=p->next) {
+        if(params->varargs && !p->next) {
+            break; //varargs: omit last parameter in function signature
+        }
+        multiname_t*m = sig2mname(p->param->type);
+       list_append(state->m->parameters, m);
+        if(p->param->value) {
+            check_constant_against_type(p->param->type, p->param->value);
+            opt=1;list_append(state->m->optional_parameters, p->param->value);
+        } else if(opt) {
+            syntaxerror("non-optional parameter not allowed after optional parameters");
+        }
+    }
+
+    /* state->vars is initialized by state_new */
+    if(new_variable((flags&FLAG_STATIC)?"class":"this", state->clsinfo)!=0) syntaxerror("Internal error");
+
+    for(p=params->list;p;p=p->next) {
+        new_variable(p->param->name, p->param->type);
+    }
+}
+static void endfunction(code_t*body)
+{
+        
+    if(!(state->cls->flags & CLASS_INTERFACE)) {
+        code_t*c = 0;
+        if(state->late_binding) {
+            c = abc_getlocal_0(c);
+            c = abc_pushscope(c);
+        }
+        c = code_append(c, state->initcode);
+        c = code_append(c, body);
+
+        /* append return if necessary */
+        if(!c || c->opcode != OPCODE_RETURNVOID && 
+                 c->opcode != OPCODE_RETURNVALUE) {
+            c = abc_returnvoid(c);
+        }
+        if(state->m->body->code) syntaxerror("internal error");
+        state->m->body->code = c;
+    }
+    old_state();
+}
+
+
+
 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
 {
     return 1; // FIXME
@@ -791,18 +862,48 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
     if(from==to)
         return c;
     if(!to) {
-        /*TODO: can omit this if from is zero? */
         return abc_coerce_a(c);
     }
+    MULTINAME(m, to);
+    if(!from) {
+        // cast an "any" type to a specific type. subject to
+        // runtime exceptions
+        return abc_coerce2(c, &m);
+    }
+    
     if(TYPE_IS_NUMBER(from) && TYPE_IS_UINT(to)) {
-        MULTINAME(m, TYPE_UINT);
         return abc_coerce2(c, &m);
     }
     if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) {
-        MULTINAME(m, TYPE_INT);
         return abc_coerce2(c, &m);
     }
-    return c;
+    /* these are subject to overflow */
+    if(TYPE_IS_INT(from) && TYPE_IS_UINT(to)) {
+        return abc_coerce2(c, &m);
+    }
+    if(TYPE_IS_UINT(from) && TYPE_IS_INT(to)) {
+        return abc_coerce2(c, &m);
+    }
+
+    classinfo_t*supertype = from;
+    while(supertype) {
+        if(supertype == to) {
+             // target type is one of from's superclasses
+             return abc_coerce2(c, &m);
+        }
+        int t=0;
+        while(supertype->interfaces[t]) {
+            if(supertype->interfaces[t]==to) {
+                // to type is one of from's interfaces
+                return abc_coerce2(c, &m);
+            }
+            t++;
+        }
+        supertype = supertype->superclass;
+    }
+    if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
+        return c;
+    syntaxerror("can't convert type %s to %s", from->name, to->name);
 }
 
 code_t*defaultvalue(code_t*c, classinfo_t*type)
@@ -822,7 +923,12 @@ char is_pushundefined(code_t*c)
     return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
 }
 
-static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign)
+void parserassert(int b)
+{
+    if(!b) syntaxerror("internal error: assertion failed");
+}
+
+static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
 {
     /* converts this:
 
@@ -849,21 +955,27 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign)
         prefix = 0;
     }
 
+    char use_temp_var = readbefore;
+
     /* generate the write instruction, and maybe append a dup to the prefix code */
     code_t* write = abc_nop(0);
     if(r->opcode == OPCODE_GETPROPERTY) {
         write->opcode = OPCODE_SETPROPERTY;
         multiname_t*m = (multiname_t*)r->data[0];
         write->data[0] = multiname_clone(m);
-        if(m->type != QNAME)
+        if(m->type != QNAME && m->type != MULTINAME)
             syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname)");
-        if(!justassign)
+        if(!justassign) {
             prefix = abc_dup(prefix); // we need the object, too
+        }
+        use_temp_var = 1;
     } else if(r->opcode == OPCODE_GETSLOT) {
         write->opcode = OPCODE_SETSLOT;
         write->data[0] = r->data[0];
-        if(!justassign)
+        if(!justassign) {
             prefix = abc_dup(prefix); // we need the object, too
+        }
+        use_temp_var = 1;
     } else if(r->opcode == OPCODE_GETLOCAL) { 
         write->opcode = OPCODE_SETLOCAL;
         write->data[0] = r->data[0];
@@ -883,28 +995,64 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign)
     
     int temp = -1;
     if(!justassign) {
-        /* read value, modify it with middle part, and write it again,
-           using prefix only once and making sure (by using a temporary
-           register) that the return value is what we just wrote */
-        temp = gettempvar();
-        c = code_append(c, prefix);
-        c = code_append(c, r);
-        c = code_append(c, middlepart);
-        c = abc_dup(c);
-        c = abc_setlocal(c, temp);
-        c = code_append(c, write);
-        c = abc_getlocal(c, temp);
-        c = abc_kill(c, temp);
+        if(use_temp_var) {
+            /* with getproperty/getslot, we have to be extra careful not
+               to execute the read code twice, as it might have side-effects
+               (e.g. if the property is in fact a setter/getter combination)
+
+               So read the value, modify it, and write it again,
+               using prefix only once and making sure (by using a temporary
+               register) that the return value is what we just wrote */
+            temp = gettempvar();
+            c = code_append(c, prefix);
+            c = code_append(c, r);
+            if(readbefore) {
+                c = abc_dup(c);
+                c = abc_setlocal(c, temp);
+            }
+            c = code_append(c, middlepart);
+            if(!readbefore) {
+                c = abc_dup(c);
+                c = abc_setlocal(c, temp);
+            }
+            c = code_append(c, write);
+            c = abc_getlocal(c, temp);
+            c = abc_kill(c, temp);
+        } else {
+            /* if we're allowed to execute the read code twice *and*
+               the middlepart doesn't modify the code, things are easier.
+            */
+            code_t* r2 = code_dup(r);
+            //c = code_append(c, prefix);
+            parserassert(!prefix);
+            c = code_append(c, r);
+            c = code_append(c, middlepart);
+            c = code_append(c, write);
+            c = code_append(c, r2);
+        }
     } else {
-        /* simpler version: overwrite the value without reading
+        /* even smaller version: overwrite the value without reading
            it out first */
-        if(prefix) {
-            c = code_append(c, prefix);
+        if(!use_temp_var) {
+            if(prefix) {
+                c = code_append(c, prefix);
+                c = abc_dup(c);
+            }
+            c = code_append(c, middlepart);
+            c = code_append(c, write);
+            c = code_append(c, r);
+        } else {
+            temp = gettempvar();
+            if(prefix) {
+                c = code_append(c, prefix);
+                c = abc_dup(c);
+            }
+            c = code_append(c, middlepart);
             c = abc_dup(c);
+            c = abc_setlocal(c, temp);
+            c = code_append(c, write);
+            c = abc_getlocal(c, temp);
         }
-        c = code_append(c, middlepart);
-        c = code_append(c, write);
-        c = code_append(c, r);
     }
 
     return c;
@@ -961,15 +1109,15 @@ VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
 
 ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
 {
-    if(variable_exists($2->text))
-        syntaxerror("Variable %s already defined", $2->text);
+    if(variable_exists($2))
+        syntaxerror("Variable %s already defined", $2);
    
     if(!is_subtype_of($4.t, $3)) {
         syntaxerror("Can't convert %s to %s", $4.t->name, 
                                               $3->name);
     }
 
-    int index = new_variable($2->text, $3);
+    int index = new_variable($2, $3);
     
     if($3) {
         if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
@@ -981,15 +1129,13 @@ ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
             $$ = abc_setlocal($$, index);
         }
 
-        /* push default value for type on stack */
-        state->initcode = defaultvalue(state->initcode, $3);
-        state->initcode = abc_setlocal(state->initcode, index);
+        /* if this is a typed variable:
+           push default value for type on stack */
+        if($3) {
+            state->initcode = defaultvalue(state->initcode, $3);
+            state->initcode = abc_setlocal(state->initcode, index);
+        }
     } else {
-        /* only bother to actually set this variable if its syntax is either
-            var x:type;
-           or
-            var x=expr;
-        */
         if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
             $$ = $4.c;
             $$ = abc_coerce_a($$);
@@ -1004,7 +1150,7 @@ ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
         state->initcode = abc_pushundefined(state->initcode);
         state->initcode = abc_setlocal(state->initcode, index);
     }*/
-    printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
+    //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
 }
 
 /* ------------ control flow ------------------------- */
@@ -1075,13 +1221,13 @@ BREAK : "break" {
 /* ------------ packages and imports ---------------- */
 
 X_IDENTIFIER: T_IDENTIFIER
-            | "package"
+            | "package" {$$="package";}
 
-PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,$2,$3);}
+PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3str($1,".",$3);}
 PACKAGE: X_IDENTIFIER             {$$=$1;}
 
 PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2)} MAYBECODE '}' {endpackage()}
-PACKAGE_DECLARATION : "package" '{' {startpackage(0)} MAYBECODE '}' {endpackage()}
+PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBECODE '}' {endpackage()}
 
 IMPORT : "import" QNAME {
        classinfo_t*c = $2;
@@ -1093,7 +1239,7 @@ IMPORT : "import" QNAME {
 }
 IMPORT : "import" PACKAGE '.' '*' {
        NEW(import_t,i);
-       i->package = $2->text;
+       i->package = $2;
        state_has_imports();
        list_append(state->wildcard_imports, i);
        $$=0;
@@ -1101,11 +1247,20 @@ IMPORT : "import" PACKAGE '.' '*' {
 
 /* ------------ classes and interfaces (header) -------------- */
 
-MODIFIERS : {$$=empty_token();}
-MODIFIERS : MODIFIER_LIST {$$=$1}
-MODIFIER_LIST : MODIFIER MODIFIER_LIST {extend($2,$1);$$=$2;}
-MODIFIER_LIST : MODIFIER               {$$=empty_token();extend($$,$1);}
-MODIFIER : KW_PUBLIC | KW_PRIVATE | KW_PROTECTED | KW_STATIC | KW_DYNAMIC | KW_FINAL | KW_OVERRIDE | KW_NATIVE | KW_INTERNAL
+MAYBE_MODIFIERS : {$$=0;}
+MAYBE_MODIFIERS : MODIFIER_LIST {$$=$1}
+MODIFIER_LIST : MODIFIER               {$$=$1;}
+MODIFIER_LIST : MODIFIER_LIST MODIFIER {$$=$1|$2;}
+
+MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;}
+         | KW_PRIVATE {$$=FLAG_PRIVATE;}
+         | KW_PROTECTED {$$=FLAG_PROTECTED;}
+         | KW_STATIC {$$=FLAG_STATIC;}
+         | KW_DYNAMIC {$$=FLAG_DYNAMIC;}
+         | KW_FINAL {$$=FLAG_FINAL;}
+         | KW_OVERRIDE {$$=FLAG_OVERRIDE;}
+         | KW_NATIVE {$$=FLAG_NATIVE;}
+         | KW_INTERNAL {$$=FLAG_INTERNAL;}
 
 EXTENDS : {$$=registry_getobjectclass();}
 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
@@ -1116,13 +1271,13 @@ EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
 IMPLEMENTS_LIST : {$$=list_new();}
 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
 
-CLASS_DECLARATION : MODIFIERS "class" T_IDENTIFIER 
+CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER 
                               EXTENDS IMPLEMENTS_LIST 
                               '{' {startclass($1,$3,$4,$5, 0);} 
                               MAYBE_DECLARATION_LIST 
                               '}' {endclass();}
 
-INTERFACE_DECLARATION : MODIFIERS "interface" T_IDENTIFIER 
+INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER 
                               EXTENDS_LIST 
                               '{' {startclass($1,$3,0,$4,1);}
                               MAYBE_IDECLARATION_LIST 
@@ -1138,36 +1293,85 @@ DECLARATION : ';'
 DECLARATION : SLOT_DECLARATION
 DECLARATION : FUNCTION_DECLARATION
 
+MAYBE_IDECLARATION_LIST : 
+MAYBE_IDECLARATION_LIST : IDECLARATION_LIST
+IDECLARATION_LIST : IDECLARATION
+IDECLARATION_LIST : IDECLARATION_LIST IDECLARATION
+IDECLARATION : ';'
+IDECLARATION : "var" T_IDENTIFIER {
+    syntaxerror("variable declarations not allowed in interfaces");
+}
+IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
+    $1 |= FLAG_PUBLIC;
+    if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
+        syntaxerror("invalid method modifiers: interface methods always need to be public");
+    }
+    startfunction(0,$1,$3,$4,&$6,$8);
+    endfunction(0);
+}
+
 /* ------------ classes and interfaces (body, slots ) ------- */
 
 VARCONST: "var" | "const"
-SLOT_DECLARATION: MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
 
-    memberinfo_t* info = memberinfo_register(state->clsinfo, $3->text, MEMBER_SLOT);
+SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
+    int flags = $1;
+    memberinfo_t* info = memberinfo_register(state->clsinfo, $3, MEMBER_SLOT);
     info->type = $4;
-
+    info->flags = flags;
     trait_t*t=0;
-    if($4) {
-        MULTINAME(m, $4);
-        t=abc_class_slot(state->cls, $3->text, &m);
+
+    namespace_t mname_ns = {flags2access(flags), ""};
+    multiname_t mname = {QNAME, &mname_ns, 0, $3};
+
+    if(!(flags&FLAG_STATIC)) {
+        if($4) {
+            MULTINAME(m, $4);
+            t=abc_class_slot(state->cls, &mname, &m);
+        } else {
+            t=abc_class_slot(state->cls, &mname, 0);
+        }
+        info->slot = t->slot_id;
     } else {
-        t=abc_class_slot(state->cls, $3->text, 0);
-    }
-    if($2->type==KW_CONST) {
-        t->kind= TRAIT_CONST;
+        if($4) {
+            MULTINAME(m, $4);
+            t=abc_class_staticslot(state->cls, &mname, &m);
+        } else {
+            t=abc_class_staticslot(state->cls, &mname, 0);
+        }
+        info->slot = t->slot_id;
     }
-    info->slot = t->slot_id;
     if($5.c && !is_pushundefined($5.c)) {
         code_t*c = 0;
         c = abc_getlocal_0(c);
         c = code_append(c, $5.c);
         c = converttype(c, $5.t, $4);
         c = abc_setslot(c, t->slot_id);
-        //c = abc_setproperty(c, $3->text); 
-        state->cls_init = code_append(state->cls_init, c);
+        if(!(flags&FLAG_STATIC))
+            state->cls_init = code_append(state->cls_init, c);
+        else
+            state->cls_static_init = code_append(state->cls_static_init, c);
+    }
+    if($2==KW_CONST) {
+        t->kind= TRAIT_CONST;
     }
 }
 
+/* ------------ constants -------------------------------------- */
+
+MAYBESTATICCONSTANT: {$$=0;}
+MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
+
+STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
+STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
+STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
+STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
+STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
+//STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
+STATICCONSTANT : KW_TRUE {$$ = constant_new_true($1);}
+STATICCONSTANT : KW_FALSE {$$ = constant_new_false($1);}
+STATICCONSTANT : KW_NULL {$$ = constant_new_null($1);}
+
 /* ------------ classes and interfaces (body, functions) ------- */
 
 // non-vararg version
@@ -1199,16 +1403,24 @@ PARAM_LIST: PARAM {
     memset(&$$,0,sizeof($$));
     list_append($$.list, $1);
 }
-PARAM:  T_IDENTIFIER ':' TYPE {
+
+PARAM:  T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
      $$ = malloc(sizeof(param_t));
-     $$->name=$1->text;$$->type = $3;
+     $$->name=$1;
+     $$->type = $3;
+     $$->value = $4;
 }
-PARAM:  T_IDENTIFIER          {
+PARAM:  T_IDENTIFIER MAYBESTATICCONSTANT {
      $$ = malloc(sizeof(param_t));
-     $$->name=$1->text;$$->type = TYPE_ANY;
+     $$->name=$1;
+     $$->type = TYPE_ANY;
+     $$->value = $2;
 }
+GETSET : "get" {$$=$1;}
+       | "set" {$$=$1;}
+       |       {$$=0;}
 
-FUNCTION_DECLARATION: MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' 
+FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' 
                       MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}' 
 {
     if(!state->m) syntaxerror("internal error: undefined function");
@@ -1220,14 +1432,14 @@ FUNCTION_DECLARATION: MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_L
 CLASS: T_IDENTIFIER {
 
     /* try current package */
-    $$ = registry_findclass(state->package, $1->text);
+    $$ = registry_findclass(state->package, $1);
 
     /* try explicit imports */
-    dictentry_t* e = dict_get_slot(state->imports, $1->text);
+    dictentry_t* e = dict_get_slot(state->imports, $1);
     while(e) {
         if($$)
             break;
-        if(!strcmp(e->key, $1->text)) {
+        if(!strcmp(e->key, $1)) {
             $$ = (classinfo_t*)e->data;
         }
         e = e->next;
@@ -1238,27 +1450,40 @@ CLASS: T_IDENTIFIER {
     while(l) {
         if($$)
             break;
-        //printf("does package %s contain a class %s?\n", l->import->package, $1->text);
-        $$ = registry_findclass(l->import->package, $1->text);
+        //printf("does package %s contain a class %s?\n", l->import->package, $1);
+        $$ = registry_findclass(l->import->package, $1);
         l = l->next;
     }
 
     /* try global package */
     if(!$$) {
-        $$ = registry_findclass("", $1->text);
+        $$ = registry_findclass("", $1);
     }
 
-    if(!$$) syntaxerror("Could not find class %s\n", $1->text);
+    if(!$$) syntaxerror("Could not find class %s\n", $1);
 }
 
 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
-    $$ = registry_findclass($1->text, $3->text);
-    if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1->text, $3->text);
+    $$ = registry_findclass($1, $3);
+    if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
 }
 
 QNAME: PACKAGEANDCLASS
      | CLASS
 
+QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
+QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
+
+TYPE : QNAME      {$$=$1;}
+     | '*'        {$$=registry_getanytype();}
+     |  "String"  {$$=registry_getstringclass();}
+     |  "int"     {$$=registry_getintclass();}
+     |  "uint"    {$$=registry_getuintclass();}
+     |  "Boolean" {$$=registry_getbooleanclass();}
+     |  "Number"  {$$=registry_getnumberclass();}
+
+MAYBETYPE: ':' TYPE {$$=$2;}
+MAYBETYPE:          {$$=0;}
 
 /* ----------function calls, constructor calls ------ */
 
@@ -1339,8 +1564,9 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
     }
    
     memberinfo_t*f = 0;
+   
     if(TYPE_IS_FUNCTION($1.t) &&
-       (f = registry_findmember($1.t, "__funcptr__"))) {
+       (f = registry_findmember($1.t, "call"))) {
         $$.t = f->return_type;
     } else {
         $$.c = abc_coerce_a($$.c);
@@ -1375,6 +1601,37 @@ E : NEW                         {$$ = $1;}
 E : T_REGEXP                    {$$.c = abc_pushundefined(0); /* FIXME */
                                  $$.t = TYPE_ANY;
                                 }
+
+CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
+                   //MULTINAME(m, registry_getintclass());
+                   //$$.c = abc_coerce2($$.c, &m); // FIXME
+                   $$.t = TYPE_INT;
+                  }
+CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
+                    $$.t = TYPE_INT;
+                   }
+CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
+                  $$.t = TYPE_INT;
+                 }
+CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
+                   $$.t = TYPE_UINT;
+                  }
+CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
+                    $$.t = TYPE_FLOAT;
+                   }
+CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
+                     $$.t = TYPE_STRING;
+                    }
+CONSTANT : KW_TRUE {$$.c = abc_pushtrue(0);
+                    $$.t = TYPE_BOOLEAN;
+                   }
+CONSTANT : KW_FALSE {$$.c = abc_pushfalse(0);
+                     $$.t = TYPE_BOOLEAN;
+                    }
+CONSTANT : KW_NULL {$$.c = abc_pushnull(0);
+                    $$.t = TYPE_NULL;
+                   }
+
 E : FUNCTIONCALL
 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
              $$.t = TYPE_BOOLEAN;
@@ -1409,7 +1666,13 @@ E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
               code_t*label = $$.c = abc_label($$.c);
               jmp->branch = label;
              }
-E : E "&&" E {$$.t = join_types($1.t, $3.t, 'A');
+E : E "&&" E {
+              $$.t = join_types($1.t, $3.t, 'A');
+              /*printf("%08x:\n",$1.t);
+              code_dump($1.c, 0, 0, "", stdout);
+              printf("%08x:\n",$3.t);
+              code_dump($3.c, 0, 0, "", stdout);
+              printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
               $$.c = $1.c;
               $$.c = converttype($$.c, $1.t, $$.t);
               $$.c = abc_dup($$.c);
@@ -1451,6 +1714,47 @@ E : E '[' E ']' {
   $$.c = abc_getproperty2($$.c, &m);
 }
 
+E : E "*=" E { 
+               code_t*c = $3.c;
+               if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
+                c=abc_multiply_i(c);
+               } else {
+                c=abc_multiply(c);
+               }
+               c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
+               $$.c = toreadwrite($1.c, c, 0, 0);
+               $$.t = $1.t;
+              }
+E : E "%=" E { 
+               code_t*c = abc_modulo($3.c);
+               c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
+               $$.c = toreadwrite($1.c, c, 0, 0);
+               $$.t = $1.t;
+              }
+E : E "<<=" E { 
+               code_t*c = abc_lshift($3.c);
+               c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
+               $$.c = toreadwrite($1.c, c, 0, 0);
+               $$.t = $1.t;
+              }
+E : E ">>=" E { 
+               code_t*c = abc_rshift($3.c);
+               c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
+               $$.c = toreadwrite($1.c, c, 0, 0);
+               $$.t = $1.t;
+              }
+E : E ">>>=" E { 
+               code_t*c = abc_urshift($3.c);
+               c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
+               $$.c = toreadwrite($1.c, c, 0, 0);
+               $$.t = $1.t;
+              }
+E : E "/=" E { 
+               code_t*c = abc_divide($3.c);
+               c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
+               $$.c = toreadwrite($1.c, c, 0, 0);
+               $$.t = $1.t;
+              }
 E : E "+=" E { 
                code_t*c = $3.c;
                if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
@@ -1460,7 +1764,7 @@ E : E "+=" E {
                }
                c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
                
-               $$.c = toreadwrite($1.c, c, 0);
+               $$.c = toreadwrite($1.c, c, 0, 0);
                $$.t = $1.t;
               }
 E : E "-=" E { code_t*c = $3.c; 
@@ -1471,13 +1775,13 @@ E : E "-=" E { code_t*c = $3.c;
                }
                c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
                
-               $$.c = toreadwrite($1.c, c, 0);
+               $$.c = toreadwrite($1.c, c, 0, 0);
                $$.t = $1.t;
              }
 E : E '=' E { code_t*c = 0;
               c = code_append(c, $3.c);
               c = converttype(c, $3.t, $1.t);
-              $$.c = toreadwrite($1.c, c, 1);
+              $$.c = toreadwrite($1.c, c, 1, 0);
               $$.t = $1.t;
             }
 
@@ -1486,38 +1790,73 @@ E : E "++" { code_t*c = 0;
              classinfo_t*type = $1.t;
              if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
                  c=abc_increment_i(c);
+                 type = TYPE_INT;
              } else {
                  c=abc_increment(c);
                  type = TYPE_NUMBER;
              }
              c=converttype(c, type, $1.t);
-             $$.c = toreadwrite($1.c, c, 0);
+             $$.c = toreadwrite($1.c, c, 0, 1);
              $$.t = $1.t;
            }
 E : E "--" { code_t*c = 0;
              classinfo_t*type = $1.t;
              if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
-                 c=abc_increment_i(c);
+                 c=abc_decrement_i(c);
+                 type = TYPE_INT;
              } else {
-                 c=abc_increment(c);
+                 c=abc_decrement(c);
                  type = TYPE_NUMBER;
              }
              c=converttype(c, type, $1.t);
-             $$.c = toreadwrite($1.c, c, 0);
+             $$.c = toreadwrite($1.c, c, 0, 1);
              $$.t = $1.t;
             }
 
+E : "++" E { code_t*c = 0;
+             classinfo_t*type = $2.t;
+             if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
+                 c=abc_increment_i(c);
+                 type = TYPE_INT;
+             } else {
+                 c=abc_increment(c);
+                 type = TYPE_NUMBER;
+             }
+             c=converttype(c, type, $2.t);
+             $$.c = toreadwrite($2.c, c, 0, 0);
+             $$.t = $2.t;
+           }
+
+E : "--" E { code_t*c = 0;
+             classinfo_t*type = $2.t;
+             if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
+                 c=abc_decrement_i(c);
+                 type = TYPE_INT;
+             } else {
+                 c=abc_decrement(c);
+                 type = TYPE_NUMBER;
+             }
+             c=converttype(c, type, $2.t);
+             $$.c = toreadwrite($2.c, c, 0, 0);
+             $$.t = $2.t;
+           }
+
 E : E '.' T_IDENTIFIER
             {$$.c = $1.c;
              if($$.t) {
-                 memberinfo_t*f = registry_findmember($$.t, $3->text);
+                 memberinfo_t*f = registry_findmember($$.t, $3);
 
                  if(f && f->slot) {
                      $$.c = abc_getslot($$.c, f->slot);
                  } else {
-                     namespace_t ns = {$$.t->access, ""}; // needs to be "", not $$.t->package
-                     multiname_t m = {QNAME, &ns, 0, $3->text};
-                     $$.c = abc_getproperty2($$.c, &m);
+                     if(f) {
+                         namespace_t ns = {flags2access(f->flags), ""}; // needs to be "", not $$.t->package (!)
+                         multiname_t m = {QNAME, &ns, 0, $3};
+                         $$.c = abc_getproperty2($$.c, &m);
+                     } else {
+                         multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
+                         $$.c = abc_getproperty2($$.c, &m);
+                     }
                  }
                  /* determine type */
                  if(f) {
@@ -1531,8 +1870,10 @@ E : E '.' T_IDENTIFIER
                     $$.t = registry_getanytype();
                  }
              } else {
-                 namespace_t ns = {ACCESS_PACKAGE, ""};
-                 multiname_t m = {QNAME, &ns, 0, $3->text};
+                 /* when resolving a property on an unknown type, we do know the
+                    name of the property (and don't seem to need the package), but
+                    we do need to make avm2 try out all access modes */
+                 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
                  $$.c = abc_getproperty2($$.c, &m);
                  $$.c = abc_coerce_a($$.c);
                  $$.t = registry_getanytype();
@@ -1543,124 +1884,82 @@ VAR_READ : T_IDENTIFIER {
     $$.t = 0;
     $$.c = 0;
     int i;
+    classinfo_t*a = 0;
     memberinfo_t*f = 0;
-    if((i = find_variable($1->text, &$$.t)) >= 0) {
+
+    /* look at variables */
+    if((i = find_variable($1, &$$.t)) >= 0) {
         // $1 is a local variable
         $$.c = abc_getlocal($$.c, i);
-    } else if((f = registry_findmember(state->clsinfo, $1->text))) {
+
+    /* look at current class' members */
+    } else if((f = registry_findmember(state->clsinfo, $1))) {
         // $1 is a function in this class
+        int var_is_static = (f->flags&FLAG_STATIC);
+        int i_am_static = (state->minfo?(state->minfo->flags&FLAG_STATIC):FLAG_STATIC);
+        if(var_is_static != i_am_static) {
+            /* there doesn't seem to be any "static" way to access
+               static properties of a class */
+            state->late_binding = 1;
+            $$.t = f->type;
+            namespace_t ns = {flags2access(f->flags), ""};
+            multiname_t m = {QNAME, &ns, 0, $1};
+            $$.c = abc_findpropstrict2($$.c, &m);
+            $$.c = abc_getproperty2($$.c, &m);
+        } else {
+            if(f->slot>0) {
+                $$.c = abc_getlocal_0($$.c);
+                $$.c = abc_getslot($$.c, f->slot);
+            } else {
+                namespace_t ns = {flags2access(f->flags), ""};
+                multiname_t m = {QNAME, &ns, 0, $1};
+                $$.c = abc_getlocal_0($$.c);
+                $$.c = abc_getproperty2($$.c, &m);
+            }
+        }
         if(f->kind == MEMBER_METHOD) {
             $$.t = TYPE_FUNCTION(f);
         } else {
             $$.t = f->type;
         }
-        if(f->slot>0) {
-            $$.c = abc_getlocal_0($$.c);
-            $$.c = abc_getslot($$.c, f->slot);
+    
+    /* look at classes in the current package */
+    } else if((a = registry_findclass(state->package, $1))) {
+        if(a->slot) {
+            $$.c = abc_getglobalscope($$.c);
+            $$.c = abc_getslot($$.c, a->slot);
         } else {
-            namespace_t ns = {state->clsinfo->access, ""};
-            multiname_t m = {QNAME, &ns, 0, $1->text};
-            $$.c = abc_getlocal_0($$.c);
-            $$.c = abc_getproperty2($$.c, &m);
+            MULTINAME(m, a);
+            $$.c = abc_getlex2($$.c, &m);
         }
+        /* this is not entirely correct (this is the class itself,
+           not an object of this class) */
+        $$.t = a;
+
+    /* unknown object, let the avm2 resolve it */
     } else {
-        // let the avm2 resolve $1 
-        if(strcmp($1->text,"trace"))
-        warning("Couldn't resolve %s, doing late binding", $1->text);
+        if(strcmp($1,"trace"))
+            warning("Couldn't resolve %s, doing late binding", $1);
         state->late_binding = 1;
+                
+        multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
 
         $$.t = 0;
-        $$.c = abc_findpropstrict($$.c, $1->text);
-        $$.c = abc_getproperty($$.c, $1->text);
+        $$.c = abc_findpropstrict2($$.c, &m);
+        $$.c = abc_getproperty2($$.c, &m);
     }
 }
 
-
-// ------------------------------------------------------------------------------
-
-
-TYPE : QNAME {$$=$1;}
-     | '*'        {$$=registry_getanytype();}
-     |  "String"  {$$=registry_getstringclass();}
-     |  "int"     {$$=registry_getintclass();}
-     |  "uint"    {$$=registry_getuintclass();}
-     |  "Boolean" {$$=registry_getbooleanclass();}
-     |  "Number"  {$$=registry_getnumberclass();}
-
-MAYBETYPE: ':' TYPE {$$=$2;}
-MAYBETYPE:          {$$=0;}
-
-//FUNCTION_HEADER:      NAMESPACE MODIFIERS T_FUNCTION GETSET T_IDENTIFIER '(' PARAMS ')' 
-FUNCTION_HEADER:      MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' 
-                      MAYBETYPE
-
-NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER
-NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER '=' T_IDENTIFIER
-NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER '=' T_STRING
-
-//NAMESPACE :              {$$=empty_token();}
-//NAMESPACE : T_IDENTIFIER {$$=$1};
-
-CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
-                   //MULTINAME(m, registry_getintclass());
-                   //$$.c = abc_coerce2($$.c, &m); // FIXME
-                   $$.t = TYPE_INT;
-                  }
-CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
-                    $$.t = TYPE_INT;
-                   }
-CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
-                  $$.t = TYPE_INT;
-                 }
-CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
-                   $$.t = TYPE_UINT;
-                  }
-CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
-                    $$.t = TYPE_FLOAT;
-                   }
-CONSTANT : T_STRING {$$.c = abc_pushstring(0, $1);
-                     $$.t = TYPE_STRING;
-                    }
-CONSTANT : KW_TRUE {$$.c = abc_pushtrue(0);
-                    $$.t = TYPE_BOOLEAN;
-                   }
-CONSTANT : KW_FALSE {$$.c = abc_pushfalse(0);
-                     $$.t = TYPE_BOOLEAN;
-                    }
-CONSTANT : KW_NULL {$$.c = abc_pushnull(0);
-                    $$.t = TYPE_NULL;
-                   }
-
-USE_NAMESPACE : "use" "namespace" T_IDENTIFIER
-
-
-//VARIABLE : T_IDENTIFIER
-//VARIABLE : VARIABLE '.' T_IDENTIFIER
+//TODO: 
 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
-//VARIABLE : VARIABLE '[' EXPRESSION ']' // unqualified expression
-
-GETSET : "get" {$$=$1;}
-       | "set" {$$=$1;}
-       |       {$$=empty_token();}
-
-IDECLARATION : VARIABLE_DECLARATION
-IDECLARATION : FUNCTION_DECLARATION
-
-//IDENTIFIER_LIST : T_IDENTIFIER ',' IDENTIFIER_LIST {extend($3,$1);$$=$3;}
-//IDENTIFIER_LIST : T_IDENTIFIER                     {$$=empty_token();extend($$,$1);}
-
-QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
-QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
 
+// ----------------- namespaces -------------------------------------------------
 
-MAYBE_IDECLARATION_LIST : 
-MAYBE_IDECLARATION_LIST : IDECLARATION_LIST
-IDECLARATION_LIST : IDECLARATION
-IDECLARATION_LIST : IDECLARATION_LIST FUNCTION_HEADER
+NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=$2;}
+NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=$2;}
+NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=$2;}
 
-// chapter 14
-// keywords: as break case catch class const continue default delete do else extends false finally for function if implements import in instanceof interface internal is native new null package private protected public return super switch this throw to true try typeof use var void while with
-// syntactic keywords: each get set namespace include dynamic final native override static
+USE_NAMESPACE : "use" "namespace" T_IDENTIFIER