fixed testcase 'scope'
[swftools.git] / lib / as3 / parser.y
index 5fa1c33..1e4b2d1 100644 (file)
 %error-verbose
 
 %union tokenunion {
-    tokenptr_t token;
+    enum yytokentype token;
+    int flags;
 
-    class_signature_t*class_signature;
-    class_signature_list_t*class_signature_list;
+    classinfo_t*classinfo;
+    classinfo_list_t*classinfo_list;
 
     int number_int;
     unsigned int number_uint;
     double number_float;
     code_t*code;
     typedcode_t value;
-    typedcode_list_t*value_list;
-    writeable_t writeable;
-    char*string;
+    //typedcode_list_t*value_list;
+    codeandnumber_t value_list;
+    param_t* param;
+    params_t params;
+    string_t str;
+    char*id;
+    constant_t*constant;
+    for_start_t for_start;
+    abc_exception_t *exception;
+    regexp_t regexp;
+    struct {
+        abc_exception_list_t *l;
+        code_t*finally;
+    } catch_list;
 }
 
 
-%token<token> T_IDENTIFIER
-%token<string> T_STRING
-%token<token> T_REGEXP
+%token<id> T_IDENTIFIER
+%token<str> T_STRING
+%token<regexp> T_REGEXP
 %token<token> T_EMPTY
 %token<number_int> T_INT
 %token<number_uint> T_UINT
 %token<number_uint> T_SHORT
 %token<number_float> T_FLOAT
 
-%token<token> KW_IMPLEMENTS
+%token<id> T_FOR "for"
+%token<id> T_WHILE "while"
+%token<id> T_DO "do"
+%token<id> T_SWITCH "switch"
+
+%token<token> KW_IMPLEMENTS "implements"
 %token<token> KW_NAMESPACE "namespace"
 %token<token> KW_PACKAGE "package"
-%token<token> KW_PROTECTED
-%token<token> KW_PUBLIC
-%token<token> KW_PRIVATE
+%token<token> KW_PROTECTED "protected"
+%token<token> KW_PUBLIC "public"
+%token<token> KW_PRIVATE "private"
 %token<token> KW_USE "use"
-%token<token> KW_INTERNAL
+%token<token> KW_INTERNAL "internal"
 %token<token> KW_NEW "new"
-%token<token> KW_NATIVE
+%token<token> KW_NATIVE "native"
 %token<token> KW_FUNCTION "function"
-%token<token> KW_FOR "for"
+%token<token> KW_FINALLY "finally"
+%token<token> KW_UNDEFINED "undefined"
+%token<token> KW_CONTINUE "continue"
 %token<token> KW_CLASS "class"
 %token<token> KW_CONST "const"
+%token<token> KW_CATCH "catch"
+%token<token> KW_CASE "case"
 %token<token> KW_SET "set"
-%token<token> KW_STATIC
+%token<token> KW_VOID "void"
+%token<token> KW_THROW "throw"
+%token<token> KW_STATIC "static"
+%token<token> KW_WITH "with"
+%token<token> KW_INSTANCEOF "instanceof"
 %token<token> KW_IMPORT "import"
+%token<token> KW_RETURN "return"
+%token<token> KW_TYPEOF "typeof"
 %token<token> KW_INTERFACE "interface"
-%token<token> KW_NULL
+%token<token> KW_NULL "null"
 %token<token> KW_VAR "var"
-%token<token> KW_DYNAMIC
-%token<token> KW_OVERRIDE
-%token<token> KW_FINAL
+%token<token> KW_DYNAMIC "dynamic"
+%token<token> KW_OVERRIDE "override"
+%token<token> KW_FINAL "final"
+%token<token> KW_EACH "each"
 %token<token> KW_GET "get"
-%token<token> KW_EXTENDS
+%token<token> KW_TRY "try"
+%token<token> KW_SUPER "super"
+%token<token> KW_EXTENDS "extends"
 %token<token> KW_FALSE "false"
 %token<token> KW_TRUE "true"
 %token<token> KW_BOOLEAN "Boolean"
 %token<token> KW_UINT "uint"
 %token<token> KW_INT "int"
-%token<token> KW_WHILE "while"
 %token<token> KW_NUMBER "Number"
 %token<token> KW_STRING "String"
+%token<token> KW_DEFAULT "default"
+%token<token> KW_DELETE "delete"
 %token<token> KW_IF "if"
 %token<token> KW_ELSE  "else"
 %token<token> KW_BREAK   "break"
 %token<token> KW_IS "is"
+%token<token> KW_IN "in"
 %token<token> KW_AS "as"
 
 %token<token> T_EQEQ "=="
 %token<token> T_EQEQEQ "==="
 %token<token> T_NE "!="
+%token<token> T_NEE "!=="
 %token<token> T_LE "<="
 %token<token> T_GE ">="
+%token<token> T_ORBY "|=" 
 %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_MINUSMINUS "--"
 %token<token> T_PLUSPLUS "++"
 %token<token> T_DOTDOT ".."
+%token<token> T_DOTDOTDOT "..."
 %token<token> T_SHL "<<"
 %token<token> T_USHR ">>>"
 %token<token> T_SHR ">>"
-%token<token> T_SEMICOLON ';'
-%token<token> T_STAR '*'
-%token<token> T_DOT '.'
 
+%type <for_start> FOR_START
+%type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
+%type <token> VARCONST
 %type <code> CODE
-%type <code> CODEPIECE
-%type <code> CODEBLOCK MAYBECODE
-%type <token> PACKAGE_DECLARATION
-%type <token> FUNCTION_DECLARATION
-%type <code> VARIABLE_DECLARATION
-%type <token> CLASS_DECLARATION
-%type <token> NAMESPACE_DECLARATION
-%type <token> INTERFACE_DECLARATION
+%type <code> CODEPIECE CODE_STATEMENT
+%type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
+%type <code> PACKAGE_DECLARATION SLOT_DECLARATION
+%type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
+%type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW 
+%type <exception> CATCH FINALLY
+%type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
+%type <code> CLASS_DECLARATION
+%type <code> NAMESPACE_DECLARATION
+%type <code> INTERFACE_DECLARATION
 %type <code> VOIDEXPRESSION
-%type <value> EXPRESSION
+%type <value> EXPRESSION NONCOMMAEXPRESSION
 %type <value> MAYBEEXPRESSION
-%type <value> E
-%type <writeable> LH
+%type <value> E DELETE
 %type <value> CONSTANT
-%type <code> FOR IF WHILE MAYBEELSE BREAK
+%type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY 
+%type <value> INNERFUNCTION
 %type <token> USE_NAMESPACE
-%type <code> ASSIGNMENT FOR_INIT
-%type <token> IMPORT
-%type <class_signature> MAYBETYPE
-%type <token> PACKAGESPEC
+%type <code> FOR_INIT
+%type <code> IMPORT
+%type <classinfo> MAYBETYPE
 %type <token> GETSET
-%type <token> PARAM
-%type <token> PARAMS
-%type <token> PARAM_LIST
-%type <token> MODIFIERS
-%type <token> MODIFIER_LIST
-%type <class_signature_list> IMPLEMENTS_LIST
-%type <class_signature> EXTENDS
-%type <class_signature_list> EXTENDS_LIST
-%type <class_signature> PACKAGEANDCLASS
-%type <class_signature_list> PACKAGEANDCLASS_LIST
-%type <token> MULTILEVELIDENTIFIER
-%type <class_signature> TYPE
-%type <token> VAR
+%type <param> PARAM
+%type <params> PARAM_LIST
+%type <params> MAYBE_PARAM_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 <classinfo> CLASS PACKAGEANDCLASS QNAME
+%type <classinfo_list> QNAME_LIST
+%type <classinfo> TYPE
 //%type <token> VARIABLE
 %type <value> VAR_READ
 %type <value> NEW
-%type <token> X_IDENTIFIER
+//%type <token> T_IDENTIFIER
 %type <token> MODIFIER
-%type <token> PACKAGE
 %type <value> FUNCTIONCALL
-%type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST
+%type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST
 
-// precendence: from low to high
-// http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00000012.html
+// precedence: from low to high
 
 %left prec_none
+
+%left below_semicolon
+%left ';'
+%left ','
+%nonassoc below_assignment // for ?:, contrary to spec
+%right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
 %right '?' ':'
-%nonassoc '='
-%nonassoc "/=" "%="
-%nonassoc "+=" "-="
-%nonassoc ">>="
-%nonassoc "<<="
-%nonassoc ">>>="
-%nonassoc "||"
-%nonassoc "&&"
-%nonassoc '|'
-%nonassoc '^'
+%left "||"
+%left "&&"
+%left '|'
+%left '^'
 %nonassoc '&'
-%nonassoc "!=" "==" "===" "<=" '<' ">=" '>' // TODO: support "a < b < c" syntax?
-%nonassoc "is"
-%left '-'
-%left '+'
-%left "<<"
-%left ">>>"
-%left ">>"
-%left '%'
-%left '/'
-%left '*'
-%left '!'
-%left '~'
-%left "--" "++"
-%left '['
-%nonassoc "as"
-%left '.' ".." "::"
+%nonassoc "==" "!=" "===" "!=="
+%nonassoc "is" "as" "in"
+%nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
+%left "<<" ">>" ">>>" 
+%left below_minus
+%left '-' '+'
+%left '/' '*' '%'
+%left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
+%left "--" "++" 
+%nonassoc below_curly
+%left '[' ']' '{' "new" '.' ".." "::"
 %nonassoc T_IDENTIFIER
-%left below_semicolon
-%left ';'
+%left above_identifier
+%left below_else
 %nonassoc "else"
 %left '('
-%left prec_highest
+
+// needed for "return" precedence:
+%nonassoc T_STRING T_REGEXP
+%nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
+%nonassoc "false" "true" "null" "undefined" "super" "function"
+%nonassoc above_function
+
 
      
 %{
 static int yyerror(char*s)
 {
    syntaxerror("%s", s); 
+   return 0; //make gcc happy
 }
-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)
+
+static char* concat2(const char* t1, const char* t2)
 {
-    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);
+    int l2 = strlen(t2);
+    char*text = malloc(l1+l2+1);
+    memcpy(text   , t1, l1);
+    memcpy(text+l1, t2, l2);
+    text[l1+l2] = 0;
+    return text;
+}
+static char* concat3(const char* t1, const char* t2, const char* t3)
 {
     int l1 = strlen(t1);
     int l2 = strlen(t2);
@@ -257,164 +280,398 @@ static char* concat3str(const char* t1, const char* t2, const char* t3)
 }
 
 typedef struct _import {
-    char*path;
+    char*package;
 } import_t;
 
 DECLARE_LIST(import);
 
-typedef struct _state {
-    abc_file_t*file;
-    abc_script_t*init;
+typedef struct _classstate {
+    /* class data */
+    classinfo_t*info;
+    abc_class_t*abc;
+    code_t*init;
+    code_t*static_init;
+    char has_constructor;
+} classstate_t;
+
+typedef struct _methodstate {
+    /* method data */
+    memberinfo_t*info;
+    char late_binding;
+    char is_constructor;
+    char has_super;
+    char is_global;
+    abc_exception_list_t*exceptions;
+} methodstate_t;
 
+typedef struct _state {
+    struct _state*old;
     int level;
-
+    
     char*package;     
-    char*function;
-    /* code that needs to be executed at the start of
-       a method (like initializing local registers) */
-    code_t*initcode;
+    import_list_t*wildcard_imports;
+    dict_t*imports;
+    char has_own_imports;
+  
+    classstate_t*cls;   
+    methodstate_t*method;
+
+    char*exception_name;
+    
+    dict_t*vars;
+} state_t;
 
-    abc_method_body_t*m;
-    import_list_t*imports;
-   
-    /* class data */
-    char*classname;
-    abc_class_t*cls;
+typedef struct _global {
+    abc_file_t*file;
+    abc_script_t*init;
 
-    array_t*vars;
-    int local_var_base;
-} state_t;
+    int variable_count;
+} global_t;
 
+static global_t*global = 0;
 static state_t* state = 0;
 
 DECLARE_LIST(state);
 
