+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" '{' {PASS12 new_state();
+ state->method->has_exceptions=1;
+ state->method->late_binding=1;//for invariant scope_code
+ } 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 = alloc_local();
+
+ 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($$);
+ $$ = code_append($$, code_dup(state->method->scope_code));
+ $$ = 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($$, state->vars);
+ PASS12 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, state->exception_name);
+ $$=code_new();
+ $$=abc_getlocal($$, v->index);
+ $$=abc_throw($$);
+}
+
+/* ------------ with -------------------------------- */
+
+WITH_HEAD : "with" '(' EXPRESSION ')' {
+ new_state();
+ if(state->method->has_exceptions) {
+ int v = alloc_local();
+ state->method->scope_code = abc_getlocal(state->method->scope_code, v);
+ state->method->scope_code = abc_pushwith(state->method->scope_code);
+ $$.number = v;
+ }
+ $$.cc = $3.c;
+}
+WITH : WITH_HEAD CODEBLOCK {
+ /* remove getlocal;pushwith from scope code again */
+ state->method->scope_code = code_cutlast(code_cutlast(state->method->scope_code));
+
+ $$ = $1.cc;
+ if(state->method->has_exceptions) {
+ $$ = abc_dup($$);
+ $$ = abc_setlocal($$, $1.number);
+ }
+ $$ = abc_pushwith($$);
+ $$ = code_append($$, $2);
+ $$ = abc_popscope($$);
+ old_state();
+}
+
+/* ------------ packages and imports ---------------- */
+
+X_IDENTIFIER: T_IDENTIFIER
+ | "package" {PASS12 $$="package";}
+ | "namespace" {PASS12 $$="namespace";}
+ | "NaN" {PASS12 $$="NaN";}
+ | T_NAMESPACE {PASS12 $$=$1;}
+
+PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
+PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
+
+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;}
+
+%code {
+ 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 import_toplevel(const char*package)
+ {
+ char* s = strdup(package);
+ while(1) {
+ dict_put(state->import_toplevel_packages, s, 0);
+ char*x = strrchr(s, '.');
+ if(!x)
+ break;
+ *x = 0;
+ }
+ free(s);
+ }
+};
+
+IMPORT : "import" T_IDENTIFIER {
+ PASS12
+ slotinfo_t*s = registry_find(state->package, $2);
+ if(!s && as3_pass==1) {as3_schedule_class(state->package, $2);}
+ state_has_imports();
+ dict_put(state->imports, state->package, $2);
+ $$=0;
+}
+IMPORT : "import" PACKAGEANDCLASS {
+ PASS12
+ slotinfo_t*s = registry_find($2->package, $2->name);
+ if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
+ as3_schedule_class($2->package, $2->name);
+ }
+ state_has_imports();
+ dict_put(state->imports, $2->name, $2);
+ import_toplevel($2->package);
+ $$=0;
+}
+IMPORT : "import" PACKAGE '.' '*' {
+ PASS12
+ if(strncmp("flash.", $2, 6) && as3_pass==1) {
+ as3_schedule_package($2);
+ }
+
+ NEW(import_t,i);
+ i->package = $2;
+ state_has_imports();
+ list_append(state->wildcard_imports, i);
+ import_toplevel(i->package);
+ $$=0;
+}
+
+/* ------------ classes and interfaces (header) -------------- */
+
+MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
+MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
+MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
+MODIFIER_LIST : MODIFIER_LIST MODIFIER {
+ PASS12
+ $$.flags=$1.flags|$2.flags;
+ if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
+ $$.ns=$1.ns?$1.ns:$2.ns;
+
+}
+MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
+ | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
+ | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
+ | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
+ | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
+ | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
+ | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
+ | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
+ | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
+ | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
+ $$.ns=$1;
+ }
+
+EXTENDS : {PASS12 $$=0;}
+EXTENDS : KW_EXTENDS CLASS_SPEC {PASS12 $$=$2;}
+
+EXTENDS_LIST : {PASS12 $$=list_new();}
+EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
+
+IMPLEMENTS_LIST : {PASS12 $$=list_new();}
+IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
+
+CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
+ EXTENDS IMPLEMENTS_LIST
+ '{' {PASS12 startclass(&$1,$3,$4,$5);}
+ MAYBE_CLASS_BODY
+ '}' {PASS12 endclass();$$=0;}
+
+INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
+ EXTENDS_LIST
+ '{' {PASS12 $1.flags|=FLAG_INTERFACE;
+ startclass(&$1,$3,0,$4);}
+ 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 : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}' {PASS_ALWAYS as3_pass=$1;}
+CLASS_BODY_ITEM : SLOT_DECLARATION
+CLASS_BODY_ITEM : FUNCTION_DECLARATION
+
+CLASS_BODY_ITEM : CODE_STATEMENT {
+ code_t*c = state->cls->static_init->header;
+ c = code_append(c, $1);
+ state->cls->static_init->header = 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.flags |= FLAG_PUBLIC;
+ if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
+ syntaxerror("invalid method modifiers: interface methods always need to be public");
+ }
+ startfunction(&$1,$3,$4,&$6,$8);
+ endfunction(&$1,$3,$4,&$6,$8, 0);
+ list_deep_free($6.list);
+}
+
+/* ------------ classes and interfaces (body, slots ) ------- */
+
+%code {
+ static int slotstate_varconst = 0;
+ static modifiers_t*slotstate_flags = 0;
+ static void setslotstate(modifiers_t* flags, int varconst)
+ {
+ slotstate_varconst = varconst;
+ slotstate_flags = flags;
+ if(state->cls) {
+ if(flags) {
+ if(flags->flags&FLAG_STATIC) {
+ state->method = state->cls->static_init;
+ } else {
+ state->method = state->cls->init;
+ }
+ } else {
+ // reset to "default" state (all in class code is static by default) */
+ state->method = state->cls->static_init;
+ }
+ } else {
+ 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);
+
+ /* slot name */
+ multiname_t mname = {QNAME, &ns, 0, name};
+
+ trait_list_t**traits;
+ code_t**code=0;
+ if(!state->cls) {
+ // global variable
+ if(!global->init) global->init = abc_initscript(global->file);
+ 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(ns.access == ACCESS_PROTECTED) {
+ ns.name = concat3(state->cls->info->package,":",state->cls->info->name);
+ }
+ }
+ if(c)
+ *c = code;
+ if(m)
+ *m = *multiname_clone(&mname);
+
+ return trait_new_member(traits, 0, multiname_clone(&mname), 0);
+ }
+};
+
+VARCONST: "var" | "const"
+
+SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {PASS12 setslotstate(&$1,$2);} SLOT_LIST {PASS12 $$=$4;setslotstate(0, 0);}
+
+SLOT_LIST: ONE_SLOT {PASS12 $$=0;}
+SLOT_LIST: SLOT_LIST ',' ONE_SLOT {PASS12 $$=0;}
+
+ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
+{
+PASS12
+ int flags = slotstate_flags->flags;
+ namespace_t ns = modifiers2access(slotstate_flags);
+
+ if(as3_pass == 1) {
+
+ varinfo_t* info = 0;
+ if(state->cls) {
+ memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
+ if(i) {
+ check_override(i, flags);
+ }
+ info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
+ } else {
+ slotinfo_t*i = registry_find(state->package, $1);
+ if(i) {
+ syntaxerror("package %s already contains '%s'", state->package, $1);
+ }
+ if(ns.name && ns.name[0]) {
+ syntaxerror("namespaces not allowed on package-level variables");
+ }
+ info = varinfo_register_global(ns.access, state->package, $1);
+ }
+
+ info->type = $2;
+ info->flags = flags;
+
+ dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, info);
+ }
+
+ if(as3_pass == 2) {
+ varinfo_t*info = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
+
+ multiname_t mname;
+ code_t**code;
+ trait_t*t = add_abc_slot(slotstate_flags, $1, &mname, &code);
+
+ if($2) {
+ MULTINAME(m, $2);
+ 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) */
+ code_t*c = 0;
+ if(v.c && !is_pushundefined(v.c)) {
+ c = abc_getlocal_0(c);
+ c = code_append(c, v.c);
+ c = converttype(c, v.t, $2);
+ if(!t->slot_id) {
+ c = abc_initproperty2(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;
+ }
+ }
+
+ $$=0;
+}
+
+/* ------------ constants -------------------------------------- */
+
+MAYBECONSTANT: {$$=0;}
+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);}
+CONSTANT : T_UINT {
+ $$ = constant_new_uint($1);
+}
+CONSTANT : T_FLOAT {$$ = constant_new_float($1);}
+CONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
+CONSTANT : "true" {$$ = constant_new_true($1);}
+CONSTANT : "false" {$$ = constant_new_false($1);}
+CONSTANT : "null" {$$ = constant_new_null($1);}
+CONSTANT : "undefined" {$$ = constant_new_undefined($1);}
+CONSTANT : KW_NAN {$$ = constant_new_float(__builtin_nan(""));}
+
+/*CONSTANT : T_NAMESPACE {
+ // TODO
+ $$ = constant_new_namespace(namespace_new_namespace($1.url));
+}*/
+
+/* ---------------------------xml ------------------------------ */
+
+%code {
+ static int xml_level = 0;
+};
+
+XML: XMLNODE {
+ multiname_t m = {QNAME, &stdns, 0, "XML"};
+ typedcode_t v;
+ v.c = 0;
+ v.c = abc_getlex2(v.c, &m);
+ v.c = code_append(v.c, node_read($1).c);
+ v.c = abc_construct(v.c, 1);
+ v.t = TYPE_XML;
+ $$ = mkcodenode(v);
+}
+
+OPEN : '<' {PASS_ALWAYS if(!xml_level++) tokenizer_begin_xml();}
+CLOSE : '>' {PASS_ALWAYS tokenizer_begin_xmltext();}
+CLOSE2 : {PASS_ALWAYS if(!--xml_level) tokenizer_end_xml(); else tokenizer_begin_xmltext();}
+
+XMLEXPR1 : '{' E {PASS_ALWAYS tokenizer_begin_xmltext();} '}' {
+ $$ = $2;
+}
+XMLEXPR2 : '{' E {PASS_ALWAYS tokenizer_begin_xml();} '}' {
+ $$ = $2;
+}
+XMLTEXT : {$$=mkstringnode("");}
+XMLTEXT : XMLTEXT XMLEXPR1 {
+ $$ = mkaddnode($1,$2);
+}
+XMLTEXT : XMLTEXT T_STRING {
+ char* str = string_cstr(&$2);
+ $$ = mkaddnode($1,mkstringnode(str));
+ free(str);
+}
+XMLTEXT : XMLTEXT '>' {
+ $$ = mkaddnode($1, mkstringnode(">"));
+}
+XML2 : XMLNODE XMLTEXT {
+ $$ = mkaddnode($1,$2);
+}
+XML2 : XML2 XMLNODE XMLTEXT {
+ $$ = mkaddnode($1, mkaddnode($2,$3));
+}
+XML_ID_OR_EXPR: T_IDENTIFIER {
+ $$ = mkstringnode($1);
+}
+XML_ID_OR_EXPR: XMLEXPR2 {
+ $$ = $1;
+}
+
+MAYBE_XMLATTRIBUTES: {
+ $$ = mkstringnode("");
+}
+MAYBE_XMLATTRIBUTES: XMLATTRIBUTES {
+ $$ = mkaddnode(mkstringnode(" "),$1);
+}
+
+XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES '/' CLOSE2 '>' {
+ //$$ = allocprintf("<%s%s/>", $2, $3, $5, $8);
+ $$ = mkaddnode(mkaddnode(mkaddnode(mkstringnode("<"),$2),$3),mkstringnode("/>"));
+}
+XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES CLOSE XMLTEXT '<' '/' XML_ID_OR_EXPR CLOSE2 '>' {
+ //$$ = allocprintf("<%s%s>%s</%s>", $2, $3, $5, $8);
+ $$ = mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(
+ mkstringnode("<"),$2),$3),mkstringnode(">")),$5),mkstringnode("</")),$8),mkstringnode(">"));
+}
+XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES CLOSE XMLTEXT XML2 '<' '/' XML_ID_OR_EXPR CLOSE2 '>' {
+ //$$ = allocprintf("<%s%s>%s%s</%s>", $2, $3, $5, $6, $9);
+ $$ = mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(
+ mkstringnode("<"),$2),$3),mkstringnode(">")),$5),$6),mkstringnode("</")),$9),mkstringnode(">"));
+}
+
+XMLATTRIBUTES: XMLATTRIBUTE {
+ $$ = $1;
+}
+XMLATTRIBUTES: XMLATTRIBUTES XMLATTRIBUTE {
+ $$ = mkaddnode($1, mkaddnode(mkstringnode(" "),$2));
+}
+XMLATTRIBUTE: XMLEXPR2 {
+ $$ = $1;
+}
+XMLATTRIBUTE: XMLEXPR2 '=' T_STRING {
+ char* str = string_cstr(&$3);
+ $$ = mkaddnode($1, mkstringnode(concat2("=",str)));
+ free(str);
+}
+XMLATTRIBUTE: XMLEXPR2 '=' XMLEXPR2 {
+ $$ = mkaddnode($1, mkaddnode(mkstringnode("=\""), mkaddnode($3, mkstringnode("\""))));
+}
+XMLATTRIBUTE: T_IDENTIFIER '=' XMLEXPR2 {
+ $$ = mkaddnode(mkaddnode(mkstringnode(concat2($1,"=\"")), $3), mkstringnode("\""));
+}
+XMLATTRIBUTE: T_IDENTIFIER '=' T_STRING {
+ char* str = string_cstr(&$3);
+ $$=mkstringnode(allocprintf("%s=%s", $1,str));
+ free(str);
+ free($1);free((char*)$3.str);
+}
+
+/* ------------ classes and interfaces (body, functions) ------- */
+
+// non-vararg version
+MAYBE_PARAM_LIST: {
+ PASS12
+ memset(&$$,0,sizeof($$));
+}
+MAYBE_PARAM_LIST: PARAM_LIST {
+ PASS12
+ $$=$1;
+}
+
+// vararg version
+MAYBE_PARAM_LIST: "..." PARAM {
+ PASS12
+ memset(&$$,0,sizeof($$));
+ $$.varargs=1;
+ list_append($$.list, $2);
+}
+MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
+ PASS12
+ $$ =$1;
+ $$.varargs=1;
+ list_append($$.list, $4);
+}
+
+// non empty
+PARAM_LIST: PARAM_LIST ',' PARAM {
+ PASS12
+ $$ = $1;
+ list_append($$.list, $3);
+}
+PARAM_LIST: PARAM {
+ PASS12
+ memset(&$$,0,sizeof($$));
+ list_append($$.list, $1);
+}
+
+PARAM: T_IDENTIFIER ':' TYPE MAYBECONSTANT {
+ PASS12
+ $$ = rfx_calloc(sizeof(param_t));
+ $$->name=$1;
+ $$->type = $3;
+ PASS2
+ $$->value = $4;
+}
+PARAM: T_IDENTIFIER MAYBECONSTANT {
+ PASS12
+ $$ = rfx_calloc(sizeof(param_t));
+ $$->name=$1;
+ $$->type = TYPE_ANY;
+ PASS2
+ $$->value = $2;
+}
+GETSET : "get"
+ | "set"
+ | {PASS12 $$=0;}
+
+FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
+ MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
+{
+ PASS1
+ endfunction(&$1,$3,$4,&$6,0,0);
+ PASS2
+ if(!state->method->info) syntaxerror("internal error");
+
+ code_t*c = method_header(state->method);
+ c = wrap_function(c, 0, $11);
+
+ endfunction(&$1,$3,$4,&$6,$8,c);
+ PASS12
+ list_deep_free($6.list);
+ $$=0;
+}
+
+MAYBE_IDENTIFIER: T_IDENTIFIER
+MAYBE_IDENTIFIER: {PASS12 $$=0;}
+INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
+ '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
+{
+ PASS1
+ endfunction(0,0,$2,&$4,0,0);
+ PASS2
+ methodinfo_t*f = state->method->info;
+ if(!f || !f->kind) syntaxerror("internal error");
+
+ code_t*c = method_header(state->method);
+ c = wrap_function(c, 0, $9);
+
+ int index = state->method->var_index;
+ endfunction(0,0,$2,&$4,$6,c);
+
+ $$.c = abc_getlocal(0, index);
+ $$.t = TYPE_FUNCTION(f);
+
+ PASS12 list_deep_free($4.list);
+}
+
+
+/* ------------- package + class ids --------------- */
+
+CLASS: X_IDENTIFIER {
+ PASS1 NEW(unresolvedinfo_t,c);
+ memset(c, 0, sizeof(*c));
+ c->kind = INFOTYPE_UNRESOLVED;
+ c->name = $1;
+ c->package = get_package_from_name($1);
+ if(!c->package) {
+ c->nsset = get_current_imports();
+ /* make the compiler look for this class in the current directory,
+ just in case: */
+ as3_schedule_class_noerror(state->package, $1);
+ }
+ $$ = (classinfo_t*)c;
+ PASS2
+ slotinfo_t*s = find_class($1);
+ if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
+ $$ = (classinfo_t*)s;
+}
+
+PACKAGEANDCLASS : PACKAGE '.' X_IDENTIFIER {
+ PASS1 NEW(unresolvedinfo_t,c);
+ memset(c, 0, sizeof(*c));
+ c->kind = INFOTYPE_UNRESOLVED;
+ c->package = $1;
+ c->name = $3;
+ $$ = (classinfo_t*)c;
+ PASS2
+ slotinfo_t*s = registry_find($1, $3);
+ if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
+ free($1);$1=0;
+ $$ = (classinfo_t*)s;
+}
+
+CLASS_SPEC: PACKAGEANDCLASS
+ | CLASS
+
+CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
+CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
+
+TYPE : CLASS_SPEC {PASS12 $$=$1;}
+ | '*' {PASS12 $$=TYPE_ANY;}
+ | "void" {PASS12 $$=TYPE_VOID;}
+ /*
+ | "String" {$$=registry_getstringclass();}
+ | "int" {$$=registry_getintclass();}
+ | "uint" {$$=registry_getuintclass();}
+ | "Boolean" {$$=registry_getbooleanclass();}
+ | "Number" {$$=registry_getnumberclass();}
+ */
+
+MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
+MAYBETYPE: {PASS12 $$=0;}
+
+/* ----------function calls, delete, constructor calls ------ */
+
+MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.number=0;}
+MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
+
+MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.number=0;}
+MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
+MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
+
+EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.number=1;
+ $$.cc = $1.c;
+ }
+
+EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
+EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
+ $$.number= $1.number+1;
+ $$.cc = code_append($1.cc, $2.c);
+ }
+
+XX : %prec new2
+NEW : "new" E XX MAYBE_PARAM_VALUES {
+ typedcode_t v = node_read($2);
+ $$.c = v.c;
+ if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
+
+ code_t*paramcode = $4.cc;
+ 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_constructprop2($$.c, name, $4.number);
+ multiname_destroy(name);
+ } else if(is_getlocal($$.c)) {
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_construct($$.c, $4.number);
+ } else if(TYPE_IS_CLASS(v.t) && v.t->data) {
+ code_free($$.c);
+ classinfo_t*c = v.t->data;
+ MULTINAME(m, c);
+ $$.c = abc_findpropstrict2(0, &m);
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_constructprop2($$.c, &m, $4.number);
+ /*} else if($$.c->opcode == OPCODE_GETSLOT) {
+ int slot = (int)(ptroff_t)$$.c->data[0];
+ trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
+ multiname_t*name = t->name;
+ $$.c = code_cutlast($$.c);
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_constructprop2($$.c, name, $4.number);*/
+ } else {
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_construct($$.c, $4.number);
+ }
+
+ $$.t = TYPE_ANY;
+ if(TYPE_IS_CLASS(v.t) && v.t->data) {
+ $$.t = v.t->data;
+ } else {
+ $$.c = abc_coerce_a($$.c);
+ $$.t = TYPE_ANY;
+ }
+}
+
+/* TODO: use abc_call (for calling local variables),
+ abc_callstatic (for calling own methods)
+ call (for closures)
+*/
+FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
+
+ typedcode_t v = node_read($1);
+ $$.c = v.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.number);
+ multiname_destroy(name);
+/* } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
+ int slot = (int)(ptroff_t)$$.c->data[0];
+ trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
+ 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.number);*/
+ } 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.number);
+ multiname_destroy(name);
+ } else {
+ $$.c = abc_getglobalscope($$.c);
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_call($$.c, $3.number);
+ }
+
+ 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 {
+ $$.t = TYPE_ANY;
+ $$.c = abc_coerce_a($$.c);
+ }
+}
+
+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.number);
+ $$.c = abc_pushundefined($$.c);
+ $$.t = TYPE_ANY;
+}
+
+DELETE: "delete" E {
+ typedcode_t v = node_read($2);
+ $$.c = v.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 = traits_find_slotid(state->cls->abc->traits,slot)->name;
+ $$.c = code_cutlast($$.c);
+ $$.c = abc_deleteproperty2($$.c, name);
+ } else {
+ $$.c = abc_getlocal_0($$.c);
+ MULTINAME_LATE(m, v.t?v.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_lt {
+ $$ = node_read($1);
+}
+EXPRESSION : COMMA_EXPRESSION {
+ $$ = node_read($1);
+}
+COMMA_EXPRESSION : E %prec below_lt {
+ $$ = mkmultinode(&node_comma, $1);
+}
+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_lt {
+ $$ = $1;
+ $$ = code_append($$, node_exec($3));
+}
+
+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);}
+DICTLH: T_INT {syntaxerror("dictionary keys must be strings");}
+DICTLH: T_UINT {syntaxerror("dictionary keys must be strings");}
+DICTLH: T_FLOAT {syntaxerror("dictionary keys must be strings");}
+
+DICT_EXPRPAIR_LIST : DICTLH ':' NONCOMMAEXPRESSION {
+ $$.cc = 0;
+ $$.cc = code_append($$.cc, $1);
+ $$.cc = code_append($$.cc, $3.c);
+ $$.number = 2;
+}
+DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST ',' DICTLH ':' NONCOMMAEXPRESSION {
+ $$.cc = $1.cc;
+ $$.number = $1.number+2;
+ $$.cc = code_append($$.cc, $3);
+ $$.cc = code_append($$.cc, $5.c);
+}
+
+// ----------------------- expression evaluation -------------------------------------
+
+E : INNERFUNCTION %prec prec_none {$$ = 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 {
+ $$ = $1;
+}
+
+/* regexp */
+E : T_REGEXP {
+ typedcode_t v;
+ v.c = 0;
+ multiname_t m = {QNAME, &stdns, 0, "RegExp"};
+ if(!$1.options) {
+ v.c = abc_getlex2(v.c, &m);
+ v.c = abc_pushstring(v.c, $1.pattern);
+ v.c = abc_construct(v.c, 1);
+ } else {
+ v.c = abc_getlex2(v.c, &m);
+ v.c = abc_pushstring(v.c, $1.pattern);
+ v.c = abc_pushstring(v.c, $1.options);
+ v.c = abc_construct(v.c, 2);
+ }
+ v.t = TYPE_REGEXP;
+ $$ = mkcodenode(v);
+}
+
+E : KW_ARGUMENTS {
+ PASS1
+ state->method->need_arguments = 1;
+ PASS2
+ typedcode_t v;
+ v.c = abc_getlocal(0, state->method->need_arguments);
+ v.t = TYPE_ARRAY;
+ $$ = mkcodenode(v);
+}
+
+/* array */
+E : '[' MAYBE_EXPRESSION_LIST ']' {
+ typedcode_t v;
+ v.c = code_new();
+ v.c = code_append(v.c, $2.cc);
+ v.c = abc_newarray(v.c, $2.number);
+ v.t = registry_getarrayclass();
+ $$ = mkcodenode(v);
+}
+
+/* dictionary */
+E : "{ (dictionary)" MAYBE_DICT_EXPRPAIR_LIST '}' {
+ typedcode_t v;
+ v.c = code_new();
+ v.c = code_append(v.c, $2.cc);
+ v.c = abc_newobject(v.c, $2.number/2);
+ v.t = registry_getobjectclass();
+ $$ = mkcodenode(v);
+}
+
+E : E '<' E {$$ = mknode2(&node_lt,$1,$3);}
+E : E '>' E {$$ = mknode2(&node_gt,$1,$3);}
+E : E "<=" E {$$ = mknode2(&node_le,$1,$3);}
+E : E ">=" E {$$ = mknode2(&node_ge,$1,$3);}
+E : E "==" E {$$ = mknode2(&node_eqeq,$1,$3);}
+E : E "===" E {$$ = mknode2(&node_eqeqeq,$1,$3);}
+E : E "!==" E {$$ = mknode2(&node_noteqeq,$1,$3);}
+E : E "!=" E {$$ = mknode2(&node_noteq,$1,$3);}
+E : E "||" E {$$ = mknode2(&node_oror,$1,$3);}
+E : E "&&" E {$$ = mknode2(&node_andand,$1,$3);}
+E : '!' E {$$ = mknode1(&node_not, $2);}
+E : '~' E {$$ = mknode1(&node_bitnot, $2);}
+E : E '&' E {$$ = mknode2(&node_bitand, $1, $3);}
+E : E '^' E {$$ = mknode2(&node_bitxor, $1, $3);}
+E : E '|' E {$$ = mknode2(&node_bitor, $1, $3);}
+E : E ">>" E {$$ = mknode2(&node_shr, $1, $3);}
+E : E ">>>" E {$$ = mknode2(&node_ushr, $1, $3);}
+E : E "<<" E {$$ = mknode2(&node_shl, $1, $3);}
+E : E '/' E {$$ = mknode2(&node_div, $1, $3);}
+E : E '%' E {$$ = mknode2(&node_mod, $1, $3);}
+E : E '+' E {$$ = mknode2(&node_plus, $1, $3);}
+E : E '-' E {$$ = mknode2(&node_minus, $1, $3);}
+E : E '*' E {$$ = mknode2(&node_multiply, $1, $3);}
+E : E "in" E {$$ = mknode2(&node_in, $1, $3);}
+E : E "as" E {$$ = mknode2(&node_as, $1, $3);}
+E : E "instanceof" E {$$ = mknode2(&node_instanceof, $1, $3);}
+E : E "is" E {$$ = mknode2(&node_is, $1, $3);}
+E : "typeof" E {$$ = mknode1(&node_typeof, $2);}
+E : "void" E {$$ = mknode1(&node_void, $2);}
+E : "void" { $$ = mkconstnode(constant_new_undefined());}
+E : '(' COMMA_EXPRESSION ')' { $$=$2;}
+E : '-' E {$$ = mknode1(&node_neg, $2);}
+E : E '[' E ']' {$$ = mknode2(&node_arraylookup, $1,$3);}
+E : E "*=" E {$$ = mknode2(&node_muleq, $1, $3);}
+E : E "%=" E {$$ = mknode2(&node_modeq, $1, $3);}
+E : E "<<=" E {$$ = mknode2(&node_shleq, $1, $3);}
+E : E ">>=" E {$$ = mknode2(&node_shreq, $1, $3);}
+E : E ">>>=" E {$$ = mknode2(&node_ushreq, $1, $3);}
+E : E "/=" E { $$ = mknode2(&node_diveq, $1, $3);}
+E : E "|=" E { $$ = mknode2(&node_bitoreq, $1, $3);}
+E : E "^=" E { $$ = mknode2(&node_bitxoreq, $1, $3);}
+E : E "&=" E { $$ = mknode2(&node_bitandeq, $1, $3);}
+E : E "+=" E { $$ = mknode2(&node_pluseq, $1, $3);}
+E : E "-=" E { $$ = mknode2(&node_minuseq, $1, $3);}
+E : E '=' E { $$ = mknode2(&node_assign, $1, $3);}
+E : E '?' E ':' E %prec below_assignment { $$ = mknode3(&node_tenary, $1, $3, $5);}
+
+E : E "++" { $$ = mknode1(&node_rplusplus, $1);}
+E : E "--" { $$ = mknode1(&node_rminusminus, $1);}
+E : "++" %prec plusplus_prefix E {$$ = mknode1(&node_lplusplus, $2); }
+E : "--" %prec minusminus_prefix E {$$ = mknode1(&node_lminusminus, $2); }
+
+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 = findmember_nsset(t, $3, 1);
+ MEMBER_MULTINAME(m, f, $3);
+ typedcode_t v;
+ v.c = 0;
+ v.c = abc_getlocal_0(v.c);
+ v.c = abc_getsuper2(v.c, &m);
+ v.t = slotinfo_gettype((slotinfo_t*)f);
+ $$ = mkcodenode(v);
+ }
+
+E : '@' T_IDENTIFIER {
+ typedcode_t v;
+ multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $2};
+ v.c = abc_getlex2(0, &m);
+ v.t = TYPE_STRING;
+ $$ = mkcodenode(v);
+}
+
+E : E '.' '(' {PASS12 new_state();state->xmlfilter=1;} E ')' {
+ PASS1 old_state();
+ PASS2
+ typedcode_t v = node_read($1);
+ typedcode_t w = node_read($5);
+ code_t*c = 0;
+ int index = alloc_local();
+ int result = alloc_local();
+ int tmp = alloc_local();
+ int xml = alloc_local();
+
+ c = code_append(c, v.c);
+ c = abc_checkfilter(c);
+ c = abc_coerce_a(c); //hasnext2 converts to *
+ c = abc_setlocal(c, xml);
+ multiname_t m = {QNAME, &stdns, 0, "XMLList"};
+ c = abc_getlex2(c, &m);
+ c = abc_construct(c, 0);
+ c = abc_setlocal(c, result);
+ c = abc_pushbyte(c, 0);
+ c = abc_setlocal(c, index);
+ code_t*jmp = c = abc_jump(c, 0);
+ code_t*loop = c = abc_label(c);
+ c = abc_getlocal(c, xml);
+ c = abc_getlocal(c, index);
+ c = abc_nextvalue(c);
+ c = abc_dup(c);
+ c = abc_setlocal(c, tmp);
+ c = abc_pushwith(c);
+ c = code_append(c, w.c);
+ c = abc_popscope(c);
+ code_t*b = c = abc_iffalse(c, 0);
+ c = abc_getlocal(c, result);
+ c = abc_getlocal(c, index);
+ c = abc_getlocal(c, tmp);
+ multiname_t m2 = {MULTINAMEL, 0, &nopackage_namespace_set, 0};
+ c = abc_setproperty2(c, &m2);
+ c = b->branch = jmp->branch = abc_nop(c);
+ c = abc_kill(c, tmp);
+ c = abc_hasnext2(c, xml, index);
+ c = abc_iftrue(c, loop);
+ c = abc_getlocal(c, result);
+ c = abc_kill(c, xml);
+ c = abc_kill(c, result);
+ c = abc_kill(c, index);
+
+ c = var_block(c, state->vars);
+ old_state();
+ typedcode_t r;
+ r.c = c;
+ r.t = TYPE_XMLLIST;
+ $$ = mkcodenode(r);
+}
+
+ID_OR_NS : T_IDENTIFIER {$$=$1;}
+ID_OR_NS : '*' {$$="*";}
+ID_OR_NS : T_NAMESPACE {$$=(char*)$1;}
+SUBNODE: X_IDENTIFIER
+ | '*' {$$="*";}
+
+/*
+MAYBE_NS: T_IDENTIFIER "::" {$$=$1;}
+ | T_NAMESPACE "::" {$$=(char*)$1;}
+ | '*' "::" {$$="*";}
+ | {$$=0;}*/
+
+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 '.' '@' 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 '.' SUBNODE {
+ typedcode_t v1 = node_read($1);
+ $$.c = v1.c;
+ classinfo_t*t = v1.t;
+ char is_static = 0;
+ if(TYPE_IS_CLASS(t) && t->data) {
+ t = t->data;
+ is_static = 1;
+ }
+ 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);
+ }
+ memberinfo_t*f = findmember_nsset(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 {
+ if(!f) {
+ if(!TYPE_IS_XMLLIST(t)) {
+ as3_softwarning("Access of undefined property '%s' in %s", $3, t->name);
+ }
+ }
+ MEMBER_MULTINAME(m, f, $3);
+ $$.c = abc_getproperty2($$.c, &m);
+ }
+ /* determine type */
+ $$.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);
+
+ slotinfo_t*a = registry_find(package->str, $3);
+ if(a) {
+ $$ = push_class(a);
+ } else if(dict_contains(state->import_toplevel_packages, package2) ||
+ registry_ispackage(package2)) {
+ $$.c = v1.c;
+ $$.c->data[0] = string_new4(package2);
+ $$.t = 0;
+ } else {
+ syntaxerror("couldn't resolve %s", package2);
+ }
+ } 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 */
+ as3_softwarning("Resolving %s on unknown type", $3);
+ multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
+ $$.c = abc_getproperty2($$.c, &m);
+ $$.c = abc_coerce_a($$.c);
+ $$.t = TYPE_ANY;
+ }
+}
+
+%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->method, 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->is_static;
+
+ /* look at current class' members */
+ if(!state->method->inner &&
+ !state->xmlfilter &&
+ 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 {
+ MEMBER_MULTINAME(m, f, 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(!state->xmlfilter && (a = find_class(name))) {
+ if(state->cls && state->cls->info == (classinfo_t*)a && i_am_static) {
+ o.c = abc_getlocal_0(0);
+ o.t = TYPE_CLASS((classinfo_t*)a);
+ } else {
+ o = push_class(a);
+ }
+ return mkcodenode(o);
+ }
+
+ /* look through package prefixes */
+ if(!state->xmlfilter &&
+ (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) {
+ if(!state->method->inner && !state->xmlfilter) {
+ /* we really should make inner functions aware of the class context */
+ 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
+ function's variables.
+ We consider everything which is not a local variable "unresolved".
+ This encompasses class names, members of the surrounding class
+ etc. which is *correct* because local variables of the parent function
+ would shadow those.
+ */
+
+ if(!find_variable(state, $1)) {
+ 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);
+ }
+
+ $$ = 0;
+ PASS2