+ 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;
+}
+
+char is_break_or_jump(code_t*c)
+{
+ if(!c)
+ return 0;
+ if(c->opcode == OPCODE_JUMP ||
+ c->opcode == OPCODE___BREAK__ ||
+ c->opcode == OPCODE___CONTINUE__ ||
+ c->opcode == OPCODE_THROW ||
+ c->opcode == OPCODE_RETURNVOID ||
+ c->opcode == OPCODE_RETURNVALUE) {
+ return 1;
+ }
+ return 0;
+}
+
+
+#define IS_FINALLY_TARGET(op) \
+ ((op) == OPCODE___CONTINUE__ || \
+ (op) == OPCODE___BREAK__ || \
+ (op) == OPCODE_RETURNVOID || \
+ (op) == OPCODE_RETURNVALUE || \
+ (op) == OPCODE___RETHROW__)
+
+static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
+{
+#define NEED_EXTRA_STACK_ARG
+ code_t*finally_label = abc_nop(0);
+ NEW(lookupswitch_t, l);
+ //_lookupswitch
+
+ code_t*i = c;
+ int count=0;
+ while(i) {
+ code_t*prev = i->prev;
+ if(IS_FINALLY_TARGET(i->opcode)) {
+ code_t*p = prev;
+ char needvalue=0;
+ if(i->opcode == OPCODE___RETHROW__ ||
+ i->opcode == OPCODE_RETURNVALUE) {
+ if(i->opcode == OPCODE___RETHROW__)
+ i->opcode = OPCODE_THROW;
+ needvalue=1;
+ p = abc_coerce_a(p);
+ p = abc_setlocal(p, tempvar);
+ }
+ p = abc_pushbyte(p, count++);
+ p = abc_jump(p, finally_label);
+ code_t*target = p = abc_label(p);
+#ifdef NEED_EXTRA_STACK_ARG
+ p = abc_pop(p);
+#endif
+ if(needvalue) {
+ p = abc_getlocal(p, tempvar);
+ }
+
+ p->next = i;i->prev = p;
+ list_append(l->targets, target);
+ }
+ i = prev;
+ }
+
+ code_t*j,*f;
+ c = abc_pushbyte(c, -1);
+ c = code_append(c, finally_label);
+ c = code_append(c, finally);
+
+#ifdef NEED_EXTRA_STACK_ARG
+ c = abc_dup(c);
+#endif
+ c = abc_lookupswitch(c, l);
+ c = l->def = abc_label(c);
+#ifdef NEED_EXTRA_STACK_ARG
+ c = abc_pop(c);
+#endif
+
+ return c;
+}
+
+static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
+{
+ code_t*i = c;
+ while(i) {
+ code_t*prev = i->prev;
+ if(IS_FINALLY_TARGET(i->opcode)) {
+ if(i->opcode == OPCODE___RETHROW__)
+ i->opcode = OPCODE_THROW;
+ code_t*end = code_dup(finally);
+ code_t*start = code_start(end);
+ if(prev) prev->next = start;
+ start->prev = prev;
+ i->prev = end;
+ end->next = i;
+ }
+ i = prev;
+ }
+ return code_append(c, finally);
+}
+
+code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
+{
+ if(!finally)
+ return c;
+ code_t*i = c;
+ char cantdup=0;
+ int num_insertion_points=0;
+ while(i) {
+ if(IS_FINALLY_TARGET(i->opcode))
+ num_insertion_points++;
+ i = i->prev;
+ }
+ i = finally;
+ int code_size=0;
+ while(i) {
+ code_size++;
+ if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
+ cantdup=1;
+ }
+ i = i->prev;
+ }
+ int simple_version_cost = (1+num_insertion_points)*code_size;
+ int lookup_version_cost = 4*num_insertion_points + 5;
+
+ if(cantdup || simple_version_cost > lookup_version_cost) {
+ printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
+ return insert_finally_lookup(c, finally, tempvar);
+ } else {
+ printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
+ return insert_finally_simple(c, finally, tempvar);
+ }
+}
+
+#define PASS1 }} if(as3_pass == 1) {{
+#define PASS1END }} if(as3_pass == 2) {{
+#define PASS2 }} if(as3_pass == 2) {{
+#define PASS12 }} {{
+#define PASS12END }} if(as3_pass == 2) {{
+
+%}
+
+%%
+
+/* ------------ 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 {
+ code_t**cc = &global->init->method->body->code;
+ *cc = code_append(*cc, $1);
+}
+
+/* ------------ 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();
+ }
+ }
+
+ /* that's the default for a local register, anyway
+ else {
+ state->method->initcode = abc_pushundefined(state->method->initcode);
+ state->method->initcode = abc_setlocal(state->method->initcode, index);
+ }*/
+ //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
+}
+
+/* ------------ control flow ------------------------- */
+
+MAYBEELSE: %prec below_else {$$ = code_new();}
+MAYBEELSE: "else" CODEBLOCK {$$=$2;}
+//MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
+
+IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
+
+ $$ = code_new();
+ $$ = code_append($$, $4.c);
+ code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);