+/* ------------ code blocks / statements ---------------- */
+
+PROGRAM: MAYBECODE
+
+MAYBECODE: CODE {$$=$1;}
+MAYBECODE: {$$=code_new();}
+
+CODE: CODE CODEPIECE {$$=code_append($1,$2);}
+CODE: CODEPIECE {$$=$1;}
+
+CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/}
+CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/}
+CODEPIECE: INTERFACE_DECLARATION {/*TODO*/$$=code_new();}
+CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/}
+CODEPIECE: ';' {$$=code_new();}
+CODEPIECE: VARIABLE_DECLARATION {$$=$1}
+CODEPIECE: VOIDEXPRESSION {$$=$1}
+CODEPIECE: FOR {$$=$1}
+CODEPIECE: WHILE {$$=$1}
+CODEPIECE: BREAK {$$=$1}
+CODEPIECE: IF {$$=$1}
+CODEPIECE: ASSIGNMENT {$$=$1}
+CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
+CODEPIECE: FUNCTION_DECLARATION {/*TODO*/$$=code_new();}
+CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();}
+
+CODEBLOCK : '{' MAYBECODE '}' {$$=$2;}
+CODEBLOCK : CODEPIECE ';' {$$=$1;}
+CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
+
+/* ------------ functions --------------------------- */
+
+FUNCTION_DECLARATION: MODIFIERS "function" GETSET T_IDENTIFIER '(' PARAMS ')'
+ MAYBETYPE '{' {startfunction(0,$1,$3,$4,$6,$8)} MAYBECODE '}' {
+ if(!state->m) syntaxerror("internal error: undefined function");
+ state->initcode = abc_nop(state->initcode);
+ state->initcode = abc_nop(state->initcode);
+ state->initcode = abc_nop(state->initcode);
+ state->m->code = code_append(state->initcode, $11);state->initcode=0;
+ endfunction()
+}
+
+/* ------------ variables --------------------------- */
+
+MAYBEEXPRESSION : '=' EXPRESSION {$$=$2;}
+ | {$$.c=abc_pushundefined(0);
+ $$.t=TYPE_ANY;
+ }
+
+VAR : "const" | "var"
+VARIABLE_DECLARATION : VAR T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
+ if(variable_exists($2->text))
+ syntaxerror("Variable %s already defined", $2->text);
+
+ if(!is_subtype_of($4.t, $3)) {
+ syntaxerror("Can't convert %s to %s", $4.t->name,
+ $3->name);
+ }
+
+ int index = new_variable($2->text, $3);
+
+ if($3) {
+ if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
+ $$ = $4.c;
+ $$ = converttype($$, $4.t, $3);
+ $$ = abc_setlocal($$, index);
+ } else {
+ $$ = defaultvalue(0, $3);
+ $$ = abc_setlocal($$, index);
+ }
+
+ /* push default value for type on stack */
+ state->initcode = defaultvalue(state->initcode, $3);
+ state->initcode = abc_setlocal(state->initcode, index);
+ } else {
+ /* only bother to actually set this variable if its syntax is either
+ var x:type;
+ or
+ var x=expr;
+ */
+ if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
+ $$ = $4.c;
+ $$ = abc_coerce_a($$);
+ $$ = abc_setlocal($$, index);
+ } else {
+ $$ = code_new();
+ }
+ }
+
+ /* that's the default for a local register, anyway
+ else {
+ state->initcode = abc_pushundefined(state->initcode);
+ state->initcode = abc_setlocal(state->initcode, index);
+ }*/
+ printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
+}
+ASSIGNMENT : T_IDENTIFIER '=' EXPRESSION {
+ class_signature_t*type=0;
+ int i = find_variable($1->text, &type);
+ $$ = $3.c;
+ if(!type && $3.t) {
+ // convert to "any" type, the register is untyped
+ $$ = abc_coerce_a($$);
+ } else {
+ // TODO: convert ints to strings etc.
+ }
+ $$ = abc_setlocal($$, i);
+}
+
+/* ------------ control flow ------------------------- */
+
+MAYBEELSE: %prec prec_none {$$ = code_new();}
+MAYBEELSE: "else" CODEBLOCK {$$=$2;}
+//MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
+
+IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
+ $$ = state->initcode;state->initcode=0;
+
+ $$ = code_append($$, $4.c);
+ code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
+
+ $$ = code_append($$, $6);
+ if($7) {
+ myjmp = $$ = abc_jump($$, 0);
+ }
+ myif->branch = $$ = abc_label($$);
+ if($7) {
+ $$ = code_append($$, $7);
+ myjmp->branch = $$ = abc_label($$);
+ }
+
+ $$ = killvars($$);old_state();
+}
+
+FOR_INIT : {$$=code_new();}
+FOR_INIT : ASSIGNMENT | VARIABLE_DECLARATION | VOIDEXPRESSION
+
+FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
+ $$ = state->initcode;state->initcode=0;
+
+ $$ = code_append($$, $4);
+ code_t*loopstart = $$ = abc_label($$);
+ $$ = code_append($$, $6.c);
+ code_t*myif = $$ = abc_iffalse($$, 0);
+ $$ = code_append($$, $10);
+ $$ = code_append($$, $8);
+ $$ = abc_jump($$, loopstart);
+ code_t*out = $$ = abc_label($$);
+ breakjumpsto($$, out);
+ myif->branch = out;
+
+ $$ = killvars($$);old_state();
+}
+
+WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
+ $$ = state->initcode;state->initcode=0;
+
+ code_t*myjmp = $$ = abc_jump($$, 0);
+ code_t*loopstart = $$ = abc_label($$);
+ $$ = code_append($$, $6);
+ myjmp->branch = $$ = abc_label($$);
+ $$ = code_append($$, $4.c);
+ $$ = abc_iftrue($$, loopstart);
+ code_t*out = $$ = abc_label($$);
+ breakjumpsto($$, out);
+
+ $$ = killvars($$);old_state();
+}
+
+BREAK : "break" {
+ $$ = abc___break__(0);
+}
+
+/* ------------ packages and imports ---------------- */
+
+PACKAGE_DECLARATION : "package" MULTILEVELIDENTIFIER '{' {startpackage($2)} MAYBECODE '}' {endpackage()}
+PACKAGE_DECLARATION : "package" '{' {startpackage(0)} MAYBECODE '}' {endpackage()}
+
+IMPORT : "import" PACKAGESPEC {addimport($2);}
+
+TYPE : PACKAGEANDCLASS {$$=$1;}
+ | '*' {$$=registry_getanytype();}
+ | "String" {$$=registry_getstringclass();}
+ | "int" {$$=registry_getintclass();}
+ | "uint" {$$=registry_getuintclass();}
+ | "Boolean" {$$=registry_getbooleanclass();}
+ | "Number" {$$=registry_getnumberclass();}