fixed a security bug in logging, added basic xml support to as3 compiler
[swftools.git] / lib / as3 / parser.y
index f16d850..8342309 100644 (file)
@@ -34,6 +34,7 @@
 #include "opcodes.h"
 #include "compiler.h"
 #include "expr.h"
+#include "initcode.h"
 
 extern int a3_lex();
 
@@ -175,7 +176,7 @@ extern int a3_lex();
 
 %type <number_int> CONDITIONAL_COMPILATION
 %type <for_start> FOR_START
-%type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
+%type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER ID_OR_NS SUBNODE
 %type <namespace_decl>  NAMESPACE_ID
 %type <token> VARCONST
 %type <code> CODE
@@ -194,6 +195,7 @@ extern int a3_lex();
 %type <node> MAYBEEXPRESSION
 %type <value> DELETE
 %type <node> E COMMA_EXPRESSION
+%type <node> VAR_READ
 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY 
 %type <value> INNERFUNCTION
 %type <code> USE_NAMESPACE
@@ -211,17 +213,18 @@ extern int a3_lex();
 %type <classinfo_list> IMPLEMENTS_LIST
 %type <classinfo> EXTENDS CLASS_SPEC
 %type <classinfo_list> EXTENDS_LIST
-
 %type <classinfo> CLASS PACKAGEANDCLASS
 %type <classinfo_list> CLASS_SPEC_LIST
-
+%type <id> XML XML2 XMLNODE XMLATTRIBUTE XMLATTRIBUTES MAYBE_XMLATTRIBUTES XMLTEXT
 %type <classinfo> TYPE
 //%type <token> VARIABLE
-%type <value> VAR_READ MEMBER
+%type <value> MEMBER
 %type <value> NEW
 //%type <token> T_IDENTIFIER
 %type <value> FUNCTIONCALL
-%type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST WITH_HEAD
+%type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES 
+%type <value_list> MAYBE_DICT_EXPRPAIR_LIST DICT_EXPRPAIR_LIST WITH_HEAD
+%type <code> DICTLH
 
 // precedence: from low to high
 
@@ -240,6 +243,7 @@ extern int a3_lex();
 %nonassoc '&'
 %nonassoc "==" "!=" "===" "!=="
 %nonassoc "is" "as" "in"
+%left below_lt
 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
 %left "<<" ">>" ">>>" 
 %left below_minus
@@ -379,11 +383,17 @@ typedef struct _state {
     int switch_var;
     
     dict_t*vars;
+    dict_t*allvars; // also contains variables from sublevels
 } state_t;
 
 typedef struct _global {
     abc_file_t*file;
-    abc_script_t*init;
+
+    parsedclass_list_t*classes;
+    abc_script_t*classinit;
+
+    abc_script_t*init; //package-level code
+
     dict_t*token2info;
     dict_t*file2token2info;
 } global_t;
@@ -393,12 +403,6 @@ static state_t* state = 0;
 
 DECLARE_LIST(state);
 
-#define MULTINAME(m,x) \
-    multiname_t m;\
-    namespace_t m##_ns;\
-    (x)->package; \
-    registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
-                    
 #define MEMBER_MULTINAME(m,f,n) \
     multiname_t m;\
     namespace_t m##_ns;\
@@ -482,16 +486,15 @@ static void state_destroy(state_t*state)
         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;
     }
+    if(state->new_vars && state->allvars) {
+        parserassert(!state->old || state->old->allvars != state->allvars);
+        DICT_ITERATE_DATA(state->allvars, void*, data) {
+            free(data);
+        }
+        dict_destroy(state->allvars);
+    }
     
     list_free(state->active_namespace_urls)
     state->active_namespace_urls = 0;
@@ -537,6 +540,7 @@ void initialize_file(char*filename)
 
     new_state();
     state->package = internal_filename_package = strdup(filename);
+    state->allvars = dict_new();
     
     global->token2info = dict_lookup(global->file2token2info, 
                                      current_filename // use long version
@@ -567,10 +571,9 @@ void finish_file()
     
     if(as3_pass==2) {
         dict_del(global->file2token2info, current_filename);
-
         code_t*header = method_header(state->method);
         code_t*c = wrap_function(header, 0, global->init->method->body->code);
-        global->init->method->body->code = c;
+        global->init->method->body->code = abc_returnvoid(c);
         free(state->method);state->method=0;
     }
 
@@ -585,12 +588,16 @@ void initialize_parser()
     global->file->flags &= ~ABCFILE_LAZY;
     global->file2token2info = dict_new();
     global->token2info = 0;
+    global->classinit = abc_initscript(global->file);
 }
 
 void* finish_parser()
 {
     dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
     global->token2info=0;
+    
+    initcode_add_classlist(global->classinit, global->classes);
+
     return global->file;
 }
 
