+
+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;}
+
+USE_NAMESPACE : "use" "namespace" T_IDENTIFIER
+