-#define MULTINAME(m,x) multiname_t m;namespace_t m##_ns;registry_fill_multiname(&m, &m##_ns, x);
+#define MULTINAME(m,x) \
+    multiname_t m;\
+    namespace_t m##_ns;\
+    registry_fill_multiname(&m, &m##_ns, x);
+                    
+#define MEMBER_MULTINAME(m,f,n) \
+    multiname_t m;\
+    namespace_t m##_ns;\
+    if(f) { \
+        m##_ns = flags2namespace(f->flags, ""); \
+        m.type = QNAME; \
+        m.ns = &m##_ns; \
+        m.namespace_set = 0; \
+        m.name = f->name; \
+    } else { \
+        m.type = MULTINAME; \
+        m.ns =0; \
+        m.namespace_set = &nopackage_namespace_set; \
+        m.name = n; \
+    }
 
-static state_list_t*state_stack=0;
+/* warning: list length of namespace set is undefined */
+#define MULTINAME_LATE(m, access, package) \
+    namespace_t m##_ns = {access, package}; \
+    namespace_set_t m##_nsset; \
+    namespace_list_t m##_l;m##_l.next = 0; \
+    m##_nsset.namespaces = &m##_l; \
+    m##_nsset = m##_nsset; \
+    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 void new_state()
 {
     NEW(state_t, s);
-    NEW(state_list_t, sl);
-
     state_t*oldstate = state;
     if(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;
-    state_stack = sl;
+    if(!s->imports) {
+        s->imports = dict_new();
+    }
     state = s;
     state->level++;
-    state->vars = array_new();
-    state->initcode = 0;
+    state->has_own_imports = 0;    
+    state->vars = dict_new(); 
+    state->old = oldstate;
+}
+static void state_has_imports()
+{
+    state->wildcard_imports = list_clone(state->wildcard_imports);
+    state->imports = dict_clone(state->imports);
+    state->has_own_imports = 1;
+}
+
+static void state_destroy(state_t*state)
+{
+    if(state->has_own_imports) {
+        list_free(state->wildcard_imports);
+        dict_destroy(state->imports);state->imports=0;
+    }
+    if(state->imports && (!state->old || state->old->imports!=state->imports)) {
+        dict_destroy(state->imports);state->imports=0;
+    }
+    if(state->vars) {
+        int t;
+        for(t=0;t<state->vars->hashsize;t++) {
+            dictentry_t*e =state->vars->slots[t];
+            while(e) {
+                free(e->data);e->data=0;
+                e = e->next;
+            }
+        }
+        dict_destroy(state->vars);state->vars=0;
+    }
+    
+    free(state);
 }
+
 static void old_state()
 {
-    if(!state_stack || !state_stack->next)
+    if(!state || !state->old)
         syntaxerror("invalid nesting");
-    state_t*oldstate = state;
-    state_list_t*old = state_stack;
-    state_stack = state_stack->next;
-    free(old);
-    state = state_stack->state;
-    state->initcode = code_append(state->initcode, oldstate->initcode);
+    state_t*leaving = state;
+    
+    state = state->old;
+    
+    if(leaving->method && leaving->method != state->method) {
+        free(leaving->method);
+        leaving->method=0;
+    }
+    if(leaving->cls && leaving->cls != state->cls) {
+        free(leaving->cls);
+        leaving->cls=0;
+    }
+    
+    state_destroy(leaving);
+}
+
+void initialize_parser()
+{
+    global = rfx_calloc(sizeof(global_t));
+    global->file = abc_file_new();
+    global->file->flags &= ~ABCFILE_LAZY;
+    global->variable_count = 1;
+    global->init = abc_initscript(global->file);
+    code_t*c = global->init->method->body->code;
+    c = abc_getlocal_0(c);
+    c = abc_pushscope(c);
+    /*c = abc_findpropstrict(c, "[package]::trace");
+    c = abc_pushstring(c, "[entering global init function]");
+    c = abc_callpropvoid(c, "[package]::trace", 1);*/
+    global->init->method->body->code = c;
 }
-void initialize_state()
+
+void initialize_file(char*filename)
 {
     new_state();
+    state->package = filename;
+    // needed for state->method->late_binding:
+    state->method = rfx_calloc(sizeof(methodstate_t));
+}
+void finish_file()
+{
+    if(!state || state->level!=1) {
+        syntaxerror("unexpected end of file");
+    }
+    state_destroy(state);state=0;
+}
+
+void* finish_parser()
+{
+    code_t*c = global->init->method->body->code;
+    /*c = abc_findpropstrict(c, "[package]::trace");
+      c = abc_pushstring(c, "[leaving global init function]");
+      c = abc_callpropvoid(c, "[package]::trace", 1);*/
+    c = abc_returnvoid(c);
+    global->init->method->body->code = c;
+    return global->file;
+}
+
 
-    state->file = abc_file_new();
-    state->file->flags &= ~ABCFILE_LAZY;
+static void xx_scopetest() 
+{
+    /* findpropstrict doesn't just return a scope object- it
+       also makes it "active" somehow. Push local_0 on the
+       scope stack and read it back with findpropstrict, it'll
+       contain properties like "trace". Trying to find the same
+       property on a "vanilla" local_0 yields only a "undefined" */
+    //c = abc_findpropstrict(c, "[package]::trace");
     
-    state->init = abc_initscript(state->file, 0, 0);
-    abc_method_body_t*m = state->init->method->body;
-    __ getlocal_0(m);
-    __ pushscope(m);
-    __ findpropstrict(m, "[package]::trace");
-    __ pushstring(m, "[entering global init function]");
-    __ callpropvoid(m, "[package]::trace", 1);
-}
-void* finalize_state()
+    /*c = abc_getlocal_0(c);
+    c = abc_findpropstrict(c, "[package]::trace");
+    c = abc_coerce_a(c);
+    c = abc_setlocal_1(c);
+
+    c = abc_pushbyte(c, 0);
+    c = abc_setlocal_2(c);
+   
+    code_t*xx = c = abc_label(c);
+    c = abc_findpropstrict(c, "[package]::trace");
+    c = abc_pushstring(c, "prop:");
+    c = abc_hasnext2(c, 1, 2);
+    c = abc_dup(c);
+    c = abc_setlocal_3(c);
+    c = abc_callpropvoid(c, "[package]::trace", 2);
+    c = abc_getlocal_3(c);
+    c = abc_kill(c, 3);
+    c = abc_iftrue(c,xx);*/
+}
+
+
+typedef struct _variable {
+    int index;
+    classinfo_t*type;
+    char init;
+} variable_t;
+
+static variable_t* find_variable(char*name)
 {
-    if(state->level!=1) {
-        syntaxerror("unexpected end of file");
+    state_t* s = state;
+    while(s) {
+        variable_t*v = 0;
+        if(s->method)
+            v = dict_lookup(s->vars, name);
+        if(v) {
+            return v;
+        }
+        s = s->old;
     }
-    abc_method_body_t*m = state->init->method->body;
-    //__ popscope(m);
+    return 0;
+} 
+static variable_t* find_variable_safe(char*name)
+{
+    variable_t* v = find_variable(name);
+    if(!v)
+        syntaxerror("undefined variable: %s", name);
+    return v;
+}
+static char variable_exists(char*name) 
+{
+    return dict_lookup(state->vars, name)!=0;
+}
+code_t*defaultvalue(code_t*c, classinfo_t*type);
+static int new_variable(char*name, classinfo_t*type, char init)
+{
+    NEW(variable_t, v);
+    v->index = global->variable_count;
+    v->type = type;
+    v->init = init;
     
-    __ findpropstrict(m, "[package]::trace");
-    __ pushstring(m, "[leaving global init function]");
-    __ callpropvoid(m, "[package]::trace", 1);
-    __ returnvoid(m);
-    return state->file;
+    dict_put(state->vars, name, v);
+
+    return global->variable_count++;
 }
+#define TEMPVARNAME "__as3_temp__"
+static int gettempvar()
+{
+    variable_t*v = find_variable(TEMPVARNAME);
+    if(v) 
+        return v->index;
+    return new_variable(TEMPVARNAME, 0, 0);
+}
+
+code_t* var_block(code_t*body) 
+{
+    code_t*c = 0;
+    code_t*k = 0;
+    int t;
+    int num=0;
+    for(t=0;t<state->vars->hashsize;t++) {
+        dictentry_t*e = state->vars->slots[t];
+        while(e) {
+            variable_t*v = (variable_t*)e->data;
+            if(v->type && v->init) {
+                c = defaultvalue(c, v->type);
+                c = abc_setlocal(c, v->index);
+                k = abc_kill(k, v->index); 
+                num++;
+            }
+            e = e->next;
+        }
+    }
 
+    if(k) {
+        code_t*x = body;
+        while(x) {
+            if(x->opcode== OPCODE___BREAK__ ||
+               x->opcode== OPCODE___CONTINUE__) {
+               /* link kill code before break/continue */
+                code_t*e = code_dup(k);
+                code_t*s = code_start(e);
+                s->prev = x->prev;
+                if(x->prev) {
+                    x->prev->next = s;
+                }
+                e->next = x;
+                x->prev = e;
+            }
+            x = x->prev;
+        }
+    }
+    
+    c = code_append(c, body);
+    c = code_append(c, k);
+    return c;
+}
 
-static void startpackage(token_t*t) 
+static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
+{
+    c = code_append(c, header);
+    c = code_append(c, var_block(body));
+    /* append return if necessary */
+    if(!c || (c->opcode != OPCODE_RETURNVOID && 
+              c->opcode != OPCODE_RETURNVALUE)) {
+        c = abc_returnvoid(c);
+    }
+    return c;
+}
+
+
+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;
+    state->package = strdup(name);
+    global->variable_count = 1;
 }
 static void endpackage()
 {
     /*printf("leaving package \"%s\"\n", state->package);*/
+
+    //used e.g. in classinfo_register:
+    //free(state->package);state->package=0;
+
     old_state();
 }
 
-char*globalclass=0;
-static void startclass(token_t*modifiers, token_t*name, class_signature_t*extends, class_signature_list_t*implements)
+void parserassert(int b)
+{
+    if(!b) syntaxerror("internal error: assertion failed");
+}
+
+
+char*as3_globalclass=0;
+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();
-    state->classname = name->text;
+    global->variable_count = 1;
+    state->cls = rfx_calloc(sizeof(classstate_t));
+    state->method = rfx_calloc(sizeof(methodstate_t)); // method state, for static constructor
 
     token_list_t*t=0;
-    class_signature_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");
-    if(extends) 
-        printf("  extends: %s.%s\n", extends->package, extends->name);
-
-    printf("  implements (%d): ", list_length(implements));
-    for(mlist=implements;mlist;mlist=mlist->next)  {
-        printf("%s ", mlist->class_signature->name);
-    }
-    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)
+    classinfo_list_t*mlist=0;
+
+    if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC))
+        syntaxerror("invalid modifier(s)");
+
+    if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
         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;
@@ -422,380 +679,1760 @@ static void startclass(token_t*modifiers, token_t*name, class_signature_t*extend
         syntaxerror("public classes only allowed inside a package");
     }
 
-    if(registry_findclass(package, state->classname)) {
-        syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, state->classname);
+    if(as3_pass==1) {
+        if(registry_findclass(package, classname)) {
+            syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
+        }
+        /* build info struct */
+        int num_interfaces = (list_length(implements));
+        state->cls->info = classinfo_register(access, package, classname, num_interfaces);
     }
     
-    class_signature_t* classname = class_signature_register(access, package, state->classname);
-
-    multiname_t*extends2 = sig2mname(extends);
-    multiname_t*classname2 = sig2mname(classname);
+    if(as3_pass == 2) {
+        state->cls->info = registry_findclass(package, classname);
+        parserassert((int)state->cls->info);
+
+        /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
+        state->cls->info->superclass = extends?extends:TYPE_OBJECT;
+        int pos = 0;
+        classinfo_list_t*l = implements;
+        for(l=implements;l;l=l->next) {
+            state->cls->info->interfaces[pos++] = l->classinfo;
+        }
 
-    state->cls = abc_class_new(state->file, classname2, extends2);
-    if(final) abc_class_final(state->cls);
-    if(sealed) abc_class_sealed(state->cls);
+        /* generate the abc code for this class */
+        MULTINAME(classname2,state->cls->info);
+        multiname_t*extends2 = sig2mname(extends);
 
-    for(mlist=implements;mlist;mlist=mlist->next) {
-        MULTINAME(m, mlist->class_signature);
-        abc_class_add_interface(state->cls, &m);
-    }
+        state->cls->abc = abc_class_new(global->file, &classname2, extends2);
+        if(flags&FLAG_FINAL) abc_class_final(state->cls->abc);
+        if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
+        if(interface) {
+            state->cls->info->flags |= CLASS_INTERFACE;
+            abc_class_interface(state->cls->abc);
+        }
 
-    /* now write the construction code for this class */
-    int slotindex = abc_initscript_addClassTrait(state->init, classname2, state->cls);
+        abc_class_protectedNS(state->cls->abc, classname);
 
-    abc_method_body_t*m = state->init->method->body;
-    __ getglobalscope(m);
-    class_signature_t*s = extends;
+        for(mlist=implements;mlist;mlist=mlist->next) {
+            MULTINAME(m, mlist->classinfo);
+            abc_class_add_interface(state->cls->abc, &m);
+        }
 
-    int count=0;
-    
-    while(s) {
-        //TODO: take a look at the current scope stack, maybe 
-        //      we can re-use something
-        s = s->superclass;
-        if(!s) 
-        break;
-       
-        multiname_t*s2 = sig2mname(s);
-        __ getlex2(m, s2);
-        multiname_destroy(s2);
-
-        __ pushscope(m);
-        m->code = m->code->prev->prev; // invert
-        count++;
-    }
-    /* continue appending after last op end */
-    while(m->code && m->code->next) m->code = m->code->next; 
-
-    /* TODO: if this is one of *our* classes, we can also 
-             do a getglobalscope/getslot <nr> (which references
-             the init function's slots) */
-    __ getlex2(m, extends2);
-    __ dup(m);
-    __ pushscope(m); // we get a Verify Error #1107 if this is not the top scope
-    __ newclass(m,state->cls);
-    while(count--) {
-        __ popscope(m);
-    }
-    __ setslot(m, slotindex);
-
-    /* flash.display.MovieClip handling */
-    if(!globalclass && public && class_signature_equals(registry_getMovieClip(),extends)) {
-        if(state->package && state->package[0]) {
-            globalclass = concat3str(state->package, ".", state->classname);
+        /* write the construction code for this class to the global init
+           function */
+        int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
+
+        abc_method_body_t*m = global->init->method->body;
+        __ getglobalscope(m);
+        classinfo_t*s = extends;
+
+        int count=0;
+        
+        while(s) {
+            //TODO: take a look at the current scope stack, maybe 
+            //      we can re-use something
+            s = s->superclass;
+            if(!s) 
+            break;
+           
+            multiname_t*s2 = sig2mname(s);
+            __ getlex2(m, s2);
+            multiname_destroy(s2);
+
+            __ pushscope(m); count++;
+            m->code = m->code->prev->prev; // invert
+        }
+        /* continue appending after last op end */
+        while(m->code && m->code->next) m->code = m->code->next; 
+
+        /* TODO: if this is one of *our* classes, we can also 
+                 do a getglobalscope/getslot <nr> (which references
+                 the init function's slots) */
+        if(extends2) {
+            __ getlex2(m, extends2);
+            __ dup(m);
+            /* notice: we get a Verify Error #1107 if the top elemnt on the scope
+               stack is not the superclass */
+            __ pushscope(m);count++;
         } else {
-            globalclass = strdup(state->classname);
+            __ pushnull(m);
+            /* notice: we get a verify error #1107 if the top element on the scope 
+               stack is not the global object */
+            __ getlocal_0(m);
+            __ pushscope(m);count++;
+        }
+        __ newclass(m,state->cls->abc);
+        while(count--) {
+            __ popscope(m);
+        }
+        __ setslot(m, slotindex);
+        multiname_destroy(extends2);
+
+        /* flash.display.MovieClip handling */
+
+        if(!as3_globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
+            if(state->package && state->package[0]) {
+                as3_globalclass = concat3(state->package, ".", classname);
+            } else {
+                as3_globalclass = strdup(classname);
+            }
         }
     }
 }
 
 static void endclass()
 {
-    /*printf("leaving class %s\n", state->classname);*/
+    if(as3_pass == 2) {
+        if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) {
+            code_t*c = 0;
+            c = abc_getlocal_0(c);
+            c = abc_constructsuper(c, 0);
+            state->cls->init = code_append(state->cls->init, c);
+        }
+        if(!state->method->late_binding) {
+            // class initialization code uses late binding
+            code_t*c = 0;
+            c = abc_getlocal_0(c);
+            c = abc_pushscope(c);
+            state->cls->static_init = code_append(c, state->cls->static_init);
+        }
+
+        if(state->cls->init) {
+            abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
+            m->body->code = wrap_function(0, state->cls->init, m->body->code);
+        }
+        if(state->cls->static_init) {
+            abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
+            m->body->code = wrap_function(0, state->cls->static_init, m->body->code);
+        }
+    }
+
     old_state();
 }