@@ -604,6 +611,7 @@ typedef struct _variable {
 
 static variable_t* find_variable(state_t*s, char*name)
 {
+    state_t*top = s;
     while(s) {
         variable_t*v = 0;
         v = dict_lookup(s->vars, name);
@@ -611,7 +619,7 @@ static variable_t* find_variable(state_t*s, char*name)
         if(s->new_vars) break;
         s = s->old;
     }
-    return 0;
+    return dict_lookup(top->allvars, name);
 }
 static variable_t* find_slot(state_t*s, const char*name)
 {
@@ -672,8 +680,10 @@ static variable_t* new_variable2(const char*name, classinfo_t*type, char init, c
     v->type = type;
     v->init = init;
  
-    if(name) 
+    if(name) {
         dict_put(state->vars, name, v);
+        dict_put(state->allvars, name, v);
+    }
 
     return v;
 }
@@ -1012,6 +1022,7 @@ static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, cl
         int num_interfaces = (list_length(implements));
         state->cls->info = classinfo_register(access, package, classname, num_interfaces);
         state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
+        state->cls->info->superclass = extends;
         
         int pos = 0;
         classinfo_list_t*l = implements;
@@ -1040,14 +1051,16 @@ static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, cl
             pos++;
         }
 
-        /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
-        state->cls->info->superclass = extends;
-
         /* generate the abc code for this class */
         MULTINAME(classname2,state->cls->info);
         multiname_t*extends2 = sig2mname(extends);
 
-        state->cls->abc = abc_class_new(global->file, &classname2, extends2);
+        /* don't add the class to the class index just yet- that will be done later
+           by initscript */
+        state->cls->abc = abc_class_new(0, &classname2, extends2);
+        state->cls->abc->file = global->file;
+
+        multiname_destroy(extends2);
         if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
         if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
         if(state->cls->info->flags&FLAG_INTERFACE) {
@@ -1061,58 +1074,12 @@ static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, cl
             abc_class_add_interface(state->cls->abc, &m);
         }
 
-        /* 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 {
-            __ 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);
+        NEW(parsedclass_t,p);
+        p->cls = state->cls->info;
+        p->abc = state->cls->abc;
+        list_append(global->classes, p);
 
         /* flash.display.MovieClip handling */
-
         if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
             if(state->package && state->package[0]) {
                 as3_globalclass = concat3(state->package, ".", classname);
@@ -1233,7 +1200,7 @@ static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, c
     if(!state->cls) {
         //package method
         minfo = methodinfo_register_global(ns.access, state->package, name);
-        minfo->return_type = 0; // save this for pass 2
+        minfo->return_type = return_type;
     } else if(getset != KW_GET && getset != KW_SET) {
         //class method
         memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
@@ -1241,7 +1208,7 @@ static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, c
             syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
         }
         minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
-        minfo->return_type = 0; // save this for pass 2 
+        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;
@@ -1258,7 +1225,7 @@ static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, c
         // not sure wether to look into superclasses here, too
         minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
         if(minfo) {
-            if(minfo->kind!=INFOTYPE_SLOT)
+            if(minfo->kind!=INFOTYPE_VAR)
                 syntaxerror("class already contains a method called '%s'", name);
             if(!(minfo->subtype & (SUBTYPE_GETSET)))
                 syntaxerror("class already contains a field called '%s'", name);
@@ -1279,10 +1246,11 @@ static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, c
             }*/
         } else {
             minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
-            minfo->kind = INFOTYPE_SLOT; //hack
+            minfo->kind = INFOTYPE_VAR; //hack
             minfo->subtype = gs;
-            minfo->return_type = 0;
+            minfo->return_type = type;
         }
+
         /* can't assign a slot as getter and setter might have different slots */
         //minfo->slot = slot;
     }
@@ -1305,6 +1273,7 @@ static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
 
     new_state();
     state->new_vars = 1;
+    state->allvars = dict_new();
    
     if(as3_pass == 1) {
         state->method = rfx_calloc(sizeof(methodstate_t));
@@ -1344,7 +1313,8 @@ static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
     }
     new_state();
     state->new_vars = 1;
-    
+    state->allvars = dict_new();
+
     if(as3_pass == 1) {
         state->method = rfx_calloc(sizeof(methodstate_t));
         state->method->has_super = 0;
@@ -1379,7 +1349,6 @@ static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
             state->cls->has_constructor |= state->method->is_constructor;
         }
         
