+IDECLARATION_LIST : IDECLARATION_LIST IDECLARATION
+IDECLARATION : ';'
+IDECLARATION : "var" T_IDENTIFIER {
+ syntaxerror("variable declarations not allowed in interfaces");
+}
+IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
+ $1 |= FLAG_PUBLIC;
+ if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
+ syntaxerror("invalid method modifiers: interface methods always need to be public");
+ }
+ startfunction(0,$1,$3,$4,&$6,$8);
+ endfunction(0,$1,$3,$4,&$6,$8, 0);
+}
+
+/* ------------ classes and interfaces (body, slots ) ------- */
+
+VARCONST: "var" | "const"
+
+SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
+ int flags = $1;
+ memberinfo_t* info = memberinfo_register(state->cls->info, $3, MEMBER_SLOT);
+ info->type = $4;
+ info->flags = flags;
+ trait_t*t=0;
+
+ namespace_t mname_ns = {flags2access(flags), ""};
+ multiname_t mname = {QNAME, &mname_ns, 0, $3};
+
+ if(!(flags&FLAG_STATIC)) {
+ if($4) {
+ MULTINAME(m, $4);
+ t=abc_class_slot(state->cls->abc, &mname, &m);
+ } else {
+ t=abc_class_slot(state->cls->abc, &mname, 0);
+ }
+ info->slot = t->slot_id;
+ } else {
+ if($4) {
+ MULTINAME(m, $4);
+ t=abc_class_staticslot(state->cls->abc, &mname, &m);
+ } else {
+ t=abc_class_staticslot(state->cls->abc, &mname, 0);
+ }
+ info->slot = t->slot_id;
+ }
+ if($5.c && !is_pushundefined($5.c)) {
+ code_t*c = 0;
+ c = abc_getlocal_0(c);
+ c = code_append(c, $5.c);
+ c = converttype(c, $5.t, $4);
+ c = abc_setslot(c, t->slot_id);
+ if(!(flags&FLAG_STATIC))
+ state->cls->init = code_append(state->cls->init, c);
+ else
+ state->cls->static_init = code_append(state->cls->static_init, c);
+ }
+ if($2==KW_CONST) {
+ t->kind= TRAIT_CONST;
+ }
+}
+
+/* ------------ constants -------------------------------------- */
+
+MAYBESTATICCONSTANT: {$$=0;}
+MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
+
+STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
+STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
+STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
+STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
+STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
+//STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
+STATICCONSTANT : "true" {$$ = constant_new_true($1);}
+STATICCONSTANT : "false" {$$ = constant_new_false($1);}
+STATICCONSTANT : "null" {$$ = constant_new_null($1);}
+
+/* ------------ classes and interfaces (body, functions) ------- */
+
+// non-vararg version
+MAYBE_PARAM_LIST: {
+ memset(&$$,0,sizeof($$));
+}
+MAYBE_PARAM_LIST: PARAM_LIST {
+ $$=$1;
+}
+
+// vararg version
+MAYBE_PARAM_LIST: "..." PARAM {
+ memset(&$$,0,sizeof($$));
+ $$.varargs=1;
+ list_append($$.list, $2);
+}
+MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
+ $$ =$1;
+ $$.varargs=1;
+ list_append($$.list, $4);
+}
+
+// non empty
+PARAM_LIST: PARAM_LIST ',' PARAM {
+ $$ = $1;
+ list_append($$.list, $3);
+}
+PARAM_LIST: PARAM {
+ memset(&$$,0,sizeof($$));
+ list_append($$.list, $1);
+}
+
+PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
+ $$ = malloc(sizeof(param_t));
+ $$->name=$1;
+ $$->type = $3;
+ $$->value = $4;
+}
+PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
+ $$ = malloc(sizeof(param_t));
+ $$->name=$1;
+ $$->type = TYPE_ANY;
+ $$->value = $2;
+}
+GETSET : "get" {$$=$1;}
+ | "set" {$$=$1;}
+ | {$$=0;}
+
+FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
+ MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
+{
+ code_t*c = 0;
+ if(state->method->late_binding) {
+ c = abc_getlocal_0(c);
+ c = abc_pushscope(c);
+ }
+ if(state->method->is_constructor && !state->method->has_super) {
+ // generate default constructor
+ c = abc_getlocal_0(c);
+ c = abc_constructsuper(c, 0);
+ }
+
+ c = code_append(c, state->method->initcode);
+ c = code_append(c, $11);
+
+ /* append return if necessary */
+ if(!c || c->opcode != OPCODE_RETURNVOID &&
+ c->opcode != OPCODE_RETURNVALUE) {
+ c = abc_returnvoid(c);
+ }
+ endfunction(0,$1,$3,$4,&$6,$8,c);
+}
+
+/* ------------- package + class ids --------------- */
+
+CLASS: T_IDENTIFIER {
+
+ /* try current package */
+ $$ = find_class($1);
+ if(!$$) syntaxerror("Could not find class %s\n", $1);
+}
+
+PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
+ $$ = registry_findclass($1, $3);
+ if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
+}
+
+QNAME: PACKAGEANDCLASS
+ | CLASS
+
+QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
+QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
+
+TYPE : QNAME {$$=$1;}
+ | '*' {$$=registry_getanytype();}
+ /*
+ | "String" {$$=registry_getstringclass();}
+ | "int" {$$=registry_getintclass();}
+ | "uint" {$$=registry_getuintclass();}
+ | "Boolean" {$$=registry_getbooleanclass();}
+ | "Number" {$$=registry_getnumberclass();}
+ */
+
+MAYBETYPE: ':' TYPE {$$=$2;}
+MAYBETYPE: {$$=0;}
+
+/* ----------function calls, delete, constructor calls ------ */
+
+MAYBE_PARAM_VALUES : %prec prec_none {$$=0;}
+MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
+
+MAYBE_EXPRESSION_LIST : {$$=0;}
+MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
+EXPRESSION_LIST : NONCOMMAEXPRESSION {$$=list_new();
+ typedcode_t*t = malloc(sizeof(typedcode_t));
+ *t = $1;
+ list_append($$, t);}
+EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {$$=$1;
+ typedcode_t*t = malloc(sizeof(typedcode_t));
+ *t = $3;
+ list_append($$, t);}
+
+NEW : "new" CLASS MAYBE_PARAM_VALUES {
+ MULTINAME(m, $2);
+ $$.c = code_new();
+
+ if($2->slot) {
+ $$.c = abc_getglobalscope($$.c);
+ $$.c = abc_getslot($$.c, $2->slot);
+ } else {
+ $$.c = abc_findpropstrict2($$.c, &m);
+ }
+
+ typedcode_list_t*l = $3;
+ int len = 0;
+ while(l) {
+ $$.c = code_append($$.c, l->typedcode->c); // push parameters on stack
+ l = l->next;
+ len ++;
+ }
+ if($2->slot)
+ $$.c = abc_construct($$.c, len);
+ else
+ $$.c = abc_constructprop2($$.c, &m, len);
+ $$.t = $2;
+}
+
+/* TODO: use abc_call (for calling local variables),
+ abc_callstatic (for calling own methods)
+ call (for closures)
+*/
+FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
+ typedcode_list_t*l = $3;
+ int len = 0;
+ code_t*paramcode = 0;
+ while(l) {
+ paramcode = code_append(paramcode, l->typedcode->c); // push parameters on stack
+ l = l->next;
+ len ++;
+ }
+
+ $$.c = $1.c;
+ if($$.c->opcode == OPCODE_COERCE_A) {
+ $$.c = code_cutlast($$.c);
+ }
+
+ $$.t = TYPE_ANY;
+ multiname_t*name = 0;
+ if($$.c->opcode == OPCODE_GETPROPERTY) {
+ name = multiname_clone($$.c->data[0]);
+ $$.c = code_cutlast($$.c);
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_callproperty2($$.c, name, len);
+ } else if($$.c->opcode == OPCODE_GETSLOT) {
+ int slot = (int)(ptroff_t)$$.c->data[0];
+ trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
+ if(t->kind!=TRAIT_METHOD) {
+ //flash allows to assign closures to members.
+ //syntaxerror("not a function");
+ }
+ 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, len);
+ } else if($$.c->opcode == OPCODE_GETSUPER) {
+ name = multiname_clone($$.c->data[0]);
+ $$.c = code_cutlast($$.c);
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_callsuper2($$.c, name, len);
+ } else {
+ $$.c = abc_getlocal_0($$.c);
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_call($$.c, len);
+ }
+
+ memberinfo_t*f = 0;
+
+ if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
+ $$.t = $1.t->function->return_type;
+ } else {
+ $$.c = abc_coerce_a($$.c);
+ $$.t = TYPE_ANY;
+ }
+}
+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);
+ typedcode_list_t*l = 0;
+ int len = 0;
+ for(l=$3;l;l=l->next) {
+ $$.c = code_append($$.c, l->typedcode->c);len++;
+ }
+ /*
+ 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, len);
+ $$.c = abc_pushundefined($$.c);
+ $$.t = TYPE_ANY;
+}
+
+DELETE: "delete" E {
+ $$.c = $2.c;
+ if($$.c->opcode == OPCODE_COERCE_A) {
+ $$.c = code_cutlast($$.c);
+ }
+ multiname_t*name = 0;
+ if($$.c->opcode == OPCODE_GETPROPERTY) {
+ $$.c->opcode = OPCODE_DELETEPROPERTY;
+ } else if($$.c->opcode == OPCODE_GETSLOT) {
+ int slot = (int)(ptroff_t)$$.c->data[0];
+ multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
+ $$.c = code_cutlast($$.c);
+ $$.c = abc_deleteproperty2($$.c, name);
+ } else {
+ $$.c = abc_getlocal_0($$.c);
+ MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
+ $$.c = abc_deleteproperty2($$.c, &m);
+ }
+ $$.t = TYPE_BOOLEAN;
+}
+
+RETURN: "return" %prec prec_none {
+ $$ = abc_returnvoid(0);
+}
+RETURN: "return" EXPRESSION {
+ $$ = $2.c;
+ $$ = abc_returnvalue($$);
+}
+
+// ----------------------- expression types -------------------------------------
+
+NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
+EXPRESSION : E %prec below_minus {$$ = $1;}
+EXPRESSION : EXPRESSION ',' E %prec below_minus {
+ $$.c = $1.c;
+ $$.c = cut_last_push($$.c);
+ $$.c = code_append($$.c,$3.c);
+ $$.t = $3.t;
+}
+VOIDEXPRESSION : EXPRESSION %prec below_minus {
+ $$=cut_last_push($1.c);
+}
+
+// ----------------------- expression evaluation -------------------------------------
+
+E : CONSTANT
+E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
+E : NEW {$$ = $1;}
+E : DELETE {$$ = $1;}
+E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
+ $$.t = TYPE_ANY;
+ }
+
+CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
+ //MULTINAME(m, registry_getintclass());
+ //$$.c = abc_coerce2($$.c, &m); // FIXME
+ $$.t = TYPE_INT;
+ }
+CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
+ $$.t = TYPE_INT;
+ }
+CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
+ $$.t = TYPE_INT;
+ }
+CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
+ $$.t = TYPE_UINT;
+ }
+CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
+ $$.t = TYPE_FLOAT;
+ }
+CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
+ $$.t = TYPE_STRING;
+ }
+CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
+ $$.t = TYPE_ANY;
+ }
+CONSTANT : "true" {$$.c = abc_pushtrue(0);
+ $$.t = TYPE_BOOLEAN;
+ }
+CONSTANT : "false" {$$.c = abc_pushfalse(0);
+ $$.t = TYPE_BOOLEAN;
+ }
+CONSTANT : "null" {$$.c = abc_pushnull(0);
+ $$.t = TYPE_NULL;
+ }
+
+E : FUNCTIONCALL
+E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+
+E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
+ $$.c = $1.c;
+ $$.c = converttype($$.c, $1.t, $$.t);
+ $$.c = abc_dup($$.c);
+ code_t*jmp = $$.c = abc_iftrue($$.c, 0);
+ $$.c = cut_last_push($$.c);
+ $$.c = code_append($$.c,$3.c);
+ $$.c = converttype($$.c, $3.t, $$.t);
+ code_t*label = $$.c = abc_label($$.c);
+ jmp->branch = label;
+ }
+E : E "&&" E {
+ $$.t = join_types($1.t, $3.t, 'A');
+ /*printf("%08x:\n",$1.t);
+ code_dump($1.c, 0, 0, "", stdout);
+ printf("%08x:\n",$3.t);
+ code_dump($3.c, 0, 0, "", stdout);
+ printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
+ $$.c = $1.c;
+ $$.c = converttype($$.c, $1.t, $$.t);
+ $$.c = abc_dup($$.c);
+ code_t*jmp = $$.c = abc_iffalse($$.c, 0);
+ $$.c = cut_last_push($$.c);
+ $$.c = code_append($$.c,$3.c);
+ $$.c = converttype($$.c, $3.t, $$.t);
+ code_t*label = $$.c = abc_label($$.c);
+ jmp->branch = label;
+ }
+
+E : '!' E {$$.c=$2.c;
+ $$.c = abc_not($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+
+E : '~' E {$$.c=$2.c;
+ $$.c = abc_bitnot($$.c);
+ $$.t = TYPE_INT;
+ }
+
+E : E '&' E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_bitand($$.c);
+ $$.t = TYPE_INT;
+ }
+
+E : E '^' E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_bitxor($$.c);
+ $$.t = TYPE_INT;
+ }
+
+E : E '|' E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_bitor($$.c);
+ $$.t = TYPE_INT;
+ }
+
+E : E '-' E {$$.c = code_append($1.c,$3.c);
+ if(BOTH_INT($1,$3)) {
+ $$.c = abc_subtract_i($$.c);
+ $$.t = TYPE_INT;
+ } else {
+ $$.c = abc_subtract($$.c);
+ $$.t = TYPE_NUMBER;
+ }
+ }
+E : E ">>" E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_rshift($$.c);
+ $$.t = TYPE_INT;
+ }
+E : E ">>>" E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_urshift($$.c);
+ $$.t = TYPE_INT;
+ }
+E : E "<<" E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_lshift($$.c);
+ $$.t = TYPE_INT;
+ }
+
+E : E '/' E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_divide($$.c);
+ $$.t = TYPE_NUMBER;
+ }
+E : E '+' E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_add($$.c);
+ $$.t = TYPE_NUMBER;
+ }
+E : E '%' E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_modulo($$.c);
+ $$.t = TYPE_NUMBER;
+ }
+E : E '*' E {$$.c = code_append($1.c,$3.c);
+ if(BOTH_INT($1,$3)) {
+ $$.c = abc_multiply_i($$.c);
+ $$.t = TYPE_INT;
+ } else {
+ $$.c = abc_multiply($$.c);
+ $$.t = TYPE_NUMBER;
+ }
+ }
+
+E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
+ if(use_astype && TYPE_IS_CLASS($3.t)) {
+ MULTINAME(m,$3.t->cls);
+ $$.c = abc_astype2($1.c, &m);
+ $$.t = $3.t->cls;
+ } else {
+ $$.c = code_append($1.c, $3.c);
+ $$.c = abc_astypelate($$.c);
+ $$.t = TYPE_ANY;
+ }
+ }
+
+E : E "is" E {$$.c = code_append($1.c, $3.c);
+ $$.c = abc_istypelate($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+
+E : "typeof" '(' E ')' {
+ $$.c = $3.c;
+ $$.c = abc_typeof($$.c);
+ $$.t = TYPE_STRING;
+ }
+
+E : "void" E {
+ $$.c = cut_last_push($2.c);
+ $$.c = abc_pushundefined($$.c);
+ $$.t = TYPE_ANY;
+ }
+
+E : "void" { $$.c = abc_pushundefined(0);
+ $$.t = TYPE_ANY;
+ }
+
+E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
+
+E : '-' E {
+ $$=$2;
+ if(IS_INT($2)) {
+ $$.c=abc_negate_i($$.c);
+ $$.t = TYPE_INT;
+ } else {
+ $$.c=abc_negate($$.c);
+ $$.t = TYPE_NUMBER;
+ }
+}
+
+E : E '[' E ']' {
+ $$.c = $1.c;
+ $$.c = code_append($$.c, $3.c);
+
+ MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
+ $$.c = abc_getproperty2($$.c, &m);
+ $$.t = 0; // array elements have unknown type
+}
+
+E : E "*=" E {
+ code_t*c = $3.c;
+ if(BOTH_INT($1,$3)) {
+ c=abc_multiply_i(c);
+ } else {
+ c=abc_multiply(c);
+ }
+ c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
+ $$.c = toreadwrite($1.c, c, 0, 0);
+ $$.t = $1.t;
+ }
+
+E : E "%=" E {
+ code_t*c = abc_modulo($3.c);
+ c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
+ $$.c = toreadwrite($1.c, c, 0, 0);
+ $$.t = $1.t;
+ }
+E : E "<<=" E {
+ code_t*c = abc_lshift($3.c);
+ c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
+ $$.c = toreadwrite($1.c, c, 0, 0);
+ $$.t = $1.t;
+ }
+E : E ">>=" E {
+ code_t*c = abc_rshift($3.c);
+ c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
+ $$.c = toreadwrite($1.c, c, 0, 0);
+ $$.t = $1.t;
+ }
+E : E ">>>=" E {
+ code_t*c = abc_urshift($3.c);
+ c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
+ $$.c = toreadwrite($1.c, c, 0, 0);
+ $$.t = $1.t;
+ }
+E : E "/=" E {
+ code_t*c = abc_divide($3.c);
+ c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
+ $$.c = toreadwrite($1.c, c, 0, 0);
+ $$.t = $1.t;
+ }
+E : E "+=" E {
+ code_t*c = $3.c;
+ if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
+ c=abc_add_i(c);
+ } else {
+ c=abc_add(c);
+ }
+ c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
+
+ $$.c = toreadwrite($1.c, c, 0, 0);
+ $$.t = $1.t;
+ }
+E : E "-=" E { code_t*c = $3.c;
+ if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
+ c=abc_subtract_i(c);
+ } else {
+ c=abc_subtract(c);
+ }
+ c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
+
+ $$.c = toreadwrite($1.c, c, 0, 0);
+ $$.t = $1.t;
+ }
+E : E '=' E { code_t*c = 0;
+ c = code_append(c, $3.c);
+ c = converttype(c, $3.t, $1.t);
+ $$.c = toreadwrite($1.c, c, 1, 0);
+ $$.t = $1.t;
+ }
+
+E : E '?' E ':' E %prec below_assignment {
+ $$.c = $1.c;
+ code_t*j1 = $$.c = abc_iffalse($$.c, 0);
+ $$.c = code_append($$.c, $3.c);
+ code_t*j2 = $$.c = abc_jump($$.c, 0);
+ $$.c = j1->branch = abc_label($$.c);
+ $$.c = code_append($$.c, $5.c);
+ $$.c = j2->branch = abc_label($$.c);
+ $$.t = join_types($3.t,$5.t,'?');
+ }
+
+// TODO: use inclocal where appropriate
+E : E "++" { code_t*c = 0;
+ classinfo_t*type = $1.t;
+ if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
+ c=abc_increment_i(c);
+ type = TYPE_INT;
+ } else {
+ c=abc_increment(c);
+ type = TYPE_NUMBER;
+ }
+ c=converttype(c, type, $1.t);
+ $$.c = toreadwrite($1.c, c, 0, 1);
+ $$.t = $1.t;
+ }
+E : E "--" { code_t*c = 0;
+ classinfo_t*type = $1.t;
+ if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
+ c=abc_decrement_i(c);
+ type = TYPE_INT;
+ } else {
+ c=abc_decrement(c);
+ type = TYPE_NUMBER;
+ }
+ c=converttype(c, type, $1.t);
+ $$.c = toreadwrite($1.c, c, 0, 1);
+ $$.t = $1.t;
+ }
+
+E : "++" %prec plusplus_prefix E { code_t*c = 0;
+ classinfo_t*type = $2.t;
+ if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
+ c=abc_increment_i(c);
+ type = TYPE_INT;
+ } else {
+ c=abc_increment(c);
+ type = TYPE_NUMBER;
+ }
+ c=converttype(c, type, $2.t);
+ $$.c = toreadwrite($2.c, c, 0, 0);
+ $$.t = $2.t;
+ }
+
+E : "--" %prec minusminus_prefix E { code_t*c = 0;
+ classinfo_t*type = $2.t;
+ if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
+ c=abc_decrement_i(c);
+ type = TYPE_INT;
+ } else {
+ c=abc_decrement(c);
+ type = TYPE_NUMBER;
+ }
+ c=converttype(c, type, $2.t);
+ $$.c = toreadwrite($2.c, c, 0, 0);
+ $$.t = $2.t;
+ }
+
+E : "super" '.' T_IDENTIFIER
+ { if(!state->cls->info)
+ syntaxerror("super keyword not allowed outside a class");
+ classinfo_t*t = state->cls->info->superclass;
+ if(!t) t = TYPE_OBJECT;
+
+ memberinfo_t*f = registry_findmember(t, $3);
+ namespace_t ns = {flags2access(f->flags), ""};
+ MEMBER_MULTINAME(m, f);
+ $$.c = 0;
+ $$.c = abc_getlocal_0($$.c);
+ $$.c = abc_getsuper2($$.c, &m);
+ $$.t = memberinfo_gettype(f);
+ }
+
+E : E '.' T_IDENTIFIER
+ {$$.c = $1.c;
+ classinfo_t*t = $1.t;
+ char is_static = 0;
+ if(TYPE_IS_CLASS(t) && t->cls) {
+ t = t->cls;
+ is_static = 1;
+ }
+ if(t) {
+ memberinfo_t*f = registry_findmember(t, $3);
+ char noslot = 0;
+ if(f && !is_static != !(f->flags&FLAG_STATIC))
+ noslot=1;
+ if(f && f->slot && !noslot) {
+ $$.c = abc_getslot($$.c, f->slot);
+ } else {
+ MEMBER_MULTINAME(m, f);
+ $$.c = abc_getproperty2($$.c, &m);
+ }
+ /* determine type */
+ $$.t = memberinfo_gettype(f);
+ if(!$$.t)
+ $$.c = abc_coerce_a($$.c);
+ } else {
+ /* when resolving a property on an unknown type, we do know the
+ name of the property (and don't seem to need the package), but
+ we need to make avm2 try out all access modes */
+ multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
+ $$.c = abc_getproperty2($$.c, &m);
+ $$.c = abc_coerce_a($$.c);
+ $$.t = registry_getanytype();
+ }
+ }
+
+VAR_READ : T_IDENTIFIER {
+ $$.t = 0;
+ $$.c = 0;
+ int i;
+ classinfo_t*a = 0;
+ memberinfo_t*f = 0;
+
+ /* look at variables */
+ if((i = find_variable($1, &$$.t)) >= 0) {
+ // $1 is a local variable
+ $$.c = abc_getlocal($$.c, i);
+
+ /* look at current class' members */
+ } else if((f = registry_findmember(state->cls->info, $1))) {
+ // $1 is a function in this class
+ int var_is_static = (f->flags&FLAG_STATIC);
+ int i_am_static = ((state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC);
+ if(var_is_static != i_am_static) {
+ /* there doesn't seem to be any "static" way to access
+ static properties of a class */
+ state->method->late_binding = 1;
+ $$.t = f->type;
+ namespace_t ns = {flags2access(f->flags), ""};
+ multiname_t m = {QNAME, &ns, 0, $1};
+ $$.c = abc_findpropstrict2($$.c, &m);
+ $$.c = abc_getproperty2($$.c, &m);
+ } else {
+ if(f->slot>0) {
+ $$.c = abc_getlocal_0($$.c);
+ $$.c = abc_getslot($$.c, f->slot);
+ } else {
+ namespace_t ns = {flags2access(f->flags), ""};
+ multiname_t m = {QNAME, &ns, 0, $1};
+ $$.c = abc_getlocal_0($$.c);
+ $$.c = abc_getproperty2($$.c, &m);
+ }
+ }
+ if(f->kind == MEMBER_METHOD) {
+ $$.t = TYPE_FUNCTION(f);
+ } else {
+ $$.t = f->type;
+ }
+
+ /* look at classes in the current package and imported classes */
+ } else if((a = find_class($1))) {
+ if(a->slot) {
+ $$.c = abc_getglobalscope($$.c);
+ $$.c = abc_getslot($$.c, a->slot);
+ } else {
+ MULTINAME(m, a);
+ $$.c = abc_getlex2($$.c, &m);
+ }
+ $$.t = TYPE_CLASS(a);
+
+ /* unknown object, let the avm2 resolve it */
+ } else {
+ if(strcmp($1,"trace"))
+ 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);
+ }
+}
+
+//TODO:
+//VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
+//VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
+//VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
+
+// ----------------- namespaces -------------------------------------------------
+
+NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=$2;}
+NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=$2;}
+NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=$2;}