-static void addimport(token_t*t)
+
+void check_code_for_break(code_t*c)
 {
-    NEW(import_t,i);
-    i->path = t->text;
-    list_append(state->imports, i);
+    while(c) {
+        if(c->opcode == OPCODE___BREAK__) {
+            char*name = string_cstr(c->data[0]);
+            syntaxerror("Unresolved \"break %s\"", name);
+        }
+        if(c->opcode == OPCODE___CONTINUE__) {
+            char*name = string_cstr(c->data[0]);
+            syntaxerror("Unresolved \"continue %s\"", name);
+        }
+        c=c->prev;
+    }
 }
-static void print_imports()
+
+
+static void check_constant_against_type(classinfo_t*t, constant_t*c)
 {
-    import_list_t*l = state->imports;
-    while(l) {
-        printf("  import %s\n", l->import->path);
-        l = l->next;
+#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 int flags2access(int flags)
+{
+    int access = 0;
+    if(flags&FLAG_PUBLIC)  {
+        if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) 
+            syntaxerror("invalid combination of access levels");
+        access = ACCESS_PACKAGE;
+    } else if(flags&FLAG_PRIVATE) {
+        if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) 
+            syntaxerror("invalid combination of access levels");
+        access = ACCESS_PRIVATE;
+    } else if(flags&FLAG_PROTECTED) {
+        if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL)) 
+            syntaxerror("invalid combination of access levels");
+        access = ACCESS_PROTECTED;
+    } else {
+        access = ACCESS_PACKAGEINTERNAL;
     }
+    return access;
 }
-static void startfunction(token_t*ns, token_t*mod, token_t*getset, token_t*name,
-                          token_t*params, class_signature_t*type)
+
+
+static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
 {
-    token_list_t*t;
-    new_state();
-    state->function = name->text;
-    
-    /*printf("entering function %s\n", name->text);
-    if(ns)
-        printf("  namespace: %s\n", ns->text);
-    printf("  getset: %s\n", getset->text);
-    printf("  params: ");for(t=params->tokens;t;t=t->next) printf("%s ", t->token->text);printf("\n");
-    printf("  mod: ");for(t=mod->tokens;t;t=t->next) printf("%s ", t->token->text);printf("\n");
-    if(type)
-        printf("  type: %s.%s\n", type->package, type->name);
-    print_imports();*/
-    
-    if(state->m) {
-        syntaxerror("not able to start another method scope");
+    memberinfo_t*minfo = 0;
+    if(!state->cls) {
+        //package method
+        minfo = memberinfo_register_global(flags2access(flags), state->package, name, MEMBER_METHOD);
+        minfo->return_type = return_type;
+    } else if(getset != KW_GET && getset != KW_SET) {
+        //class method
+        if((minfo = registry_findmember(state->cls->info, name, 0))) {
+            if(minfo->parent == state->cls->info) {
+                syntaxerror("class already contains a member/method called '%s'", name);
+            } else if(!minfo->parent) {
+                syntaxerror("internal error: overriding method %s, which doesn't have parent", name);
+            } else {
+                if(!(minfo->flags&(FLAG_STATIC|FLAG_PRIVATE)))
+                    syntaxerror("function %s already exists in superclass. Did you forget the 'override' keyword?");
+            }
+        }
+        minfo = memberinfo_register(state->cls->info, 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->method->abc->method->trait->slot_id;
+    } else {
+        //class getter/setter
+        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;
+        // not sure wether to look into superclasses here, too
+        if((minfo=registry_findmember(state->cls->info, name, 0))) {
+            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->cls->info, 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_PACKAGEINTERNAL) minfo->flags |= FLAG_PACKAGEINTERNAL;
+    if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
+    return minfo;
+}
 
-    multiname_t*type2 = sig2mname(type);
+static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
+                          params_t*params, classinfo_t*return_type)
+{
+    if(state->method && state->method->info) {
+        syntaxerror("not able to start another method scope");
+    }
+    new_state();
+    state->method = rfx_calloc(sizeof(methodstate_t));
+    state->method->has_super = 0;
 
-    if(!strcmp(state->classname,name->text)) {
-        state->m = abc_class_constructor(state->cls, type2, 0);
+    if(state->cls) {
+        state->method->is_constructor = !strcmp(state->cls->info->name,name);
+        state->cls->has_constructor |= state->method->is_constructor;
     } else {
-        state->m = abc_class_method(state->cls, type2, name->text, 0);
+        state->method->is_global = 1;
+        state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
     }
-    array_append(state->vars, "this", 0);
+    if(state->method->is_constructor)
+        name = "__as3_constructor__";
 
-    __ getlocal_0(state->m);
-    __ pushscope(state->m);
+    if(as3_pass == 1) {
+        return_type = 0;
+        state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
+    }
 
-    multiname_destroy(type2);
+    if(as3_pass == 2) {
+        /* retrieve the member info that we stored in the first pass.
+           TODO: better getter/setter support? */
+        if(!state->cls) state->method->info = registry_findclass(state->package, name)->function;
+        else            state->method->info = registry_findmember(state->cls->info, name, 0);
+        state->method->info->return_type = return_type;
+
+        global->variable_count = 0;
+        /* state->vars is initialized by state_new */
+        if(!state->method->is_global)
+            new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0);
+        else
+            new_variable("globalscope", 0, 0);
+        param_list_t*p=0;
+        for(p=params->list;p;p=p->next) {
+            new_variable(p->param->name, p->param->type, 0);
+        }
+    } 
 }
-static void endfunction()
+
+static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
+                          params_t*params, classinfo_t*return_type, code_t*body)
 {
-    printf("leaving function %s\n", state->function);
-    __ returnvoid(state->m);
+    if(as3_pass==1) {
+        old_state();
+        return;
+    }
 
+    abc_method_t*f = 0;
+
+    multiname_t*type2 = sig2mname(return_type);
+    int slot = 0;
+    if(state->method->is_constructor) {
+        f = abc_class_getconstructor(state->cls->abc, type2);
+    } else if(!state->method->is_global) {
+        namespace_t mname_ns = flags2namespace(flags, "");
+        multiname_t mname = {QNAME, &mname_ns, 0, name};
+
+        if(flags&FLAG_STATIC)
+            f = abc_class_staticmethod(state->cls->abc, type2, &mname);
+        else
+            f = abc_class_method(state->cls->abc, type2, &mname);
+        slot = f->trait->slot_id;
+    } else {
+        namespace_t mname_ns = flags2namespace(flags, state->package);
+        multiname_t mname = {QNAME, &mname_ns, 0, name};
+
+        f = abc_method_new(global->file, type2, 1);
+        trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
+        //abc_code_t*c = global->init->method->body->code;
+    }
+    //flash doesn't seem to allow us to access function slots
+    //state->method->info->slot = slot;
+
+    if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
+    if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
+    if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
+    if(params->varargs) f->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(f->parameters, m);
+        if(p->param->value) {
+            check_constant_against_type(p->param->type, p->param->value);
+            opt=1;list_append(f->optional_parameters, p->param->value);
+        } else if(opt) {
+            syntaxerror("non-optional parameter not allowed after optional parameters");
+        }
+    }
+    check_code_for_break(body);
+
+    if(f->body) {
+        f->body->code = body;
+        f->body->exceptions = state->method->exceptions;
+    } else { //interface
+        if(body)
+            syntaxerror("interface methods can't have a method body");
+    }
+       
     old_state();
 }
-static token_t* empty_token()
+
+
+
+char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
 {
-    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;
+    return 1; // FIXME
 }
 