-        state->method->info->return_type = return_type;
         function_initvars(state->method, params, mod->flags, 1);
     } 
 }
@@ -1439,6 +1408,8 @@ static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*
             }
             state->method->uses_slots = i;
             dict_destroy(state->vars);state->vars = 0;
+            parserassert(state->new_vars);
+            dict_destroy(state->allvars);state->allvars = 0;
         }
         old_state();
         return 0;
@@ -1459,8 +1430,13 @@ static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*
         } else if(state->method->is_constructor) {
             f = abc_class_getconstructor(state->cls->abc, type2);
         } else if(!state->method->is_global) {
-            namespace_t mname_ns = modifiers2access(mod);
-            multiname_t mname = {QNAME, &mname_ns, 0, name};
+            namespace_t ns = modifiers2access(mod);
+          
+            /* deal with protected */
+            if(ns.access == ACCESS_PROTECTED && state->cls)
+                ns.name = state->cls->info->name;
+
+            multiname_t mname = {QNAME, &ns, 0, name};
 
             if(mod->flags&FLAG_STATIC)
                 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
@@ -1908,7 +1884,9 @@ INPACKAGE_CODE: INTERFACE_DECLARATION
 MAYBECODE: CODE {$$=$1;}
 MAYBECODE: {$$=code_new();}
 
-CODE: CODE CODEPIECE {$$=code_append($1,$2);}
+CODE: CODE CODEPIECE {
+    $$=code_append($1,$2);
+}
 CODE: CODEPIECE {$$=$1;}
 
 // code which may appear outside of methods
@@ -1935,7 +1913,15 @@ CODEPIECE: BREAK
 CODEPIECE: CONTINUE
 CODEPIECE: RETURN
 CODEPIECE: THROW
-CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {PASS_ALWAYS as3_pass=$1;}
+CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {
+    PASS_ALWAYS 
+    if(as3_pass) {
+        $$ = $3;
+    } else {
+        $$ = 0;
+    }
+    as3_pass=$1;
+}
 
 //CODEBLOCK :  '{' CODE '}' {$$=$2;}
 //CODEBLOCK :  '{' '}'      {$$=0;}
@@ -2562,6 +2548,40 @@ IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LI
             parserassert(state->method);
         }
     }
+    static trait_t* add_abc_slot(modifiers_t* modifiers, const char*name, multiname_t*m, code_t***c)
+    {
+        int flags = modifiers->flags;
+        namespace_t ns = modifiers2access(modifiers);
+        /* deal with protected */
+        if(ns.access == ACCESS_PROTECTED && state->cls)
+            ns.name = state->cls->info->name;
+
+        /* slot name */
+        multiname_t mname = {QNAME, &ns, 0, name};
+      
+        trait_list_t**traits;
+        code_t**code=0;
+        if(!state->cls) {
+            // global variable
+            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->header;
+        } else {
+            // instance variable
+            traits = &state->cls->abc->traits;
+            code = &state->cls->init->header;
+        }
+        if(c)
+            *c = code;
+        if(m) 
+            memcpy(m, &mname, sizeof(multiname_t));
+            
+        return trait_new_member(traits, 0, multiname_clone(&mname), 0);
+    }
 };
 
 VARCONST: "var" | "const"
@@ -2606,40 +2626,27 @@ PASS12
     if(as3_pass == 2) {
         varinfo_t*info = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
 
-        /* slot name */
-        multiname_t mname = {QNAME, &ns, 0, $1};
-      
-        trait_list_t**traits;
+        multiname_t mname;
         code_t**code;
-        if(!state->cls) {
-            // global variable
-            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->header;
-        } else {
-            // instance variable
-            traits = &state->cls->abc->traits;
-            code = &state->cls->init->header;
-        }
-        
-        trait_t*t=0;
+        trait_t*t = add_abc_slot(slotstate_flags, $1, &mname, &code);
+
         if($2) {
             MULTINAME(m, $2);
-            t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
-        } else {
-            t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
+            t->type_name = multiname_clone(&m);
         }
         info->slot = t->slot_id;
+        
+        /* workaround for "VerifyError: Error #1053: Illegal override of ::test2 in C1" 
+           FIXME: is there a way to use slots and still don't have conflicting overrides?
+        */
+        info->slot = t->slot_id = 0;
        
         constant_t cval = $3->type->eval($3);
         if(cval.type!=CONSTANT_UNKNOWN) {
             /* compile time constant */
             t->value = malloc(sizeof(constant_t));
             memcpy(t->value, &cval, sizeof(constant_t));
+            info->value = constant_clone(t->value);
         } else {
             typedcode_t v = node_read($3);
             /* initalization code (if needed) */
@@ -2648,13 +2655,18 @@ PASS12
                 c = abc_getlocal_0(c);
                 c = code_append(c, v.c);
                 c = converttype(c, v.t, $2);
-                c = abc_setslot(c, t->slot_id);
+                if(!t->slot_id) {
+                    c = abc_setproperty2(c, &mname);
+                } else {
+                    c = abc_setslot(c, t->slot_id);
+                }
             }
             *code = code_append(*code, c);
         }
 
         if(slotstate_varconst==KW_CONST) {
             t->kind= TRAIT_CONST;
+            info->flags |= FLAG_CONST;
         }
     }
 
