+ syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
+ }
+ } else if(r->opcode == OPCODE_GETSLOT) {
+ write->opcode = OPCODE_SETSLOT;
+ write->data[0] = r->data[0];
+ if(!justassign) {
+ prefix = abc_dup(prefix); // we need the object, too
+ }
+ use_temp_var = 1;
+ } else if(r->opcode == OPCODE_GETLOCAL) {
+ write->opcode = OPCODE_SETLOCAL;
+ write->data[0] = r->data[0];
+ } else if(r->opcode == OPCODE_GETLOCAL_0) {
+ write->opcode = OPCODE_SETLOCAL_0;
+ } else if(r->opcode == OPCODE_GETLOCAL_1) {
+ write->opcode = OPCODE_SETLOCAL_1;
+ } else if(r->opcode == OPCODE_GETLOCAL_2) {
+ write->opcode = OPCODE_SETLOCAL_2;
+ } else if(r->opcode == OPCODE_GETLOCAL_3) {
+ write->opcode = OPCODE_SETLOCAL_3;
+ } else {
+ code_dump(r, 0, 0, "", stdout);
+ syntaxerror("illegal lvalue: can't assign a value to this expression");
+ }
+ code_t* c = 0;
+
+ int temp = -1;
+ if(!justassign) {
+ if(use_temp_var) {
+ /* with getproperty/getslot, we have to be extra careful not
+ to execute the read code twice, as it might have side-effects
+ (e.g. if the property is in fact a setter/getter combination)
+
+ So read the value, modify it, and write it again,
+ using prefix only once and making sure (by using a temporary
+ register) that the return value is what we just wrote */
+ temp = gettempvar();
+ c = code_append(c, prefix);
+ c = code_append(c, r);
+ if(readbefore) {
+ c = abc_dup(c);
+ c = abc_setlocal(c, temp);
+ }
+ c = code_append(c, middlepart);
+ if(!readbefore) {
+ c = abc_dup(c);
+ c = abc_setlocal(c, temp);
+ }
+ c = code_append(c, write);
+ c = abc_getlocal(c, temp);
+ c = abc_kill(c, temp);
+ } else {
+ /* if we're allowed to execute the read code twice *and*
+ the middlepart doesn't modify the code, things are easier.
+ */
+ code_t* r2 = code_dup(r);
+ //c = code_append(c, prefix);
+ parserassert(!prefix);
+ c = code_append(c, r);
+ c = code_append(c, middlepart);
+ c = code_append(c, write);
+ c = code_append(c, r2);
+ }
+ } else {
+ /* even smaller version: overwrite the value without reading
+ it out first */
+ if(!use_temp_var) {
+ if(prefix) {
+ c = code_append(c, prefix);
+ c = abc_dup(c);
+ }
+ c = code_append(c, middlepart);
+ c = code_append(c, write);
+ c = code_append(c, r);
+ } else {
+ temp = gettempvar();
+ if(prefix) {
+ c = code_append(c, prefix);
+ }
+ c = code_append(c, middlepart);
+ c = abc_dup(c);
+ c = abc_setlocal(c, temp);
+ c = code_append(c, write);
+ c = abc_getlocal(c, temp);
+ c = abc_kill(c, temp);
+ }
+ }
+
+ return c;
+}
+
+
+%}
+
+
+%%
+
+/* ------------ code blocks / statements ---------------- */
+
+PROGRAM: MAYBE_PROGRAM_CODE_LIST
+
+MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
+PROGRAM_CODE_LIST: PROGRAM_CODE
+ | PROGRAM_CODE_LIST PROGRAM_CODE
+
+PROGRAM_CODE: PACKAGE_DECLARATION
+ | INTERFACE_DECLARATION
+ | CLASS_DECLARATION
+ | FUNCTION_DECLARATION
+ | SLOT_DECLARATION
+ | PACKAGE_INITCODE
+ | ';'
+
+MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
+INPACKAGE_CODE_LIST: INPACKAGE_CODE
+ | INPACKAGE_CODE_LIST INPACKAGE_CODE
+
+INPACKAGE_CODE: INTERFACE_DECLARATION
+ | CLASS_DECLARATION
+ | FUNCTION_DECLARATION
+ | SLOT_DECLARATION
+ | PACKAGE_INITCODE
+ | ';'
+
+MAYBECODE: CODE {$$=$1;}
+MAYBECODE: {$$=code_new();}
+
+CODE: CODE CODEPIECE {$$=code_append($1,$2);}
+CODE: CODEPIECE {$$=$1;}
+
+// code which also may appear outside a method
+CODE_STATEMENT: IMPORT
+CODE_STATEMENT: FOR
+CODE_STATEMENT: FOR_IN
+CODE_STATEMENT: WHILE
+CODE_STATEMENT: DO_WHILE
+CODE_STATEMENT: SWITCH
+CODE_STATEMENT: IF
+CODE_STATEMENT: WITH
+CODE_STATEMENT: TRY
+CODE_STATEMENT: VOIDEXPRESSION
+
+// code which may appear anywhere
+CODEPIECE: ';' {$$=0;}
+CODEPIECE: CODE_STATEMENT
+CODEPIECE: VARIABLE_DECLARATION
+CODEPIECE: BREAK
+CODEPIECE: CONTINUE
+CODEPIECE: RETURN
+CODEPIECE: THROW
+
+CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;}
+CODEPIECE: USE_NAMESPACE {/*TODO*/$$=0;}
+
+CODEBLOCK : '{' CODE '}' {$$=$2;}
+CODEBLOCK : '{' '}' {$$=0;}
+CODEBLOCK : CODEPIECE ';' {$$=$1;}
+CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
+
+/* ------------ package init code ------------------- */
+
+PACKAGE_INITCODE: CODE_STATEMENT {
+ if($1) as3_warning("code ignored");
+}
+
+/* ------------ variables --------------------------- */
+
+MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
+ | {$$.c=abc_pushundefined(0);
+ $$.t=TYPE_ANY;
+ }
+
+VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
+VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
+
+VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
+VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
+
+ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
+{
+ if(variable_exists($1))
+ syntaxerror("Variable %s already defined", $1);
+
+ if(!is_subtype_of($3.t, $2)) {
+ syntaxerror("Can't convert %s to %s", $3.t->name,
+ $2->name);
+ }
+
+ int index = new_variable($1, $2, 1);
+
+ if($2) {
+ if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
+ $$ = $3.c;
+ $$ = converttype($$, $3.t, $2);
+ $$ = abc_setlocal($$, index);
+ } else {
+ $$ = defaultvalue(0, $2);
+ $$ = abc_setlocal($$, index);
+ }
+ } else {
+ if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
+ $$ = $3.c;
+ $$ = abc_coerce_a($$);
+ $$ = abc_setlocal($$, index);
+ } else {
+ $$ = code_new();