-int find_variable(char*name, class_signature_t**m)
+void breakjumpsto(code_t*c, char*name, code_t*jump) 
 {
-    state_list_t* s = state_stack;
-    while(s) {
-        int i = array_find(s->state->vars, name);
-        if(i>=0) {
-            if(m) {
-                *m = array_getvalue(s->state->vars, i);
+    while(c) {
+        if(c->opcode == OPCODE___BREAK__) {
+            string_t*name2 = c->data[0];
+            if(!name2->len || !strncmp(name2->str, name, name2->len)) {
+                c->opcode = OPCODE_JUMP;
+                c->branch = jump;
             }
-            return i + s->state->local_var_base;
         }
-        s = s->next;
+        c=c->prev;
     }
-    syntaxerror("undefined variable: %s", name);
 }
-
-class_signature_t*join_types(class_signature_t*type1, class_signature_t*type2, char op)
-{
-    return registry_getanytype(); // FIXME
-}
-char is_subtype_of(class_signature_t*type, class_signature_t*supertype)
+void continuejumpsto(code_t*c, char*name, code_t*jump) 
 {
-    return 1; // FIXME
+    while(c) {
+        if(c->opcode == OPCODE___CONTINUE__) {
+            string_t*name2 = c->data[0];
+            if(!name2->len || !strncmp(name2->str, name, name2->len)) {
+                c->opcode = OPCODE_JUMP;
+                c->branch = jump;
+            }
+        }
+        c = c->prev;
+    }
 }
 
-#define TYPE_ANY                  registry_getanytype()
-#define TYPE_IS_ANY(t)    ((t) == registry_getanytype())
-#define TYPE_INT                  registry_getintclass()
-#define TYPE_IS_INT(t)    ((t) == registry_getintclass())
-#define TYPE_UINT                 registry_getuintclass()
-#define TYPE_IS_UINT(t)   ((t) == registry_getuintclass())
-#define TYPE_FLOAT                registry_getnumberclass()
-#define TYPE_IS_FLOAT(t)  ((t) == registry_getnumberclass())
-#define TYPE_BOOLEAN              registry_getbooleanclass()
-#define TYPE_IS_BOOLEAN(t)((t) == registry_getbooleanclass())
-#define TYPE_STRING               registry_getstringclass()
-#define TYPE_IS_STRING(t) ((t) == registry_getstringclass())
-#define TYPE_NULL                 registry_getnullclass()
-#define TYPE_IS_NULL(t)   ((t) == registry_getnullclass())
-
-%}
+#define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
+#define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
+#define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
 
+classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
+{
+    if(!type1 || !type2) 
+        return registry_getanytype();
+    if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
+        return registry_getanytype();
+
+    if(op=='+') {
+        if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
+            return TYPE_NUMBER;
+        } else {
+            return TYPE_ANY;
+        }
+    }
 
-%%
+    if(type1 == type2)
+        return type1;
+    return registry_getanytype();
+}
+code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
+{
+    if(from==to)
+        return c;
+    if(!to) {
+        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(from) || TYPE_IS_INT(from)) &&
+       (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
+        // allow conversion between number types
+        return abc_coerce2(c, &m);
+    }
+    //printf("%s.%s\n", from.package, from.name);
+    //printf("%s.%s\n", to.package, to.name);
+
+    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) {
+                // target 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;
+    if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
+        return c;
+    syntaxerror("can't convert type %s to %s", from->name, to->name);
+    return 0; // make gcc happy
+}
 
-/* ------------ code blocks / statements ---------------- */
+code_t*defaultvalue(code_t*c, classinfo_t*type)
+{
+    if(TYPE_IS_INT(type)) {
+       c = abc_pushbyte(c, 0);
+    } else if(TYPE_IS_UINT(type)) {
+       c = abc_pushuint(c, 0);
+    } else if(TYPE_IS_FLOAT(type)) {
+       c = abc_pushnan(c);
+    } else if(TYPE_IS_BOOLEAN(type)) {
+       c = abc_pushfalse(c);
+    } else if(!type) {
+       //c = abc_pushundefined(c);
+    } else {
+       c = abc_pushnull(c);
+       MULTINAME(m, type);
+       c = abc_coerce2(c, &m);
+    }
+    return c;
+}
 
-PROGRAM: MAYBECODE
+char is_pushundefined(code_t*c)
+{
+    return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
+}
 
-MAYBECODE: CODE {$$=$1;}
-MAYBECODE:      {$$=code_new();}
+static classinfo_t* find_class(char*name)
+{
+    classinfo_t*c=0;
+
+    c = registry_findclass(state->package, name);
+    if(c) return c;
+
+    /* try explicit imports */
+    dictentry_t* e = dict_get_slot(state->imports, name);
+    if(c) return c;
+    while(e) {
+        if(!strcmp(e->key, name)) {
+            c = (classinfo_t*)e->data;
+            if(c) return c;
+        }
+        e = e->next;
+    }
 
-CODE: CODE CODEPIECE {$$=code_append($1,$2);}
-CODE: CODEPIECE {$$=$1;}
+    /* try package.* imports */
+    import_list_t*l = state->wildcard_imports;
+    while(l) {
+        //printf("does package %s contain a class %s?\n", l->import->package, name);
+        c = registry_findclass(l->import->package, name);
+        if(c) return c;
+        l = l->next;
+    }
 
-CODEPIECE: PACKAGE_DECLARATION   {$$=code_new();/*enters a scope*/}
-CODEPIECE: CLASS_DECLARATION     {$$=code_new();/*enters a scope*/}
-CODEPIECE: INTERFACE_DECLARATION {/*TODO*/$$=code_new();}
-CODEPIECE: IMPORT                {$$=code_new();/*adds imports to current scope*/}
-CODEPIECE: ';'                   {$$=code_new();}
-CODEPIECE: VARIABLE_DECLARATION  {$$=$1}
-CODEPIECE: VOIDEXPRESSION        {$$=$1}
-CODEPIECE: FOR                   {$$=$1}
-CODEPIECE: WHILE                 {$$=$1}
-CODEPIECE: BREAK                 {$$=$1}
-CODEPIECE: IF                    {$$=$1}
-CODEPIECE: ASSIGNMENT            {$$=$1}
-CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
-CODEPIECE: FUNCTION_DECLARATION  {/*TODO*/$$=code_new();}
-CODEPIECE: USE_NAMESPACE         {/*TODO*/$$=code_new();}
-
-CODEBLOCK :  '{' MAYBECODE '}' {$$=$2;}
-CODEBLOCK :  CODEPIECE ';'             {$$=$1;}
-CODEBLOCK :  CODEPIECE %prec below_semicolon {$$=$1;}
+    /* try global package */
+    c = registry_findclass("", name);
+    if(c) return c;
+   
+    /* try local "filename" package */
+    c = registry_findclass(current_filename_short, name);
+    if(c) return c;
 
-/* ------------ functions --------------------------- */
+    return 0;
+}
 
-FUNCTION_DECLARATION: MODIFIERS "function" GETSET T_IDENTIFIER '(' PARAMS ')' 
-                      MAYBETYPE '{' {startfunction(0,$1,$3,$4,$6,$8)} MAYBECODE '}' {
-    if(!state->m) syntaxerror("internal error: undefined function");
-    state->m->code = code_append(state->initcode, $11);state->initcode=0;
-    endfunction()
+static char is_getlocal(code_t*c)
+{
+    if(!c || c->prev || c->next)
+        return 0;
+    return(c->opcode == OPCODE_GETLOCAL
+        || c->opcode == OPCODE_GETLOCAL_0
+        || c->opcode == OPCODE_GETLOCAL_1
+        || c->opcode == OPCODE_GETLOCAL_2
+        || c->opcode == OPCODE_GETLOCAL_3);
+}
+static int getlocalnr(code_t*c)
+{
+    if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
+    else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
+    else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
+    else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
+    else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
+    else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
+    return 0;
 }
 
-/* ------------ variables --------------------------- */
+static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
+{
+    /* converts this:
 
-MAYBEEXPRESSION : '=' EXPRESSION {$$=$2;}
-                |                {$$.c=abc_pushundefined(0);
-                                  $$.t=TYPE_ANY;
-                                 }
+       [prefix code] [read instruction]
 
-VAR : "const" | "var"
-VARIABLE_DECLARATION : VAR T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
-    if(array_contains(state->vars, $2->text))
-        syntaxerror("Variable %s already defined", $2->text);
-    $$ = $4.c;
-   
-    if(!is_subtype_of($4.t, $3)) {
-        syntaxerror("Can't convert %s to %s", $4.t->name, 
-                                              $3->name);
-    }
+       to this:
 
-    int index = array_append(state->vars, $2->text, $3) + state->local_var_base;
-    $$ = abc_setlocal($$, index);
+       [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
+    */
+    
+    if(in && in->opcode == OPCODE_COERCE_A) {
+        in = code_cutlast(in);
+    }
+    if(in->next)
+        syntaxerror("internal error");
+
+    /* chop off read instruction */
+    code_t*prefix = in;
+    code_t*r = in;
+    if(r->prev) {
+        prefix = r->prev;r->prev = 0;
+        prefix->next=0;
+    } else {
+        prefix = 0;
+    }
 
-    if($3) {
-        if(TYPE_IS_INT($3) || TYPE_IS_UINT($3) || TYPE_IS_FLOAT($3)) {
-            state->initcode = abc_pushbyte(state->initcode, 32);
-        } else if(TYPE_IS_BOOLEAN($3)) {
-            state->initcode = abc_pushfalse(state->initcode);
+    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 || m->type == MULTINAME) {
+            if(!justassign) {
+                prefix = abc_dup(prefix); // we need the object, too
+            }
+            use_temp_var = 1;
+        } else if(m->type == MULTINAMEL) {
+            if(!justassign) {
+                /* dupping two values on the stack requires 5 operations and one register- 
+                   couldn't adobe just have given us a dup2? */
+                int temp = gettempvar();
+                prefix = abc_setlocal(prefix, temp);
+                prefix = abc_dup(prefix);
+                prefix = abc_getlocal(prefix, temp);
+                prefix = abc_swap(prefix);
+                prefix = abc_getlocal(prefix, temp);
+                if(!use_temp_var);
+                    prefix = abc_kill(prefix, temp);
+            }
+            use_temp_var = 1;
         } else {
-            state->initcode = abc_pushnull(state->initcode);
+            syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
         }
-        state->initcode = abc_setlocal(state->initcode, index);
-    } /*else {
-        // that's the default for a local register, anyway
-        state->initcode = abc_pushundefined(state->initcode);
-        state->initcode = abc_setlocal(state->initcode, index);
-    }*/
-    printf("variable %s -> %d (%s)\n", $2->text, index, $4.t->name);
-}
-ASSIGNMENT :           T_IDENTIFIER '=' EXPRESSION {
-    class_signature_t*type=0;
-    int i = find_variable($1->text, &type);
-    $$ = $3.c;
-    if(!type && $3.t) {
-        // convert to "any" type, the register is untyped
-        $$ = abc_coerce_a($$);
+    } else if(r->opcode == OPCODE_GETSLOT) {
+        write->opcode = OPCODE_SETSLOT;
+        write->data[0] = r->data[0];
+        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];
+    } else if(r->opcode == OPCODE_GETLOCAL_0) { 
+        write->opcode = OPCODE_SETLOCAL_0;
+    } else if(r->opcode == OPCODE_GETLOCAL_1) { 
+        write->opcode = OPCODE_SETLOCAL_1;
+    } else if(r->opcode == OPCODE_GETLOCAL_2) { 
+        write->opcode = OPCODE_SETLOCAL_2;
+    } else if(r->opcode == OPCODE_GETLOCAL_3) { 
+        write->opcode = OPCODE_SETLOCAL_3;
     } else {
-        // TODO: convert ints to strings etc.
+        code_dump(r);
+        syntaxerror("illegal lvalue: can't assign a value to this expression");
     }
-    $$ = abc_setlocal($$, i);
-}
-
-/* ------------ control flow ------------------------- */
-
-MAYBEELSE:  %prec prec_none {$$ = code_new();}
-MAYBEELSE: "else" CODEBLOCK {$$=$2;}
-//MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
-
-IF  : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
-    $$=$4.c;
-    code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
+    code_t* c = 0;
+    
+    int temp = -1;
+    if(!justassign) {
+        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 {
+        /* even smaller version: overwrite the value without reading
+           it out first */
+        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 = 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);
+        }
+    }
+    return c;
+}
+
+char is_break_or_jump(code_t*c)
+{
+    if(!c)
+        return 0;
+    if(c->opcode == OPCODE_JUMP ||
+       c->opcode == OPCODE___BREAK__ ||
+       c->opcode == OPCODE___CONTINUE__ ||
+       c->opcode == OPCODE_THROW ||
+       c->opcode == OPCODE_RETURNVOID ||
+       c->opcode == OPCODE_RETURNVALUE) {
+       return 1;
+    }
+    return 0;
+}
+
+
+#define IS_FINALLY_TARGET(op) \
+        ((op) == OPCODE___CONTINUE__ || \
+         (op) == OPCODE___BREAK__ || \
+         (op) == OPCODE_RETURNVOID || \
+         (op) == OPCODE_RETURNVALUE || \
+         (op) == OPCODE___RETHROW__)
+
+static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
+{
+#define NEED_EXTRA_STACK_ARG
+    code_t*finally_label = abc_nop(0);
+    NEW(lookupswitch_t, l);
+    //_lookupswitch
+
+    code_t*i = c;
+    int count=0;
+    while(i) {
+        code_t*prev = i->prev;
+        if(IS_FINALLY_TARGET(i->opcode)) {
+           code_t*p = prev;
+           char needvalue=0;
+           if(i->opcode == OPCODE___RETHROW__ ||
+              i->opcode == OPCODE_RETURNVALUE) {
+               if(i->opcode == OPCODE___RETHROW__)
+                 i->opcode = OPCODE_THROW;
+               needvalue=1;
+               p = abc_coerce_a(p);
+               p = abc_setlocal(p, tempvar);
+           }
+           p = abc_pushbyte(p, count++);
+           p = abc_jump(p, finally_label);
+           code_t*target = p = abc_label(p);
+#ifdef NEED_EXTRA_STACK_ARG
+           p = abc_pop(p);
+#endif
+           if(needvalue) {
+               p = abc_getlocal(p, tempvar);
+           }
+
+           p->next = i;i->prev = p;
+           list_append(l->targets, target);
+        }
+        i = prev;
+    }
+
+    code_t*j,*f;
+    c = abc_pushbyte(c, -1);
+    c = code_append(c, finally_label);
+    c = code_append(c, finally);
+
+#ifdef NEED_EXTRA_STACK_ARG
+    c = abc_dup(c);
+#endif
+    c = abc_lookupswitch(c, l);
+    c = l->def = abc_label(c);
+#ifdef NEED_EXTRA_STACK_ARG
+    c = abc_pop(c);
+#endif
+
+    return c;
+}
+
+static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
+{
+    code_t*i = c;
+    while(i) {
+        code_t*prev = i->prev;
+        if(IS_FINALLY_TARGET(i->opcode)) {
+           if(i->opcode == OPCODE___RETHROW__)
+                i->opcode = OPCODE_THROW;
+           code_t*end = code_dup(finally);
+           code_t*start = code_start(end);
+           if(prev) prev->next = start;
+           start->prev = prev;
+           i->prev = end;
+           end->next = i;
+        }
+        i = prev;
+    }
+    return code_append(c, finally);
+}
+
+code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
+{
+    if(!finally)
+        return c;
+    code_t*i = c;
+    char cantdup=0;
+    int num_insertion_points=0;
+    while(i) {
+        if(IS_FINALLY_TARGET(i->opcode))
+            num_insertion_points++;
+        i = i->prev;
+    }
+    i = finally;
+    int code_size=0;
+    while(i) {
+        code_size++;
+        if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
+            cantdup=1;
+        }
+        i = i->prev;
+    }
+    int simple_version_cost = (1+num_insertion_points)*code_size;
+    int lookup_version_cost = 4*num_insertion_points + 5;
+
+    if(cantdup || simple_version_cost > lookup_version_cost) {
+        printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
+        return insert_finally_lookup(c, finally, tempvar);
+    } else {
+        printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
+        return insert_finally_simple(c, finally, tempvar);
+    }
+}
+
+#define PASS1 }} if(as3_pass == 1) {{
+#define PASS1END }} if(as3_pass == 2) {{
+#define PASS2 }} if(as3_pass == 2) {{
+#define PASS12 }} {{
+#define PASS12END }} if(as3_pass == 2) {{
+
+%}
+
+%%
+
+/* ------------ code blocks / statements ---------------- */
+
+PROGRAM: MAYBE_PROGRAM_CODE_LIST
+
+MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST 
+PROGRAM_CODE_LIST: PROGRAM_CODE 
+                 | PROGRAM_CODE_LIST PROGRAM_CODE
+
+PROGRAM_CODE: PACKAGE_DECLARATION 
+            | INTERFACE_DECLARATION 
+            | CLASS_DECLARATION
+            | FUNCTION_DECLARATION
+            | SLOT_DECLARATION
+            | PACKAGE_INITCODE
+            | ';'
+
+MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
+INPACKAGE_CODE_LIST: INPACKAGE_CODE 
+                   | INPACKAGE_CODE_LIST INPACKAGE_CODE
+
+INPACKAGE_CODE: INTERFACE_DECLARATION 
+              | CLASS_DECLARATION
+              | FUNCTION_DECLARATION
+              | SLOT_DECLARATION
+              | PACKAGE_INITCODE
+              | ';'
+
+MAYBECODE: CODE {$$=$1;}
+MAYBECODE: {$$=code_new();}
+
+CODE: CODE CODEPIECE {$$=code_append($1,$2);}
+CODE: CODEPIECE {$$=$1;}
+
+// code which also may appear outside a method
+CODE_STATEMENT: IMPORT 
+CODE_STATEMENT: FOR 
+CODE_STATEMENT: FOR_IN 
+CODE_STATEMENT: WHILE 
+CODE_STATEMENT: DO_WHILE 
+CODE_STATEMENT: SWITCH 
+CODE_STATEMENT: IF
+CODE_STATEMENT: WITH
+CODE_STATEMENT: TRY
+CODE_STATEMENT: VOIDEXPRESSION 
+
+// code which may appear anywhere
+CODEPIECE: ';' {$$=0;}
+CODEPIECE: CODE_STATEMENT
+CODEPIECE: VARIABLE_DECLARATION
+CODEPIECE: BREAK
+CODEPIECE: CONTINUE
+CODEPIECE: RETURN
+CODEPIECE: THROW
+
+CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;}
+CODEPIECE: USE_NAMESPACE         {/*TODO*/$$=0;}
+
+CODEBLOCK :  '{' CODE '}' {$$=$2;}
+CODEBLOCK :  '{' '}'      {$$=0;}
+CODEBLOCK :  CODEPIECE ';'             {$$=$1;}
+CODEBLOCK :  CODEPIECE %prec below_semicolon {$$=$1;}
+
+/* ------------ package init code ------------------- */
+
+PACKAGE_INITCODE: CODE_STATEMENT {
+    code_t**cc = &global->init->method->body->code;
+    *cc = code_append(*cc, $1);
+}
+
+/* ------------ variables --------------------------- */
+
+MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
+                |                {$$.c=abc_pushundefined(0);
+                                  $$.t=TYPE_ANY;
+                                 }
+
+VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
+VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
+
+VARIABLE_LIST: ONE_VARIABLE                   {$$ = $1;}
+VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
+
+ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
+{
+    if(variable_exists($1))
+        syntaxerror("Variable %s already defined", $1);
+   
+    if(!is_subtype_of($3.t, $2)) {
+        syntaxerror("Can't convert %s to %s", $3.t->name, 
+                                              $2->name);
+    }
+
+    int index = new_variable($1, $2, 1);
+    
+    if($2) {
+        if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
+            $$ = $3.c;
+            $$ = converttype($$, $3.t, $2);
+            $$ = abc_setlocal($$, index);
+        } else {
+            $$ = defaultvalue(0, $2);
+            $$ = abc_setlocal($$, index);
+        }
+    } else {
+        if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
+            $$ = $3.c;
+            $$ = abc_coerce_a($$);
+            $$ = abc_setlocal($$, index);
+        } else {
+            $$ = code_new();
+        }
+    }
+    
+    /* that's the default for a local register, anyway
+        else {
+        state->method->initcode = abc_pushundefined(state->method->initcode);
+        state->method->initcode = abc_setlocal(state->method->initcode, index);
+    }*/
+    //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
+}
+
+/* ------------ control flow ------------------------- */
+
+MAYBEELSE:  %prec below_else {$$ = code_new();}
+MAYBEELSE: "else" CODEBLOCK {$$=$2;}
+//MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
+
+IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
+     
+    $$ = code_new();
+    $$ = code_append($$, $4.c);
+    code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
    
     $$ = code_append($$, $6);
     if($7) {
         myjmp = $$ = abc_jump($$, 0);
     }