@@ -2664,7 +2676,13 @@ PASS12
 /* ------------ constants -------------------------------------- */
 
 MAYBECONSTANT: {$$=0;}
-MAYBECONSTANT: '=' CONSTANT {$$=$2;}
+MAYBECONSTANT: '=' E {
+  $$ = malloc(sizeof(constant_t));
+  *$$ = node_eval($2);
+  if($$->type == CONSTANT_UNKNOWN) {
+    syntaxerror("can't evaluate default parameter value (needs to be a compile-time constant)");
+  }
+}
 
 //CONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
 CONSTANT : T_INT {$$ = constant_new_int($1);}
@@ -2689,6 +2707,48 @@ CONSTANT : T_IDENTIFIER {
     }
 }*/
 
+/* ---------------------------xml ------------------------------ */
+
+%code {
+    static int xml_level = 0;
+};
+
+XML: XMLNODE
+
+OPEN : '<' {PASS_ALWAYS tokenizer_begin_xml();xml_level++;}
+CLOSE : '>' {PASS_ALWAYS tokenizer_begin_xmltext();}
+CLOSE2 : {PASS_ALWAYS if(!--xml_level) tokenizer_end_xml(); else tokenizer_begin_xmltext();}
+
+XMLTEXT : {$$="";}
+XMLTEXT : XMLTEXT T_STRING {$$=concat2($1, string_cstr(&$2));}
+XMLTEXT : XMLTEXT '>' {$$=concat2($1, ">");}
+
+XML2 : XMLNODE XMLTEXT {$$=concat2($1,$2);}
+XML2 : XML2 XMLNODE XMLTEXT {$$=concat3($1,$2,$3);free($1);free($2);free($3);}
+
+XMLNODE : OPEN T_IDENTIFIER MAYBE_XMLATTRIBUTES CLOSE XMLTEXT '<' '/' T_IDENTIFIER CLOSE2 '>' {
+    $$ = allocprintf("<%s%s>%s</%s>", $2, $3, $5, $8);
+    free($2);free($3);free($5);free($8);
+}
+XMLNODE : OPEN T_IDENTIFIER MAYBE_XMLATTRIBUTES '/' CLOSE2 '>' {
+    $$ = allocprintf("<%s%s/>", $2, $3);
+}
+XMLNODE : OPEN T_IDENTIFIER MAYBE_XMLATTRIBUTES CLOSE XMLTEXT XML2 '<' '/' T_IDENTIFIER CLOSE2 '>' {
+    $$ = allocprintf("<%s%s>%s%s</%s>", $2, $3, $5, $6, $9);
+    free($2);free($3);free($5);free($6);free($6);free($9);
+}
+
+MAYBE_XMLATTRIBUTES:                      {$$=strdup("");}
+MAYBE_XMLATTRIBUTES: XMLATTRIBUTES        {$$=concat2(" ",$1);}
+XMLATTRIBUTES: XMLATTRIBUTE               {$$=$1;}
+XMLATTRIBUTES: XMLATTRIBUTES XMLATTRIBUTE {$$=concat3($1," ",$2);free($1);free($2);}
+XMLATTRIBUTE: T_IDENTIFIER '=' T_STRING {
+    char* str = string_cstr(&$3);
+    $$=allocprintf("%s=\"%s\"", $1,str);
+    free(str);
+    free($1);free((char*)$3.str);
+}
+
 /* ------------ classes and interfaces (body, functions) ------- */
 
 // non-vararg version
@@ -2831,7 +2891,7 @@ CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3
 
 TYPE : CLASS_SPEC {PASS12 $$=$1;}
      | '*'        {PASS12 $$=TYPE_ANY;}
