+RETURN: "return" %prec prec_none {
+ $$ = abc_returnvoid(0);
+}
+RETURN: "return" EXPRESSION {
+ $$ = $2.c;
+ $$ = abc_returnvalue($$);
+}
+// ----------------------- expression types -------------------------------------
+
+NONCOMMAEXPRESSION : E %prec prec_belowminus {$$=$1;}
+EXPRESSION : E %prec prec_belowminus {$$ = $1;}
+EXPRESSION : EXPRESSION ',' E %prec prec_belowminus {
+ $$.c = $1.c;
+ $$.c = abc_pop($$.c);
+ $$.c = code_append($$.c,$3.c);
+ $$.t = $3.t;
+}
+VOIDEXPRESSION : EXPRESSION %prec prec_belowminus {$$=abc_pop($1.c);}
+
+// ----------------------- expression evaluation -------------------------------------
+
+E : CONSTANT
+E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
+E : NEW {$$ = $1;}
+E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
+ $$.t = TYPE_ANY;
+ }
+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_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 = abc_pop($$.c);
+ $$.c = code_append($$.c,$3.c);
+ $$.c = converttype($$.c, $1.t, $$.t);
+ code_t*label = $$.c = abc_label($$.c);
+ jmp->branch = label;
+ }
+E : E "&&" E {$$.t = join_types($1.t, $3.t, 'A');
+ $$.c = $1.c;
+ $$.c = converttype($$.c, $1.t, $$.t);
+ $$.c = abc_dup($$.c);
+ code_t*jmp = $$.c = abc_iffalse($$.c, 0);
+ $$.c = abc_pop($$.c);
+ $$.c = code_append($$.c,$3.c);
+ $$.c = converttype($$.c, $1.t, $$.t);
+ code_t*label = $$.c = abc_label($$.c);
+ jmp->branch = label;
+ }
+
+E : E '.' T_IDENTIFIER
+ {$$.c = $1.c;
+ if($$.t) {
+ //namespace_t ns = {$$.t->access, (char*)$$.t->package};
+ namespace_t ns = {$$.t->access, ""};
+ multiname_t m = {QNAME, &ns, 0, $3->text};
+ $$.c = abc_getproperty2($$.c, &m);
+ /* FIXME: get type of ($1.t).$3 */
+ $$.t = registry_getanytype();
+ } else {
+ namespace_t ns = {ACCESS_PACKAGE, ""};
+ multiname_t m = {QNAME, &ns, 0, $3->text};
+ $$.c = abc_getproperty2($$.c, &m);
+ $$.t = registry_getanytype();
+ }
+ }
+
+E : '!' E {$$.c=$2.c;
+ $$.c = abc_not($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+
+E : E '-' E
+E : E '/' E
+E : E '+' E {$$.c = code_append($1.c,$3.c);$$.c = abc_add($$.c);$$.c=abc_coerce_a($$.c);
+ $$.t = join_types($1.t, $3.t, '+');
+ }
+E : E '%' E {$$.c = code_append($1.c,$3.c);$$.c = abc_modulo($$.c);$$.c=abc_coerce_a($$.c);
+ $$.t = join_types($1.t, $3.t, '%');
+ }
+E : E '*' E {$$.c = code_append($1.c,$3.c);$$.c = abc_multiply($$.c);$$.c=abc_coerce_a($$.c);
+ $$.t = join_types($1.t, $3.t, '*');
+ }
+
+E : E "as" E
+E : E "is" E
+E : '(' E ')' {$$=$2;}
+E : '-' E {$$=$2;}
+
+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);
+ $$.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);
+ $$.t = $1.t;
+ }
+E : E '=' E { code_t*c = 0;
+ c = abc_pop(c);
+ c = code_append(c, $3.c);
+ c = converttype(c, $3.t, $1.t);
+ $$.c = toreadwrite($1.c, c);
+ $$.t = $1.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);
+ } else {
+ c=abc_increment(c);
+ type = TYPE_NUMBER;
+ }
+ c=converttype(c, type, $1.t);
+ $$.c = toreadwrite($1.c, c);
+ $$.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_increment_i(c);
+ } else {
+ c=abc_increment(c);
+ type = TYPE_NUMBER;
+ }
+ c=converttype(c, type, $1.t);
+ $$.c = toreadwrite($1.c, c);
+ $$.t = $1.t;
+ }
+
+VAR_READ : T_IDENTIFIER {
+ $$.t = 0;
+ $$.c = 0;
+ int i;
+ memberinfo_t*m;
+ if((i = find_variable($1->text, &$$.t)) >= 0) {
+ $$.c = abc_getlocal($$.c, i);
+ } else if((m = registry_findmember(state->clsinfo, $1->text))) {
+ $$.t = m->type;
+ if(m->slot>0) {
+ $$.c = abc_getlocal_0($$.c);
+ $$.c = abc_getslot($$.c, m->slot);
+ } else {
+ $$.c = abc_getlocal_0($$.c);
+ $$.c = abc_getproperty($$.c, $1->text);
+ }
+ } else {
+ warning("Couldn't resolve %s, doing late binding", $1->text);
+ state->late_binding = 1;