-    myif->branch = $$ = abc_label($$);
+    myif->branch = $$ = abc_nop($$);
     if($7) {
         $$ = code_append($$, $7);
-        myjmp->branch = $$ = abc_label($$);
+        myjmp->branch = $$ = abc_nop($$);
     }
+    $$ = var_block($$);
     old_state();
 }
 
 FOR_INIT : {$$=code_new();}
-FOR_INIT : ASSIGNMENT | VARIABLE_DECLARATION | VOIDEXPRESSION
+FOR_INIT : VARIABLE_DECLARATION
+FOR_INIT : VOIDEXPRESSION
 
-FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
-    $$ = $4;
+// TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
+FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
+    $$=$2;new_variable($2,$3,1);
+}
+FOR_IN_INIT : T_IDENTIFIER {
+    $$=$1;
+}
+
+FOR_START : T_FOR '(' {new_state();$$.name=$1;$$.each=0;}
+FOR_START : T_FOR "each" '(' {new_state();$$.name=$1;$$.each=1;}
+
+FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
+    if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
+    $$ = code_new();
+    $$ = code_append($$, $2);
     code_t*loopstart = $$ = abc_label($$);
-    $$ = code_append($$, $6.c);
+    $$ = code_append($$, $4.c);
     code_t*myif = $$ = abc_iffalse($$, 0);
-    $$ = code_append($$, $10);
     $$ = code_append($$, $8);
+    code_t*cont = $$ = abc_nop($$);
+    $$ = code_append($$, $6);
+    $$ = abc_jump($$, loopstart);
+    code_t*out = $$ = abc_nop($$);
+    breakjumpsto($$, $1.name, out);
+    continuejumpsto($$, $1.name, cont);
+    myif->branch = out;
+
+    $$ = var_block($$);
+    old_state();
+}
+
+FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
+    variable_t*var = find_variable($2);
+    char*tmp1name = concat2($2, "__tmp1__");
+    int it = new_variable(tmp1name, TYPE_INT, 0);
+    char*tmp2name = concat2($2, "__array__");
+    int array = new_variable(tmp1name, 0, 0);
+
+    $$ = code_new();
+    $$ = code_append($$, $4.c);
+    $$ = abc_coerce_a($$);
+    $$ = abc_setlocal($$, array);
+    $$ = abc_pushbyte($$, 0);
+    $$ = abc_setlocal($$, it);
+
+    code_t*loopstart = $$ = abc_label($$);
+    
+    $$ = abc_hasnext2($$, array, it);
+    code_t*myif = $$ = abc_iffalse($$, 0);
+    $$ = abc_getlocal($$, array);
+    $$ = abc_getlocal($$, it);
+    if(!$1.each)
+        $$ = abc_nextname($$);
+    else
+        $$ = abc_nextvalue($$);
+    $$ = converttype($$, 0, var->type);
+    $$ = abc_setlocal($$, var->index);
+
+    $$ = code_append($$, $6);
     $$ = abc_jump($$, loopstart);
-    $$ = abc_label($$);
-    myif->branch = $$;
+    
+    code_t*out = $$ = abc_nop($$);
+    breakjumpsto($$, $1.name, out);
+    continuejumpsto($$, $1.name, loopstart);
+    
+    myif->branch = out;
+
+    $$ = var_block($$);
     old_state();
+
+    free(tmp1name);
+    free(tmp2name);
 }
 
-WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
-    code_t*myjmp = $$ = abc_jump(0, 0);
+WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK {
+
+    $$ = code_new();
+
+    code_t*myjmp = $$ = abc_jump($$, 0);
     code_t*loopstart = $$ = abc_label($$);
     $$ = code_append($$, $6);
-    myjmp->branch = $$ = abc_label($$);
+    code_t*cont = $$ = abc_nop($$);
+    myjmp->branch = cont;
     $$ = code_append($$, $4.c);
     $$ = abc_iftrue($$, loopstart);
+    code_t*out = $$ = abc_nop($$);
+    breakjumpsto($$, $1, out);
+    continuejumpsto($$, $1, cont);
+
+    $$ = var_block($$);
+    old_state();
+}
+
+DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
+    $$ = code_new();
+    code_t*loopstart = $$ = abc_label($$);
+    $$ = code_append($$, $3);
+    code_t*cont = $$ = abc_nop($$);
+    $$ = code_append($$, $6.c);
+    $$ = abc_iftrue($$, loopstart);
+    code_t*out = $$ = abc_nop($$);
+    breakjumpsto($$, $1, out);
+    continuejumpsto($$, $1, cont);
+    
+    $$ = var_block($$);
     old_state();
 }
 
-BREAK : "break" {
-    $$ = abc___break__(0);
+BREAK : "break" %prec prec_none {
+    $$ = abc___break__(0, "");
+}
+BREAK : "break" T_IDENTIFIER {
+    $$ = abc___break__(0, $2);
+}
+CONTINUE : "continue" %prec prec_none {
+    $$ = abc___continue__(0, "");
+}
+CONTINUE : "continue" T_IDENTIFIER {
+    $$ = abc___continue__(0, $2);
+}
+
+MAYBE_CASE_LIST :           {$$=0;}
+MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
+MAYBE_CASE_LIST : DEFAULT   {$$=$1;}
+MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
+CASE_LIST: CASE             {$$=$1;}
+CASE_LIST: CASE_LIST CASE   {$$=code_append($$,$2);}
+
+CASE: "case" E ':' MAYBECODE {
+    $$ = abc_dup(0);
+    $$ = code_append($$, $2.c);
+    code_t*j = $$ = abc_ifne($$, 0);
+    $$ = code_append($$, $4);
+    if($$->opcode != OPCODE___BREAK__) {
+        $$ = abc___fallthrough__($$, "");
+    }
+    code_t*e = $$ = abc_nop($$);
+    j->branch = e;
+}
+DEFAULT: "default" ':' MAYBECODE {
+    $$ = $3;
+}
+SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
+    $$=$4.c;
+    $$ = code_append($$, $7);
+    code_t*out = $$ = abc_pop($$);
+    breakjumpsto($$, $1, out);
+    
+    code_t*c = $$,*lastblock=0;
+    while(c) {
+        if(c->opcode == OPCODE_IFNE) {
+            if(!c->next) syntaxerror("internal error in fallthrough handling");
+            lastblock=c->next;
+        } else if(c->opcode == OPCODE___FALLTHROUGH__) {
+            if(lastblock) {
+                c->opcode = OPCODE_JUMP;
+                c->branch = lastblock;
+            } else {
+                /* fall through end of switch */
+                c->opcode = OPCODE_NOP;
+            }
+        }
+        c=c->prev;
+    }
+   
+    $$ = var_block($$);
+    old_state();
+}
+
+/* ------------ try / catch /finally ---------------- */
+
+CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name=$3;new_variable($3, $4, 0);} 
+        '{' MAYBECODE '}' {
+    namespace_t name_ns = {ACCESS_PACKAGE, ""};
+    multiname_t name = {QNAME, &name_ns, 0, $3};
+    
+    NEW(abc_exception_t, e)
+    e->exc_type = sig2mname($4);
+    e->var_name = multiname_clone(&name);
+    $$ = e;
+
+    code_t*c = 0;
+    int i = find_variable_safe($3)->index;
+    e->target = c = abc_nop(0);
+    c = abc_setlocal(c, i);
+    c = code_append(c, $8);
+    c = abc_kill(c, i);
+
+    c = var_block(c);
+    old_state();
+}
+FINALLY: "finally" '{' {new_state();state->exception_name=0;} MAYBECODE '}' {
+    $4 = var_block($4);
+    if(!$4) {
+        $$=0;
+        old_state();
+    } else {
+        NEW(abc_exception_t, e)
+        e->exc_type = 0; //all exceptions
+        e->var_name = 0; //no name
+        e->target = 0;
+        e->to = abc_nop(0);
+        e->to = code_append(e->to, $4);
+        old_state();
+        $$ = e;
+    }
+}
+
+CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
+CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
+CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
+CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
+    $$ = $1;
+    $$.finally = 0;
+    if($2) {
+        list_append($$.l,$2);
+        $$.finally = $2->to;$2->to=0;
+    }
+}
+CATCH_FINALLY_LIST: FINALLY {
+    $$.l=list_new();
+    $$.finally = 0;
+    if($1) {
+        list_append($$.l,$1);
+        $$.finally = $1->to;$1->to=0;
+    }
+}
+
+TRY : "try" '{' {new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
+    code_t*out = abc_nop(0);
+
+    code_t*start = abc_nop(0);
+    $$ = code_append(start, $4);
+    if(!is_break_or_jump($4)) {
+        $$ = abc_jump($$, out);
+    }
+    code_t*end = $$ = abc_nop($$);
+  
+    int tmp;
+    if($6.finally)
+        tmp = new_variable("__finally__", 0, 0);
+    
+    abc_exception_list_t*l = $6.l;
+    int count=0;
+    while(l) {
+        abc_exception_t*e = l->abc_exception;
+        if(e->var_name) {
+            $$ = code_append($$, e->target);
+            $$ = abc_jump($$, out);
+        } else {
+            parserassert((ptroff_t)$6.finally);
+            // finally block
+            e->target = $$ = abc_nop($$);
+            $$ = abc___rethrow__($$);
+        }
+        
+        e->from = start;
+        e->to = end;
+
+        l = l->next;
+    }
+    $$ = code_append($$, out);
+
+    $$ = insert_finally($$, $6.finally, tmp);
+        
+    list_concat(state->method->exceptions, $6.l);
+   
+    $$ = var_block($$);
+    old_state();
+}
+
+/* ------------ throw ------------------------------- */
+
+THROW : "throw" EXPRESSION {
+    $$=$2.c;
+    $$=abc_throw($$);
+}
+THROW : "throw" %prec prec_none {
+    if(!state->exception_name)
+        syntaxerror("re-throw only possible within a catch block");
+    variable_t*v = find_variable(state->exception_name);
+    $$=code_new();
+    $$=abc_getlocal($$, v->index);
+    $$=abc_throw($$);
+}
+
+/* ------------ with -------------------------------- */
+
+WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
+     $$ = $3.c;
+     $$ = abc_pushscope($$);
+     $$ = code_append($$, $5);
+     $$ = abc_popscope($$);
 }
 
 /* ------------ packages and imports ---------------- */
 