-     | "void"     {PASS12 $$=TYPE_ANY;}
+     | "void"     {PASS12 $$=TYPE_VOID;}
     /*
      |  "String"  {$$=registry_getstringclass();}
      |  "int"     {$$=registry_getintclass();}
@@ -2941,6 +3001,9 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
    
     if(TYPE_IS_FUNCTION(v.t) && v.t->data) {
         $$.t = ((methodinfo_t*)(v.t->data))->return_type;
+    } else if(TYPE_IS_CLASS(v.t) && v.t->data) {
+        // calling a class is like a typecast
+        $$.t = (classinfo_t*)v.t->data;
     } else {
         $$.c = abc_coerce_a($$.c);
         $$.t = TYPE_ANY;
@@ -3000,55 +3063,70 @@ RETURN: "return" EXPRESSION {
 
 // ----------------------- expression types -------------------------------------
 
-NONCOMMAEXPRESSION : E %prec below_minus {
+NONCOMMAEXPRESSION : E %prec below_lt {
     $$ = node_read($1);
 }
 EXPRESSION : COMMA_EXPRESSION {
     $$ = node_read($1);
 }
-COMMA_EXPRESSION : E %prec below_minus {
+COMMA_EXPRESSION : E %prec below_lt {
     $$ = mkmultinode(&node_comma, $1);
 }
-COMMA_EXPRESSION : COMMA_EXPRESSION ',' E %prec below_minus {
+COMMA_EXPRESSION : COMMA_EXPRESSION ',' E %prec below_lt {
     $$ = multinode_extend($1, $3);
 }
 VOIDEXPRESSION : E %prec below_minus { 
     $$ = node_exec($1); 
 }
-VOIDEXPRESSION : VOIDEXPRESSION ',' E %prec below_minus { 
+VOIDEXPRESSION : VOIDEXPRESSION ',' E %prec below_lt { 
     $$ = $1;
     $$ = code_append($$, node_exec($3)); 
 }
 
-MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
-MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
+MAYBE_DICT_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
+MAYBE_DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST {$$=$1;}
+
+DICTLH: T_IDENTIFIER {$$=abc_pushstring(0,$1);}
+DICTLH: T_STRING     {$$=abc_pushstring2(0,&$1);}
 
-EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
+DICT_EXPRPAIR_LIST : DICTLH ':' NONCOMMAEXPRESSION {
     $$.cc = 0;
-    $$.cc = code_append($$.cc, $1.c);
+    $$.cc = code_append($$.cc, $1);
     $$.cc = code_append($$.cc, $3.c);
     $$.number = 2;
 }
-EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
+DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST ',' DICTLH ':' NONCOMMAEXPRESSION {
     $$.cc = $1.cc;
     $$.number = $1.number+2;
-    $$.cc = code_append($$.cc, $3.c);
+    $$.cc = code_append($$.cc, $3);
     $$.cc = code_append($$.cc, $5.c);
 }
 
 // ----------------------- expression evaluation -------------------------------------
 
 E : INNERFUNCTION %prec prec_none {$$ = mkcodenode($1);}
-E : VAR_READ %prec T_IDENTIFIER   {$$ = mkcodenode($1);}
 E : MEMBER %prec '.'              {$$ = mkcodenode($1);}
 E : NEW                           {$$ = mkcodenode($1);}
 E : DELETE                        {$$ = mkcodenode($1);}
 E : FUNCTIONCALL                  {$$ = mkcodenode($1);}
+E : VAR_READ %prec T_IDENTIFIER   {$$ = $1;}
 
 E : CONSTANT { 
     $$ = mkconstnode($1);
 }
 
+E : XML {
+    typedcode_t v;
+    v.c = 0;
+    namespace_t ns = {ACCESS_PACKAGE, ""};
+    multiname_t m = {QNAME, &ns, 0, "XML"};
+    v.c = abc_getlex2(v.c, &m);
+    v.c = abc_pushstring(v.c, $1);
+    v.c = abc_construct(v.c, 1);
+    v.t = TYPE_XML;
+    $$ = mkcodenode(v);
+}
+
 /* regexp */
 E : T_REGEXP {
     typedcode_t v;
@@ -3080,7 +3158,7 @@ E : '[' MAYBE_EXPRESSION_LIST ']' {
 }
 
 /* dictionary */
-E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
+E : "{ (dictionary)" MAYBE_DICT_EXPRPAIR_LIST '}' {
     typedcode_t v;
     v.c = code_new();
     v.c = code_append(v.c, $2.cc);
@@ -3157,43 +3235,96 @@ E : "super" '.' T_IDENTIFIER
            }
 
 E : '@' T_IDENTIFIER {
-              // attribute TODO
-              $$ = mkdummynode();
-              as3_warning("ignored @ operator");
-           }
-
-E : E '.' '@' T_IDENTIFIER {
-              // child attribute  TODO
-              $$ = mkdummynode();
-              as3_warning("ignored .@ operator");
-           }
+    // attribute occuring in .() loops 
+    // TODO
+    $$ = mkdummynode();
+    as3_warning("ignored @ operator");
+}
 
