+/* ------------ 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);
+
+ $$ = code_append($$, $6);
+ if($7) {
+ myjmp = $$ = abc_jump($$, 0);
+ }
+ myif->branch = $$ = abc_nop($$);
+ if($7) {
+ $$ = code_append($$, $7);
+ myjmp->branch = $$ = abc_nop($$);
+ }
+ $$ = var_block($$);
+ old_state();
+}
+
+FOR_INIT : {$$=code_new();}
+FOR_INIT : VARIABLE_DECLARATION
+FOR_INIT : VOIDEXPRESSION
+
+// TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
+FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
+ $$=$2;new_variable($2,$3,1);
+}
+FOR_IN_INIT : T_IDENTIFIER {
+ $$=$1;
+}
+
+FOR_START : T_FOR '(' {new_state();$$.name=$1;$$.each=0;}
+FOR_START : T_FOR "each" '(' {new_state();$$.name=$1;$$.each=1;}
+
+FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
+ if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
+ $$ = code_new();
+ $$ = code_append($$, $2);
+ code_t*loopstart = $$ = abc_label($$);
+ $$ = code_append($$, $4.c);
+ code_t*myif = $$ = abc_iffalse($$, 0);
+ $$ = code_append($$, $8);
+ code_t*cont = $$ = abc_nop($$);
+ $$ = code_append($$, $6);
+ $$ = abc_jump($$, loopstart);
+ code_t*out = $$ = abc_nop($$);
+ breakjumpsto($$, $1.name, out);
+ continuejumpsto($$, $1.name, cont);
+ myif->branch = out;
+
+ $$ = var_block($$);
+ old_state();
+}
+
+FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
+ variable_t*var = find_variable($2);
+ char*tmp1name = concat2($2, "__tmp1__");
+ int it = new_variable(tmp1name, TYPE_INT, 0);
+ char*tmp2name = concat2($2, "__array__");
+ int array = new_variable(tmp1name, 0, 0);
+
+ $$ = code_new();
+ $$ = code_append($$, $4.c);
+ $$ = abc_coerce_a($$);
+ $$ = abc_setlocal($$, array);
+ $$ = abc_pushbyte($$, 0);
+ $$ = abc_setlocal($$, it);
+
+ code_t*loopstart = $$ = abc_label($$);
+
+ $$ = abc_hasnext2($$, array, it);
+ code_t*myif = $$ = abc_iffalse($$, 0);
+ $$ = abc_getlocal($$, array);
+ $$ = abc_getlocal($$, it);
+ if(!$1.each)
+ $$ = abc_nextname($$);
+ else
+ $$ = abc_nextvalue($$);
+ $$ = converttype($$, 0, var->type);
+ $$ = abc_setlocal($$, var->index);
+
+ $$ = code_append($$, $6);
+ $$ = abc_jump($$, loopstart);
+
+ code_t*out = $$ = abc_nop($$);
+ breakjumpsto($$, $1.name, out);
+ continuejumpsto($$, $1.name, loopstart);
+
+ myif->branch = out;
+
+ $$ = var_block($$);
+ old_state();
+
+ free(tmp1name);
+ free(tmp2name);
+}
+
+WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK {
+
+ $$ = code_new();
+
+ code_t*myjmp = $$ = abc_jump($$, 0);
+ code_t*loopstart = $$ = abc_label($$);
+ $$ = code_append($$, $6);
+ code_t*cont = $$ = abc_nop($$);
+ myjmp->branch = cont;
+ $$ = code_append($$, $4.c);
+ $$ = abc_iftrue($$, loopstart);
+ code_t*out = $$ = abc_nop($$);
+ breakjumpsto($$, $1, out);
+ continuejumpsto($$, $1, cont);
+
+ $$ = var_block($$);
+ old_state();
+}
+
+DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
+ $$ = code_new();
+ code_t*loopstart = $$ = abc_label($$);
+ $$ = code_append($$, $3);
+ code_t*cont = $$ = abc_nop($$);
+ $$ = code_append($$, $6.c);
+ $$ = abc_iftrue($$, loopstart);
+ code_t*out = $$ = abc_nop($$);
+ breakjumpsto($$, $1, out);
+ continuejumpsto($$, $1, cont);
+
+ $$ = var_block($$);
+ old_state();
+}
+
+BREAK : "break" %prec prec_none {
+ $$ = abc___break__(0, "");
+}
+BREAK : "break" T_IDENTIFIER {
+ $$ = abc___break__(0, $2);
+}
+CONTINUE : "continue" %prec prec_none {
+ $$ = abc___continue__(0, "");
+}
+CONTINUE : "continue" T_IDENTIFIER {
+ $$ = abc___continue__(0, $2);
+}
+
+MAYBE_CASE_LIST : {$$=0;}
+MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
+MAYBE_CASE_LIST : DEFAULT {$$=$1;}
+MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
+CASE_LIST: CASE {$$=$1;}
+CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
+
+CASE: "case" E ':' MAYBECODE {
+ $$ = abc_dup(0);
+ $$ = code_append($$, $2.c);
+ code_t*j = $$ = abc_ifne($$, 0);
+ $$ = code_append($$, $4);
+ if($$->opcode != OPCODE___BREAK__) {
+ $$ = abc___fallthrough__($$, "");
+ }
+ code_t*e = $$ = abc_nop($$);
+ j->branch = e;
+}
+DEFAULT: "default" ':' MAYBECODE {
+ $$ = $3;
+}
+SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
+ $$=$4.c;
+ $$ = code_append($$, $7);
+ code_t*out = $$ = abc_pop($$);
+ breakjumpsto($$, $1, out);
+
+ code_t*c = $$,*lastblock=0;
+ while(c) {
+ if(c->opcode == OPCODE_IFNE) {
+ if(!c->next) syntaxerror("internal error in fallthrough handling");
+ lastblock=c->next;
+ } else if(c->opcode == OPCODE___FALLTHROUGH__) {
+ if(lastblock) {
+ c->opcode = OPCODE_JUMP;
+ c->branch = lastblock;
+ } else {
+ /* fall through end of switch */
+ c->opcode = OPCODE_NOP;
+ }
+ }
+ c=c->prev;
+ }
+
+ $$ = var_block($$);
+ old_state();
+}
+
+/* ------------ try / catch /finally ---------------- */
+
+CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name=$3;new_variable($3, $4, 0);}
+ '{' MAYBECODE '}' {
+ namespace_t name_ns = {ACCESS_PACKAGE, ""};
+ multiname_t name = {QNAME, &name_ns, 0, $3};
+
+ NEW(abc_exception_t, e)
+ e->exc_type = sig2mname($4);
+ e->var_name = multiname_clone(&name);
+ $$ = e;
+
+ code_t*c = 0;
+ int i = find_variable_safe($3)->index;
+ e->target = c = abc_nop(0);
+ c = abc_setlocal(c, i);
+ c = code_append(c, $8);
+ c = abc_kill(c, i);
+
+ c = var_block(c);
+ old_state();
+}
+FINALLY: "finally" '{' {new_state();state->exception_name=0;} MAYBECODE '}' {
+ $4 = var_block($4);
+ if(!$4) {
+ $$=0;
+ old_state();
+ } else {
+ NEW(abc_exception_t, e)
+ e->exc_type = 0; //all exceptions
+ e->var_name = 0; //no name
+ e->target = 0;
+ e->to = abc_nop(0);
+ e->to = code_append(e->to, $4);
+ old_state();
+ $$ = e;
+ }
+}
+
+CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
+CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
+CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
+CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
+ $$ = $1;
+ $$.finally = 0;
+ if($2) {
+ list_append($$.l,$2);
+ $$.finally = $2->to;$2->to=0;
+ }
+}
+CATCH_FINALLY_LIST: FINALLY {
+ $$.l=list_new();
+ $$.finally = 0;
+ if($1) {
+ list_append($$.l,$1);
+ $$.finally = $1->to;$1->to=0;
+ }
+}