-PACKAGE_DECLARATION : "package" MULTILEVELIDENTIFIER '{' {startpackage($2)} MAYBECODE '}' {endpackage()}
-PACKAGE_DECLARATION : "package" '{' {startpackage(0)} MAYBECODE '}' {endpackage()}
+X_IDENTIFIER: T_IDENTIFIER
+            | "package" {PASS12 $$="package";}
+
+PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
+PACKAGE: X_IDENTIFIER             {PASS12 $$=strdup($1);}
 
-IMPORT : "import" PACKAGESPEC {addimport($2);}
+PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
+                                MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
+PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");} 
+                                MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
 
-TYPE : PACKAGEANDCLASS {$$=$1;}
+IMPORT : "import" QNAME {
+       classinfo_t*c = $2;
+       if(!c) 
+            syntaxerror("Couldn't import class\n");
+       state_has_imports();
+       dict_put(state->imports, c->name, c);
+       $$=0;
+}
+IMPORT : "import" PACKAGE '.' '*' {
+       NEW(import_t,i);
+       i->package = $2;
+       state_has_imports();
+       list_append(state->wildcard_imports, i);
+       $$=0;
+}
+
+/* ------------ classes and interfaces (header) -------------- */
+
+MAYBE_MODIFIERS : %prec above_function {PASS12 $$=0;}
+MAYBE_MODIFIERS : MODIFIER_LIST        {PASS12 $$=$1;}
+MODIFIER_LIST : MODIFIER               {PASS12 $$=$1;}
+MODIFIER_LIST : MODIFIER_LIST MODIFIER {PASS12 $$=$1|$2;}
+
+MODIFIER : KW_PUBLIC {PASS12 $$=FLAG_PUBLIC;}
+         | KW_PRIVATE {PASS12 $$=FLAG_PRIVATE;}
+         | KW_PROTECTED {PASS12 $$=FLAG_PROTECTED;}
+         | KW_STATIC {PASS12 $$=FLAG_STATIC;}
+         | KW_DYNAMIC {PASS12 $$=FLAG_DYNAMIC;}
+         | KW_FINAL {PASS12 $$=FLAG_FINAL;}
+         | KW_OVERRIDE {PASS12 $$=FLAG_OVERRIDE;}
+         | KW_NATIVE {PASS12 $$=FLAG_NATIVE;}
+         | KW_INTERNAL {PASS12 $$=FLAG_PACKAGEINTERNAL;}
+
+EXTENDS : {$$=registry_getobjectclass();}
+EXTENDS : KW_EXTENDS QNAME {$$=$2;}
+
+EXTENDS_LIST : {PASS12 $$=list_new();}
+EXTENDS_LIST : KW_EXTENDS QNAME_LIST {PASS12 $$=$2;}
+
+IMPLEMENTS_LIST : {PASS12 $$=list_new();}
+IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {PASS12 $$=$2;}
+
+CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER 
+                              EXTENDS IMPLEMENTS_LIST 
+                              '{' {PASS12 startclass($1,$3,$4,$5, 0);} 
+                              MAYBE_CLASS_BODY 
+                              '}' {PASS12 endclass();$$=0;}
+
+INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER 
+                              EXTENDS_LIST 
+                              '{' {PASS12 startclass($1,$3,0,$4,1);}
+                              MAYBE_INTERFACE_BODY 
+                              '}' {PASS12 endclass();$$=0;}
+
+/* ------------ classes and interfaces (body) -------------- */
+
+MAYBE_CLASS_BODY : 
+MAYBE_CLASS_BODY : CLASS_BODY
+CLASS_BODY : CLASS_BODY_ITEM
+CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
+CLASS_BODY_ITEM : ';'
+CLASS_BODY_ITEM : SLOT_DECLARATION
+CLASS_BODY_ITEM : FUNCTION_DECLARATION
+
+CLASS_BODY_ITEM : CODE_STATEMENT {
+    code_t*c = state->cls->static_init;
+    c = code_append(c, $1);  
+    state->cls->static_init = c;
+}
+
+MAYBE_INTERFACE_BODY : 
+MAYBE_INTERFACE_BODY : INTERFACE_BODY
+INTERFACE_BODY : IDECLARATION
+INTERFACE_BODY : INTERFACE_BODY IDECLARATION
+IDECLARATION : ';'
+IDECLARATION : "var" T_IDENTIFIER {
+    syntaxerror("variable declarations not allowed in interfaces");
+}
+IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
+    PASS12
+    $1 |= FLAG_PUBLIC;
+    if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
+        syntaxerror("invalid method modifiers: interface methods always need to be public");
+    }
+    startfunction(0,$1,$3,$4,&$6,$8);
+    endfunction(0,$1,$3,$4,&$6,$8, 0);
+}
+
+/* ------------ classes and interfaces (body, slots ) ------- */
+
+VARCONST: "var" | "const"
+
+SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
+    int flags = $1;
+    memberinfo_t* info = state->cls?
+            memberinfo_register(state->cls->info, $3, MEMBER_SLOT):
+            memberinfo_register_global(flags2access($1), state->package, $3, MEMBER_SLOT);
+
+    info->type = $4;
+    info->flags = flags;
+
+    /* slot name */
+    namespace_t mname_ns = {flags2access(flags), ""};
+    multiname_t mname = {QNAME, &mname_ns, 0, $3};
+  
+    trait_list_t**traits;
+    code_t**code;
+    if(!state->cls) {
+        // global variable
+        mname_ns.name = state->package;
+        traits = &global->init->traits;
+        code = &global->init->method->body->code;
+    } else if(flags&FLAG_STATIC) {
+        // static variable
+        traits = &state->cls->abc->static_traits;
+        code = &state->cls->static_init;
+    } else {
+        // instance variable
+        traits = &state->cls->abc->traits;
+        code = &state->cls->init;
+    }
+    
+    trait_t*t=0;
+    if($4) {
+        MULTINAME(m, $4);
+        t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
+    } else {
+        t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
+    }
+    info->slot = t->slot_id;
+    
+    /* initalization code (if needed) */
+    code_t*c = 0;
+    if($5.c && !is_pushundefined($5.c)) {
+        c = abc_getlocal_0(c);
+        c = code_append(c, $5.c);
+        c = converttype(c, $5.t, $4);
+        c = abc_setslot(c, t->slot_id);
+    }
+
+    *code = code_append(*code, c);
+
+    if($2==KW_CONST) {
+        t->kind= TRAIT_CONST;
+    }
+
+    $$=0;
+}
+
+/* ------------ 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 : "true" {$$ = constant_new_true($1);}
+STATICCONSTANT : "false" {$$ = constant_new_false($1);}
+STATICCONSTANT : "null" {$$ = constant_new_null($1);}
+
+/* ------------ classes and interfaces (body, functions) ------- */
+
+// non-vararg version
+MAYBE_PARAM_LIST: {
+    memset(&$$,0,sizeof($$));
+}
+MAYBE_PARAM_LIST: PARAM_LIST {
+    $$=$1;
+}
+
+// vararg version
+MAYBE_PARAM_LIST: "..." PARAM {
+    memset(&$$,0,sizeof($$));
+    $$.varargs=1;
+    list_append($$.list, $2);
+}
+MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
+    $$ =$1;
+    $$.varargs=1;
+    list_append($$.list, $4);
+}
+
+// non empty
+PARAM_LIST: PARAM_LIST ',' PARAM {
+    $$ = $1;
+    list_append($$.list, $3);
+}
+PARAM_LIST: PARAM {
+    memset(&$$,0,sizeof($$));
+    list_append($$.list, $1);
+}
+
+PARAM:  T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
+     $$ = malloc(sizeof(param_t));
+     $$->name=$1;
+     $$->type = $3;
+     $$->value = $4;
+}
+PARAM:  T_IDENTIFIER MAYBESTATICCONSTANT {
+     $$ = malloc(sizeof(param_t));
+     $$->name=$1;
+     $$->type = TYPE_ANY;
+     $$->value = $2;
+}
+GETSET : "get" {$$=$1;}
+       | "set" {$$=$1;}
+       |       {$$=0;}
+
+FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' 
+                      MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}' 
+{
+    PASS1 old_state();
+    PASS2
+    code_t*c = 0;
+    if(state->method->late_binding) {
+        c = abc_getlocal_0(c);
+        c = abc_pushscope(c);
+    }
+    if(state->method->is_constructor && !state->method->has_super) {
+        // call default constructor
+        c = abc_getlocal_0(c);
+        c = abc_constructsuper(c, 0);
+    }
+    c = wrap_function(c, 0, $11);
+
+    endfunction(0,$1,$3,$4,&$6,$8,c);
+    $$=0;
+}
+
+MAYBE_IDENTIFIER: T_IDENTIFIER
+MAYBE_IDENTIFIER: {$$=0;}
+INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE '{' MAYBECODE '}'
+{
+    syntaxerror("nested functions not supported yet");
+}
+
+
+/* ------------- package + class ids --------------- */
+
+CLASS: T_IDENTIFIER {
+    PASS1 $$=0;
+    PASS2
+    /* try current package */
+    $$ = find_class($1);
+    if(!$$) syntaxerror("Could not find class %s\n", $1);
+}
+
+PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
+    PASS1 $$=0;
+    PASS2
+    $$ = registry_findclass($1, $3);
+    if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
+    free($1);$1=0;
+}
+
+QNAME: PACKAGEANDCLASS
+     | CLASS
+
+QNAME_LIST : QNAME {PASS12 $$=list_new();list_append($$, $1);}
+QNAME_LIST : QNAME_LIST ',' QNAME {PASS12 $$=$1;list_append($$,$3);}
+
+TYPE : QNAME      {$$=$1;}
      | '*'        {$$=registry_getanytype();}
+     | "void"     {$$=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 '(' PARAMS ')' 
-                      MAYBETYPE
+/* ----------function calls, delete, constructor calls ------ */
+
+MAYBE_PARAM_VALUES :  %prec prec_none {$$.cc=0;$$.len=0;}
+MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
+
+MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
+MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
+EXPRESSION_LIST : NONCOMMAEXPRESSION             {$$.len=1;
+                                                  $$.cc = $1.c;
+                                                 }
+EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {
+                                                  $$.len= $1.len+1;
+                                                  $$.cc = code_append($1.cc, $3.c);
+                                                  }
+
+NEW : "new" CLASS MAYBE_PARAM_VALUES {
+    MULTINAME(m, $2);
+    $$.c = code_new();
+
+    if($2->slot) {
+        $$.c = abc_getglobalscope($$.c);
+        $$.c = abc_getslot($$.c, $2->slot);
+    } else {
+        $$.c = abc_findpropstrict2($$.c, &m);
+    }
+
+    $$.c = code_append($$.c, $3.cc);
 
-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
+    if($2->slot)
+        $$.c = abc_construct($$.c, $3.len);
+    else
+        $$.c = abc_constructprop2($$.c, &m, $3.len);
+    $$.t = $2;
+}
+
+/* TODO: use abc_call (for calling local variables),
+         abc_callstatic (for calling own methods) 
+         call (for closures)
+*/
+FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
+    
+    $$.c = $1.c;
+    if($$.c->opcode == OPCODE_COERCE_A) {
+        $$.c = code_cutlast($$.c);
+    }
+    code_t*paramcode = $3.cc;
+
+    $$.t = TYPE_ANY;
+    if($$.c->opcode == OPCODE_GETPROPERTY) {
+        multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
+        $$.c = code_cutlast($$.c);
+        $$.c = code_append($$.c, paramcode);
+        $$.c = abc_callproperty2($$.c, name, $3.len);
+        multiname_destroy(name);
+    } else if($$.c->opcode == OPCODE_GETSLOT) {
+        int slot = (int)(ptroff_t)$$.c->data[0];
+        trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
+        if(t->kind!=TRAIT_METHOD) {
+            //ok: flash allows to assign closures to members.
+        }
+        multiname_t*name = t->name;
+        $$.c = code_cutlast($$.c);
+        $$.c = code_append($$.c, paramcode);
+        //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
+        $$.c = abc_callproperty2($$.c, name, $3.len);
+    } else if($$.c->opcode == OPCODE_GETSUPER) {
+        multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
+        $$.c = code_cutlast($$.c);
+        $$.c = code_append($$.c, paramcode);
+        $$.c = abc_callsuper2($$.c, name, $3.len);
+        multiname_destroy(name);
+    } else {
+        $$.c = abc_getlocal_0($$.c);
+        $$.c = code_append($$.c, paramcode);
+        $$.c = abc_call($$.c, $3.len);
+    }
+   
+    memberinfo_t*f = 0;
+   
+    if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
+        $$.t = $1.t->function->return_type;
+    } else {
+        $$.c = abc_coerce_a($$.c);
+        $$.t = TYPE_ANY;
+    }
 