-E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
-              // namespace declaration TODO
-              $$ = mkdummynode();
-              as3_warning("ignored :: operator");
-           }
+E : E '.' '(' E ')' {
+    // filter 
+    // TODO: this needs to be implemented using a loop
+    $$ = mkdummynode();
+    as3_warning("ignored .() operator");
+}
 
-E : E ".." T_IDENTIFIER {
-              // descendants TODO
-              $$ = mkdummynode();
-              as3_warning("ignored .. operator");
-           }
+ID_OR_NS : T_IDENTIFIER {$$=$1;}
+ID_OR_NS : T_NAMESPACE {$$=(char*)$1;}
+SUBNODE: T_IDENTIFIER
+       | '*' {$$="*";}
 
-E : E '.' '(' E ')' {
-              // filter TODO
-              $$ = mkdummynode();
-              as3_warning("ignored .() operator");
-           }
+E : E '.' ID_OR_NS "::" SUBNODE {
+    typedcode_t v = node_read($1);
+    typedcode_t w = node_read(resolve_identifier($3));
+    v.c = code_append(v.c, w.c);
+    if(!TYPE_IS_NAMESPACE(w.t)) {
+        as3_softwarning("%s might not be a namespace", $3);
+    }
+    v.c = converttype(v.c, w.t, TYPE_NAMESPACE);
+    multiname_t m = {RTQNAME, 0, 0, $5};
+    v.c = abc_getproperty2(v.c, &m);
+    if(TYPE_IS_XML(v.t)) {
+        v.t = TYPE_XMLLIST;
+    } else {
+        v.c = abc_coerce_a(v.c);
+        v.t = TYPE_ANY;
+    }
+    $$ = mkcodenode(v);
+}
+E : E ".." SUBNODE {
+    typedcode_t v = node_read($1);
+    multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
+    v.c = abc_getdescendants2(v.c, &m);
+    v.t = TYPE_XMLLIST;
+    $$ = mkcodenode(v);
+}
+E : E '.' '[' E ']' {
+    typedcode_t v = node_read($1);
+    typedcode_t w = node_read($4);
+    multiname_t m = {MULTINAMEL, 0, &nopackage_namespace_set, 0};
+    v.c = code_append(v.c, w.c);
+    v.c = converttype(w.c, w.t, TYPE_STRING);
+    v.c = abc_getproperty2(v.c, &m);
+    v.t = TYPE_XMLLIST;
+    $$ = mkcodenode(v);
+}
 
-//E : E "::" '[' E ']' {
-//              // qualified expression TODO
-//              $$.c = abc_pushundefined(0);
-//              $$.t = 0;
-//              as3_warning("ignored ::[] operator");
-//           }
+E : E '.' '@' SUBNODE {
+    typedcode_t v = node_read($1);
+    multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $4};
+    v.c = abc_getproperty2(v.c, &m);
+    v.t = TYPE_STRING;
+    $$ = mkcodenode(v);
+}
+E : E ".." '@' SUBNODE {
+    typedcode_t v = node_read($1);
+    multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $4};
+    v.c = abc_getdescendants2(v.c, &m);
+    v.t = TYPE_STRING;
+    $$ = mkcodenode(v);
+}
+E : E '.' '@' '[' E ']' {
+    typedcode_t v = node_read($1);
+    typedcode_t w = node_read($5);
+    multiname_t m = {MULTINAMELA, 0, &nopackage_namespace_set, 0};
+    v.c = code_append(v.c, w.c);
+    v.c = converttype(w.c, w.t, TYPE_STRING);
+    v.c = abc_getproperty2(v.c, &m);
+    v.t = TYPE_STRING;
+    $$ = mkcodenode(v);
+}
+E : E ".." '@' '[' E ']' {
+    typedcode_t v = node_read($1);
+    typedcode_t w = node_read($5);
+    multiname_t m = {MULTINAMELA, 0, &nopackage_namespace_set, 0};
+    v.c = code_append(v.c, w.c);
+    v.c = converttype(w.c, w.t, TYPE_STRING);
+    v.c = abc_getdescendants2(v.c, &m);
+    v.t = TYPE_STRING;
+    $$ = mkcodenode(v);
+}
 
