X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=8342309c76c7921055609bb4f8370ceb9af3dd44;hb=e4687b3aa2aed49fb16ba9e9561344d808750297;hp=f16d85035e9e627cdf9be95848e61c7b9cef1cb4;hpb=3dea7306ae2c9590673c10dec1fbabcd6d7e1806;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index f16d850..8342309 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -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 CONDITIONAL_COMPILATION %type FOR_START -%type X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER +%type X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER ID_OR_NS SUBNODE %type NAMESPACE_ID %type VARCONST %type CODE @@ -194,6 +195,7 @@ extern int a3_lex(); %type MAYBEEXPRESSION %type DELETE %type E COMMA_EXPRESSION +%type VAR_READ %type FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY %type INNERFUNCTION %type USE_NAMESPACE @@ -211,17 +213,18 @@ extern int a3_lex(); %type IMPLEMENTS_LIST %type EXTENDS CLASS_SPEC %type EXTENDS_LIST - %type CLASS PACKAGEANDCLASS %type CLASS_SPEC_LIST - +%type XML XML2 XMLNODE XMLATTRIBUTE XMLATTRIBUTES MAYBE_XMLATTRIBUTES XMLTEXT %type TYPE //%type VARIABLE -%type VAR_READ MEMBER +%type MEMBER %type NEW //%type T_IDENTIFIER %type FUNCTIONCALL -%type MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST WITH_HEAD +%type MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES +%type MAYBE_DICT_EXPRPAIR_LIST DICT_EXPRPAIR_LIST WITH_HEAD +%type 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;tvars->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 (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", $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", $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);