-//NAMESPACE :              {$$=empty_token();}
-//NAMESPACE : T_IDENTIFIER {$$=$1};
+}
+FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
+    if(!state->cls) syntaxerror("super() not allowed outside of a class");
+    if(!state->method) syntaxerror("super() not allowed outside of a function");
+    if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
+
+    $$.c = code_new();
+    $$.c = abc_getlocal_0($$.c);
+
+    $$.c = code_append($$.c, $3.cc);
+    /*
+    this is dependent on the control path, check this somewhere else
+    if(state->method->has_super)
+        syntaxerror("constructor may call super() only once");
+    */
+    state->method->has_super = 1;
+    $$.c = abc_constructsuper($$.c, $3.len);
+    $$.c = abc_pushundefined($$.c);
+    $$.t = TYPE_ANY;
+}
+
+DELETE: "delete" E {
+    $$.c = $2.c;
+    if($$.c->opcode == OPCODE_COERCE_A) {
+        $$.c = code_cutlast($$.c);
+    }
+    multiname_t*name = 0;
+    if($$.c->opcode == OPCODE_GETPROPERTY) {
+        $$.c->opcode = OPCODE_DELETEPROPERTY;
+    } else if($$.c->opcode == OPCODE_GETSLOT) {
+        int slot = (int)(ptroff_t)$$.c->data[0];
+        multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
+        $$.c = code_cutlast($$.c);
+        $$.c = abc_deleteproperty2($$.c, name);
+    } else {
+        $$.c = abc_getlocal_0($$.c);
+        MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
+        $$.c = abc_deleteproperty2($$.c, &m);
+    }
+    $$.t = TYPE_BOOLEAN;
+}
+
+RETURN: "return" %prec prec_none {
+    $$ = abc_returnvoid(0);
+}
+RETURN: "return" EXPRESSION {
+    $$ = $2.c;
+    $$ = abc_returnvalue($$);
+}
+
+// ----------------------- expression types -------------------------------------
+
+NONCOMMAEXPRESSION : E        %prec below_minus {$$=$1;}
+EXPRESSION : E                %prec below_minus {$$ = $1;}
+EXPRESSION : EXPRESSION ',' E %prec below_minus {
+    $$.c = $1.c;
+    $$.c = cut_last_push($$.c);
+    $$.c = code_append($$.c,$3.c);
+    $$.t = $3.t;
+}
+VOIDEXPRESSION : EXPRESSION %prec below_minus {
+    $$=cut_last_push($1.c);
+}
+
+// ----------------------- expression evaluation -------------------------------------
+
+E : INNERFUNCTION %prec prec_none {$$ = $1;}
+//V : CONSTANT                    {$$ = 0;}
+E : CONSTANT
+//V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
+E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
+//V : NEW                         {$$ = $1.c;}
+E : NEW                         {$$ = $1;}
+//V : DELETE                      {$$ = $1.c;}
+E : DELETE                      {$$ = $1;}
+
+E : T_REGEXP {
+    $$.c = 0;
+    namespace_t ns = {ACCESS_PACKAGE, ""};
+    multiname_t m = {QNAME, &ns, 0, "RegExp"};
+    if(!$1.options) {
+        $$.c = abc_getlex2($$.c, &m);
+        $$.c = abc_pushstring($$.c, $1.pattern);
+        $$.c = abc_construct($$.c, 1);
+    } else {
+        $$.c = abc_getlex2($$.c, &m);
+        $$.c = abc_pushstring($$.c, $1.pattern);
+        $$.c = abc_pushstring($$.c, $1.options);
+        $$.c = abc_construct($$.c, 2);
+    }
+    $$.t = TYPE_REGEXP;
+}
 
 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
-                   MULTINAME(m, registry_getintclass());
-                   $$.c = abc_coerce2($$.c, &m); // FIXME
+                   //MULTINAME(m, registry_getintclass());
+                   //$$.c = abc_coerce2($$.c, &m); // FIXME
                    $$.t = TYPE_INT;
                   }
 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
@@ -810,33 +2447,22 @@ CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
                     $$.t = TYPE_FLOAT;
                    }
-CONSTANT : T_STRING {$$.c = abc_pushstring(0, $1);
+CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
                      $$.t = TYPE_STRING;
                     }
-CONSTANT : KW_TRUE {$$.c = abc_pushtrue(0);
+CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
+                    $$.t = TYPE_ANY;
+                   }
+CONSTANT : "true" {$$.c = abc_pushtrue(0);
                     $$.t = TYPE_BOOLEAN;
                    }
-CONSTANT : KW_FALSE {$$.c = abc_pushfalse(0);
+CONSTANT : "false" {$$.c = abc_pushfalse(0);
                      $$.t = TYPE_BOOLEAN;
                     }
-CONSTANT : KW_NULL {$$.c = abc_pushnull(0);
+CONSTANT : "null" {$$.c = abc_pushnull(0);
                     $$.t = TYPE_NULL;
                    }
 
-USE_NAMESPACE : "use" "namespace" T_IDENTIFIER
-
-
-EXPRESSION : E %prec prec_none  /*precendence below '-x'*/ {$$ = $1;}
-VOIDEXPRESSION : E %prec prec_none {$$=$1.c;/*calculate and discard*/$$=abc_pop($$);}
-
-E : CONSTANT
-E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
-E : NEW                         {$$.c = abc_pushundefined(0); /* FIXME */
-                                 $$.t = TYPE_ANY;
-                                }
-E : T_REGEXP                    {$$.c = abc_pushundefined(0); /* FIXME */
-                                 $$.t = TYPE_ANY;
-                                }
 E : FUNCTIONCALL
 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
              $$.t = TYPE_BOOLEAN;
@@ -855,170 +2481,526 @@ E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
              }
 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
               $$.t = TYPE_BOOLEAN;
+              }
+E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
+              $$.t = TYPE_BOOLEAN;
              }
 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
               $$.t = TYPE_BOOLEAN;
              }
 
-E : E '-' E
-E : E '/' E
-E : E '+' E {$$.c = code_append($1.c,$3.c);$$.c = abc_add($$.c);$$.c=abc_coerce_a($$.c);
-             $$.t = join_types($1.t, $3.t, '+');
-            }
-E : E '%' E {$$.c = code_append($1.c,$3.c);$$.c = abc_modulo($$.c);$$.c=abc_coerce_a($$.c);
-             $$.t = join_types($1.t, $3.t, '%');
+E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
+              $$.c = $1.c;
+              $$.c = converttype($$.c, $1.t, $$.t);
+              $$.c = abc_dup($$.c);
+              code_t*jmp = $$.c = abc_iftrue($$.c, 0);
+              $$.c = cut_last_push($$.c);
+              $$.c = code_append($$.c,$3.c);
+              $$.c = converttype($$.c, $3.t, $$.t);
+              code_t*label = $$.c = abc_label($$.c);
+              jmp->branch = label;
+             }
+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);
+              code_t*jmp = $$.c = abc_iffalse($$.c, 0);
+              $$.c = cut_last_push($$.c);
+              $$.c = code_append($$.c,$3.c);
+              $$.c = converttype($$.c, $3.t, $$.t);
+              code_t*label = $$.c = abc_label($$.c);
+              jmp->branch = label;              
+             }
+
+E : '!' E    {$$.c=$2.c;
+              $$.c = abc_not($$.c);
+              $$.t = TYPE_BOOLEAN;
+             }
+
+E : '~' E    {$$.c=$2.c;
+              $$.c = abc_bitnot($$.c);
+              $$.t = TYPE_INT;
+             }
+
+E : E '&' E {$$.c = code_append($1.c,$3.c);
+             $$.c = abc_bitand($$.c);
+             $$.t = TYPE_INT;
             }