-MEMBER : E '.' T_IDENTIFIER {
+MEMBER : E '.' SUBNODE {
     typedcode_t v1 = node_read($1);
     $$.c = v1.c;
     classinfo_t*t = v1.t;
@@ -3202,7 +3333,12 @@ MEMBER : E '.' T_IDENTIFIER {
         t = t->data;
         is_static = 1;
     }
-    if(t) {
+    if(TYPE_IS_XML(t)) {
+        multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
+        $$.c = abc_getproperty2($$.c, &m);
+        $$.c = abc_coerce_a($$.c);
+        $$.t = TYPE_XMLLIST;
+    } else if(t) {
         if(t->subtype==INFOTYPE_UNRESOLVED) {
             syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
         }
@@ -3214,9 +3350,8 @@ MEMBER : E '.' T_IDENTIFIER {
             $$.c = abc_getslot($$.c, f->slot);
         } else {
             if(!f) {
-                as3_warning("Access of undefined property '%s' in %s", $3, t->name);
+                as3_softwarning("Access of undefined property '%s' in %s", $3, t->name);
             }
-            
             MEMBER_MULTINAME(m, f, $3);
             $$.c = abc_getproperty2($$.c, &m);
         }
@@ -3224,6 +3359,7 @@ MEMBER : E '.' T_IDENTIFIER {
         $$.t = slotinfo_gettype((slotinfo_t*)f);
         if(!$$.t)
            $$.c = abc_coerce_a($$.c);
+        
     } else if(v1.c && v1.c->opcode == OPCODE___PUSHPACKAGE__) {
         string_t*package = v1.c->data[0];
         char*package2 = concat3(package->str, ".", $3);
@@ -3251,6 +3387,113 @@ MEMBER : E '.' T_IDENTIFIER {
     }
 }
 
+%code {
+    node_t* resolve_identifier(char*name)
+    {
+        typedcode_t o;
+        o.t = 0;
+        o.c = 0;
+
+        slotinfo_t*a = 0;
+        memberinfo_t*f = 0;
+
+        variable_t*v;
+        /* look at variables */
+        if((v = find_variable(state, name))) {
+            // name is a local variable
+            o.c = abc_getlocal(o.c, v->index);
+            o.t = v->type;
+            return mkcodenode(o);
+        }
+        if((v = find_slot(state, name))) {
+            o.c = abc_getscopeobject(o.c, 1);
+            o.c = abc_getslot(o.c, v->index);
+            o.t = v->type;
+            return mkcodenode(o);
+        }
+
+        int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
+
+        /* look at current class' members */
+        if(!state->method->inner && 
+            state->cls && 
+            (f = findmember_nsset(state->cls->info, name, 1)))
+        {
+            // name is a member or attribute in this class
+            int var_is_static = (f->flags&FLAG_STATIC);
+
+            if(f->kind == INFOTYPE_VAR && (f->flags&FLAG_CONST)) {
+                /* if the variable is a constant (and we know what is evaluates to), we
+                   can just use the value itself */
+                varinfo_t*v = (varinfo_t*)f;
+                if(v->value) {
+                    return mkconstnode(v->value);
+                }
+            }
+           
+            if(var_is_static >= i_am_static) {
+                if(f->kind == INFOTYPE_METHOD) {
+                    o.t = TYPE_FUNCTION(f);
+                } else {
+                    o.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;
+                    o.t = f->type;
+                    namespace_t ns = {f->access, f->package};
+                    multiname_t m = {QNAME, &ns, 0, name};
+                    o.c = abc_findpropstrict2(o.c, &m);
+                    o.c = abc_getproperty2(o.c, &m);
+                    return mkcodenode(o);
+                } else if(f->slot>0) {
+                    o.c = abc_getlocal_0(o.c);
+                    o.c = abc_getslot(o.c, f->slot);
+                    return mkcodenode(o);
+                } else {
+                    namespace_t ns = {f->access, f->package};
+                    multiname_t m = {QNAME, &ns, 0, name};
+                    o.c = abc_getlocal_0(o.c);
+                    o.c = abc_getproperty2(o.c, &m);
+                    return mkcodenode(o);
+                }
+            }
+        } 
+        
+        /* look at actual classes, in the current package and imported */
+        if((a = find_class(name))) {
+            o = push_class(a);
+            return mkcodenode(o);
+        }
+
+        /* look through package prefixes */
+        if(dict_contains(state->import_toplevel_packages, name) || 
+           registry_ispackage(name)) {
+            o.c = abc___pushpackage__(o.c, name);
+            o.t = 0;
+            return mkcodenode(o); //?
+        }
+
+        /* unknown object, let the avm2 resolve it */
+        if(1) {
+            //as3_softwarning("Couldn't resolve '%s', doing late binding", name);
+            as3_warning("Couldn't resolve '%s', doing late binding", name);
+            state->method->late_binding = 1;
+                    
+            multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, name};
+
+            o.t = 0;
+            o.c = abc_findpropstrict2(o.c, &m);
+            o.c = abc_getproperty2(o.c, &m);
+            return mkcodenode(o);
+        }
+    }
+};
+
 VAR_READ : T_IDENTIFIER {
     PASS1
     /* Queue unresolved identifiers for checking against the parent
@@ -3260,106 +3503,32 @@ VAR_READ : T_IDENTIFIER {
        etc. which is *correct* because local variables of the parent function
        would shadow those.
        */
-    if(state->method->inner && !find_variable(state, $1)) {
-        unknown_variable($1);
+    if(!find_variable(state, $1)) {
+        if(state->method->inner) {
+            unknown_variable($1);
+        }
+        /* let the compiler know that it might want to check the current directory/package
+           for this identifier- maybe there's a file $1.as defining $1. */
+        as3_schedule_class_noerror(state->package, $1);
     }
    
-    /* let the compiler know that it might want to check the current directory/package
-       for this identifier- maybe there's a file $1.as defining $1. */
-    as3_schedule_class_noerror(state->package, $1);
+    $$ = 0;
     PASS2
 
-    $$.t = 0;
-    $$.c = 0;
-    slotinfo_t*a = 0;
-    memberinfo_t*f = 0;
-
-    variable_t*v;
-    /* look at variables */
-    if((v = find_variable(state, $1))) {
-        // $1 is a local variable
-        $$.c = abc_getlocal($$.c, v->index);
-        $$.t = v->type;
-        break;
-    }
-    if((v = find_slot(state, $1))) {
-        $$.c = abc_getscopeobject($$.c, 1);
-        $$.c = abc_getslot($$.c, v->index);
-        $$.t = v->type;
-        break;
-    }
-
-    int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
-
-    /* look at current class' members */
-    if(!state->method->inner && 
-        state->cls && 
-        (f = findmember_nsset(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);
-
-        if(f->kind == INFOTYPE_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 = {f->access, f->package};
-            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 = {f->access, f->package};
-            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))) {
-        $$ = push_class(a);
-        break;
-    }
-
-    /* look through package prefixes */
-    if(dict_contains(state->import_toplevel_packages, $1) || 
-       registry_ispackage($1)) {
-        $$.c = abc___pushpackage__($$.c, $1);
-        $$.t = 0;
-        break;
-    }
-
-    /* unknown object, let the avm2 resolve it */
-    if(1) {
-        //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
-        as3_warning("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);
-    }
+    $$ = resolve_identifier($1);
 }
 
 // ----------------- namespaces -------------------------------------------------
 
+%code {
+    void add_active_url(const char*url)
+    {
+        NEW(namespace_t,n);
+        n->name = url;
+        list_append(state->active_namespace_urls, n);
+    }
+};
+
 NAMESPACE_ID : "namespace" T_IDENTIFIER {
     PASS12
     NEW(namespace_decl_t,n);
@@ -3392,19 +3561,17 @@ NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
     ns.access = ACCESS_NAMESPACE;
     ns.name = $2->url;
     var->value = constant_new_namespace(&ns);
+      
+    if(as3_pass==2) {
+        MULTINAME(m, TYPE_NAMESPACE);
+        trait_t*t = add_abc_slot(&$1, $2->name, 0, 0);
+        t->value = var->value;
+        t->type_name = multiname_clone(&m);
+    }
 
     $$=0;
 }
 
-%code {
-    void add_active_url(const char*url)
-    {
-        NEW(namespace_t,n);
-        n->name = url;
-        list_append(state->active_namespace_urls, n);
-    }
-};
-
 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
     PASS12
     const char*url = $3->name;
@@ -3416,7 +3583,7 @@ USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
             syntaxerror("Couldn't resolve namespace %s", $3->name);
     }
 
-    if(!s || s->kind != INFOTYPE_SLOT)
+    if(!s || s->kind != INFOTYPE_VAR)
         syntaxerror("%s.%s is not a public namespace (%d)", $3->package, $3->name, s?s->kind:-1);
     if(!s->value || !NS_TYPE(s->value->type))
         syntaxerror("%s.%s is not a namespace", $3->package, $3->name);