-E : E '*' E {$$.c = code_append($1.c,$3.c);$$.c = abc_multiply($$.c);$$.c=abc_coerce_a($$.c);
-             $$.t = join_types($1.t, $3.t, '*');
+
+E : E '^' E {$$.c = code_append($1.c,$3.c);
+             $$.c = abc_bitxor($$.c);
+             $$.t = TYPE_INT;
             }
 
-E : E "as" TYPE
-E : E "is" TYPE
-E : '(' E ')' {$$=$2;}
-E : '-' E {$$=$2;}
+E : E '|' E {$$.c = code_append($1.c,$3.c);
+             $$.c = abc_bitor($$.c);
+             $$.t = TYPE_INT;
+            }
 
-E : LH "+=" E {$$.c = $1.read;$$.c=code_append($$.c,$3.c);$$.c=abc_add($$.c);
-               MULTINAME(m, registry_getintclass());
-               $$.c=abc_coerce2($$.c, &m); // FIXME
-               $$.c=abc_dup($$.c);$$.c=code_append($$.c,$1.write);
-               $$.t = $1.type;
-              }
-E : LH "-=" E {$$.c = $1.read;$$.c=code_append($$.c,$3.c);$$.c=abc_add($$.c);
-               MULTINAME(m, registry_getintclass());
-               $$.c=abc_coerce2($$.c, &m); // FIXME
-               $$.c=abc_dup($$.c);$$.c=code_append($$.c,$1.write);
-               $$.t = $1.type;
-              }
+E : E ">>" E {$$.c = code_append($1.c,$3.c);
+             $$.c = abc_rshift($$.c);
+             $$.t = TYPE_INT;
+            }
+E : E ">>>" E {$$.c = code_append($1.c,$3.c);
+             $$.c = abc_urshift($$.c);
+             $$.t = TYPE_INT;
+            }
+E : E "<<" E {$$.c = code_append($1.c,$3.c);
+             $$.c = abc_lshift($$.c);
+             $$.t = TYPE_INT;
+            }
 
-// TODO: use inclocal where appropriate
-E : LH "++" {$$.c = $1.read;$$.c=abc_increment($$.c);
-             MULTINAME(m, registry_getintclass());
-             $$.c=abc_coerce2($$.c, &m); //FIXME
-             $$.c=abc_dup($$.c);$$.c=code_append($$.c,$1.write);
-             $$.t = $1.type;
+E : E '/' E {$$.c = code_append($1.c,$3.c);
+             $$.c = abc_divide($$.c);
+             $$.t = TYPE_NUMBER;
             }
-E : LH "--" {$$.c = $1.read;$$.c=abc_decrement($$.c);
-             MULTINAME(m, registry_getintclass());
-             $$.c=abc_coerce2($$.c, &m); //FIXME
-             $$.c=abc_dup($$.c);$$.c=code_append($$.c,$1.write);
-             $$.t = $1.type;
+E : E '%' E {$$.c = code_append($1.c,$3.c);
+             $$.c = abc_modulo($$.c);
+             $$.t = TYPE_NUMBER;
+            }
+E : E '+' E {$$.c = code_append($1.c,$3.c);
+             if(BOTH_INT($1.t, $3.t)) {
+                $$.c = abc_add_i($$.c);
+                $$.t = TYPE_INT;
+             } else {
+                $$.c = abc_add($$.c);
+                $$.t = join_types($1.t,$3.t,'+');
+             }
+            }
+E : E '-' E {$$.c = code_append($1.c,$3.c);
+             if(BOTH_INT($1.t,$3.t)) {
+                $$.c = abc_subtract_i($$.c);
+                $$.t = TYPE_INT;
+             } else {
+                $$.c = abc_subtract($$.c);
+                $$.t = TYPE_NUMBER;
+             }
+            }
+E : E '*' E {$$.c = code_append($1.c,$3.c);
+             if(BOTH_INT($1.t,$3.t)) {
+                $$.c = abc_multiply_i($$.c);
+                $$.t = TYPE_INT;
+             } else {
+                $$.c = abc_multiply($$.c);
+                $$.t = TYPE_NUMBER;
+             }
             }
 
-LH: T_IDENTIFIER {
-  int i = find_variable($1->text, &$$.type);
-  $$.read = abc_getlocal(0, i);
-  $$.write = abc_setlocal(0, i);
-}
-
-NEW : "new" T_IDENTIFIER                         {$$.c=0;$$.t=0;/*FIXME*/}
-    | "new" T_IDENTIFIER '(' ')'                 {$$.c=0;$$.t=0;/*FIXME*/}
-    | "new" T_IDENTIFIER '(' EXPRESSION_LIST ')' {$$.c=0;$$.t=0;/*FIXME*/}
-
-FUNCTIONCALL : T_IDENTIFIER '(' MAYBE_EXPRESSION_LIST ')' {
-        /* TODO: use abc_call (for calling local variables),
-                 abc_callstatic (for calling own methods) */
-        $$.c = code_new();
-        $$.c = abc_findpropstrict($$.c, $1->text);
-        typedcode_list_t*l = $3;
-        // push parameters on stack
-        int len = 0;
-        while(l) {
-            $$.c = code_append($$.c, l->typedcode.c);
-            l = l->nxt;
-            len ++;
-        }
-        $$.c = abc_callproperty($$.c, $1->text, len);
-}
+E : E "in" E {$$.c = code_append($1.c,$3.c);
+              $$.c = abc_in($$.c);
+              $$.t = TYPE_BOOLEAN;
+             }
 
-MAYBE_EXPRESSION_LIST : {$$=0;}
-MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
-EXPRESSION_LIST : EXPRESSION                     {$$=malloc(sizeof(typedcode_list_t));
-                                                  $$->nxt = 0;
-                                                  $$->typedcode = $1;}
-EXPRESSION_LIST : EXPRESSION_LIST ',' EXPRESSION {$$=malloc(sizeof(typedcode_list_t));
-                                                  $$->nxt = $1;
-                                                  $$->typedcode = $3;
-                                                 }
+E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
+              if(use_astype && TYPE_IS_CLASS($3.t)) {
+                MULTINAME(m,$3.t->cls);
+                $$.c = abc_astype2($1.c, &m);
+                $$.t = $3.t->cls;
+              } else {
+                $$.c = code_append($1.c, $3.c);
+                $$.c = abc_astypelate($$.c);
+                $$.t = TYPE_ANY;
+              }
+             }
 
-VAR_READ : T_IDENTIFIER {
-        int i = find_variable($1->text, &$$.t);
-        $$.c = abc_getlocal(0, i);
+E : E "instanceof" E 
+             {$$.c = code_append($1.c, $3.c);
+              $$.c = abc_instanceof($$.c);
+              $$.t = TYPE_BOOLEAN;
+             }
+
+E : E "is" E {$$.c = code_append($1.c, $3.c);
+              $$.c = abc_istypelate($$.c);
+              $$.t = TYPE_BOOLEAN;
+             }
+
+E : "typeof" '(' E ')' {
+              $$.c = $3.c;
+              $$.c = abc_typeof($$.c);
+              $$.t = TYPE_STRING;
+             }
+
+E : "void" E {
+              $$.c = cut_last_push($2.c);
+              $$.c = abc_pushundefined($$.c);
+              $$.t = TYPE_ANY;
+             }
+
+E : "void" { $$.c = abc_pushundefined(0);
+             $$.t = TYPE_ANY;
+           }
+
+E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
+
+E : '-' E {
+  $$=$2;
+  if(IS_INT($2.t)) {
+   $$.c=abc_negate_i($$.c);
+   $$.t = TYPE_INT;
+  } else {
+   $$.c=abc_negate($$.c);
+   $$.t = TYPE_NUMBER;
+  }
 }
 
-//VARIABLE : T_IDENTIFIER
-//VARIABLE : VARIABLE '.' T_IDENTIFIER
-//VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
-//VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
-//VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
-//VARIABLE : VARIABLE '[' EXPRESSION ']' // unqualified expression
+E : E '[' E ']' {
+  $$.c = $1.c;
+  $$.c = code_append($$.c, $3.c);
 
-// keywords which also may be identifiers
-X_IDENTIFIER : T_IDENTIFIER | KW_PACKAGE
+  MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
+  $$.c = abc_getproperty2($$.c, &m);
+  $$.t = 0; // array elements have unknown type
+}
 
-PACKAGESPEC : PACKAGESPEC '.' PACKAGESPEC {if($1->text[0]=='*') syntaxerror("wildcard in the middle of path");
-                                           $$ = concat3($1,$2,$3);}
-PACKAGESPEC : X_IDENTIFIER                {$$=$1;}
-PACKAGESPEC : '*'                         {$$=$1;}
+E : '[' MAYBE_EXPRESSION_LIST ']' {
+    $$.c = code_new();
+    $$.c = code_append($$.c, $2.cc);
+    $$.c = abc_newarray($$.c, $2.len);
+    $$.t = registry_getarrayclass();
+}
 
-GETSET : "get" {$$=$1;}
-       | "set" {$$=$1;}
-       |       {$$=empty_token();}
+MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
+MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
 
-CLASS_DECLARATION : MODIFIERS "class" T_IDENTIFIER EXTENDS IMPLEMENTS_LIST '{' {startclass($1,$3,$4,$5);} MAYBE_DECLARATION_LIST '}' {endclass();}
-INTERFACE_DECLARATION : MODIFIERS "interface" T_IDENTIFIER EXTENDS_LIST '{' MAYBE_IDECLARATION_LIST '}'
+EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
+    $$.cc = 0;
+    $$.cc = code_append($$.cc, $1.c);
+    $$.cc = code_append($$.cc, $3.c);
+    $$.len = 2;
+}
+EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
+    $$.cc = $1.cc;
+    $$.len = $1.len+2;
+    $$.cc = code_append($$.cc, $3.c);
+    $$.cc = code_append($$.cc, $5.c);
+}
+//MAYBECOMMA: ','
+//MAYBECOMMA:
+
+E : '{' MAYBE_EXPRPAIR_LIST '}' {
+    $$.c = code_new();
+    $$.c = code_append($$.c, $2.cc);
+    $$.c = abc_newobject($$.c, $2.len/2);
+    $$.t = registry_getobjectclass();
+}
 
-PARAMS: {$$=empty_token();}
-PARAMS: PARAM_LIST {$$=$1;}
-PARAM_LIST: PARAM_LIST ',' PARAM {extend($1,$3);$$=$1;}
-PARAM_LIST: PARAM                {$$=empty_token();extend($$,$1);}
-PARAM:  T_IDENTIFIER ':' TYPE {$$=$1;}
+E : E "*=" E { 
+               code_t*c = $3.c;
+               if(BOTH_INT($1.t,$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;
+              }
 
-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
+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 = abc_bitor($3.c);
+               c=converttype(c, TYPE_INT, $1.t);
+               $$.c = toreadwrite($1.c, c, 0, 0);
+               $$.t = $1.t;
+              }
+E : E "+=" E { 
+               code_t*c = $3.c;
+
+               if(TYPE_IS_INT($1.t)) {
+                c=abc_add_i(c);
+               } else {
+                c=abc_add(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($1.t)) {
+                c=abc_subtract_i(c);
+               } else {
+                c=abc_subtract(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 = 0;
+              c = code_append(c, $3.c);
+              c = converttype(c, $3.t, $1.t);
+              $$.c = toreadwrite($1.c, c, 1, 0);
+              $$.t = $1.t;
+            }
 
-DECLARATION : VARIABLE_DECLARATION
-DECLARATION : FUNCTION_DECLARATION
+E : E '?' E ':' E %prec below_assignment { 
+              $$.t = join_types($3.t,$5.t,'?');
+              $$.c = $1.c;
+              code_t*j1 = $$.c = abc_iffalse($$.c, 0);
+              $$.c = code_append($$.c, $3.c);
+              $$.c = converttype($$.c, $3.t, $$.t);
+              code_t*j2 = $$.c = abc_jump($$.c, 0);
+              $$.c = j1->branch = abc_label($$.c);
+              $$.c = code_append($$.c, $5.c);
+              $$.c = converttype($$.c, $3.t, $$.t);
+              $$.c = j2->branch = abc_label($$.c);
+            }
 
-IDECLARATION : VARIABLE_DECLARATION
-IDECLARATION : FUNCTION_DECLARATION
+E : E "++" { code_t*c = 0;
+             classinfo_t*type = $1.t;
+             if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) {
+                 int nr = getlocalnr($1.c);
+                 code_free($1.c);$1.c=0;
+                 if(TYPE_IS_INT($1.t)) {
+                    $$.c = abc_getlocal(0, nr);
+                    $$.c = abc_inclocal_i($$.c, nr);
+                 } else if(TYPE_IS_NUMBER($1.t)) {
+                    $$.c = abc_getlocal(0, nr);
+                    $$.c = abc_inclocal($$.c, nr);
+                 } else syntaxerror("internal error");
+             } else {
+                 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, 1);
+                 $$.t = $1.t;
+             }
+           }
+
+// TODO: use inclocal, like with ++
+E : E "--" { code_t*c = 0;
+             classinfo_t*type = $1.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, $1.t);
+             $$.c = toreadwrite($1.c, c, 0, 1);
+             $$.t = $1.t;
+            }
 
-IMPLEMENTS_LIST : {$$=list_new();}
-IMPLEMENTS_LIST : KW_IMPLEMENTS PACKAGEANDCLASS_LIST {$$=$2;}
+E : "++" %prec plusplus_prefix 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 : "--" %prec minusminus_prefix 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 : "super" '.' T_IDENTIFIER 
+           { if(!state->cls->info)
+                  syntaxerror("super keyword not allowed outside a class");
+              classinfo_t*t = state->cls->info->superclass;
+              if(!t) t = TYPE_OBJECT;
+
+              memberinfo_t*f = registry_findmember(t, $3, 1);
+              namespace_t ns = flags2namespace(f->flags, "");
+              MEMBER_MULTINAME(m, f, $3);
+              $$.c = 0;
+              $$.c = abc_getlocal_0($$.c);
+              $$.c = abc_getsuper2($$.c, &m);
+              $$.t = memberinfo_gettype(f);
+           }
+
+E : E '.' T_IDENTIFIER
+            {$$.c = $1.c;
+             classinfo_t*t = $1.t;
+             char is_static = 0;
+             if(TYPE_IS_CLASS(t) && t->cls) {
+                 t = t->cls;
+                 is_static = 1;
+             }
+             if(t) {
+                 memberinfo_t*f = registry_findmember(t, $3, 1);
+                 char noslot = 0;
+                 if(f && !is_static != !(f->flags&FLAG_STATIC))
+                    noslot=1;
+                 if(f && f->slot && !noslot) {
+                     $$.c = abc_getslot($$.c, f->slot);
+                 } else {
+                     MEMBER_MULTINAME(m, f, $3);
+                     $$.c = abc_getproperty2($$.c, &m);
+                 }
+                 /* determine type */
+                 $$.t = memberinfo_gettype(f);
+                 if(!$$.t)
+                    $$.c = abc_coerce_a($$.c);
+             } else {
+                 /* 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 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();
+             }
+            }
 
-EXTENDS : {$$=registry_getobjectclass();}
-EXTENDS : KW_EXTENDS PACKAGEANDCLASS {$$=$2;}
+VAR_READ : T_IDENTIFIER {
+    $$.t = 0;
+    $$.c = 0;
+    classinfo_t*a = 0;
+    memberinfo_t*f = 0;
+
+    variable_t*v;
+    /* look at variables */
+    if((v = find_variable($1))) {
+        // $1 is a local variable
+        $$.c = abc_getlocal($$.c, v->index);
+        $$.t = v->type;
+        break;
+    }
 
-EXTENDS_LIST : {$$=list_new();}
-EXTENDS_LIST : KW_EXTENDS PACKAGEANDCLASS_LIST {$$=$2;}
+    int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
 
-//IDENTIFIER_LIST : T_IDENTIFIER ',' IDENTIFIER_LIST {extend($3,$1);$$=$3;}
-//IDENTIFIER_LIST : T_IDENTIFIER                     {$$=empty_token();extend($$,$1);}
+    /* look at current class' members */
+    if(state->cls && (f = registry_findmember(state->cls->info, $1, 1)) &&
+        (f->flags&FLAG_STATIC) >= i_am_static) {
+        // $1 is a function in this class
+        int var_is_static = (f->flags&FLAG_STATIC);
 
-PACKAGEANDCLASS : T_IDENTIFIER {$$ = registry_findclass(state->package, $1->text);}
-PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {$$ = registry_findclass($1->text, $3->text);}
-PACKAGE : X_IDENTIFIER
-PACKAGE : PACKAGE '.' X_IDENTIFIER {$$=$1;extend_s($$,".",$3);}
+        if(f->kind == MEMBER_METHOD) {
+            $$.t = TYPE_FUNCTION(f);
+        } else {
+            $$.t = f->type;
+        }
+        if(var_is_static && !i_am_static) {
+        /* access to a static member from a non-static location.
+           do this via findpropstrict:
+           there doesn't seem to be any non-lookup way to access
+           static properties of a class */
+            state->method->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);
+            break;
+        } else if(f->slot>0) {
+            $$.c = abc_getlocal_0($$.c);
+            $$.c = abc_getslot($$.c, f->slot);
+            break;
+        } else {
+            namespace_t ns = {flags2access(f->flags), ""};
+            multiname_t m = {QNAME, &ns, 0, $1};
+            $$.c = abc_getlocal_0($$.c);
+            $$.c = abc_getproperty2($$.c, &m);
+            break;
+        }
+    } 
+    
+    /* look at actual classes, in the current package and imported */
+    if((a = find_class($1))) {
+        if(a->flags & FLAG_METHOD) {
+            MULTINAME(m, a);
+            $$.c = abc_findpropstrict2($$.c, &m);
+            $$.c = abc_getproperty2($$.c, &m);
+            if(a->function->kind == MEMBER_METHOD) {
+                $$.t = TYPE_FUNCTION(a->function);
+            } else {
+                $$.t = a->function->type;
+            }
+        } else {
+            if(a->slot) {
+                $$.c = abc_getglobalscope($$.c);
+                $$.c = abc_getslot($$.c, a->slot);
+            } else {
+                MULTINAME(m, a);
+                $$.c = abc_getlex2($$.c, &m);
+            }
+            $$.t = TYPE_CLASS(a);
+        }
+        break;
+    }
 
-MULTILEVELIDENTIFIER : MULTILEVELIDENTIFIER '.' X_IDENTIFIER {$$=$1;extend_s($$, ".", $3)}
-MULTILEVELIDENTIFIER : T_IDENTIFIER                 {$$=$1;extend($$,$1)};
+    /* unknown object, let the avm2 resolve it */
+    if(1) {
+        if(strcmp($1,"trace"))
+            as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
+        state->method->late_binding = 1;
+                
+        multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
+
+        $$.t = 0;
+        $$.c = abc_findpropstrict2($$.c, &m);
+        $$.c = abc_getproperty2($$.c, &m);
+    }
+}
 
-PACKAGEANDCLASS_LIST : PACKAGEANDCLASS {$$=list_new();list_append($$, $1);}
-PACKAGEANDCLASS_LIST : PACKAGEANDCLASS_LIST ',' PACKAGEANDCLASS {$$=$1;list_append($$,$3);}
+//TODO: 
+//VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
+//VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
+//VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
 
-MAYBE_DECLARATION_LIST : 
-MAYBE_DECLARATION_LIST : DECLARATION_LIST
-DECLARATION_LIST : DECLARATION
-DECLARATION_LIST : DECLARATION_LIST DECLARATION
+// ----------------- 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 {$$=0;}
+NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;}
+NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;}
 
-// 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 {$$=0;}