file copied from ming 0.3alpha2
[swftools.git] / lib / action / swf5compiler.y
diff --git a/lib/action/swf5compiler.y b/lib/action/swf5compiler.y
new file mode 100644 (file)
index 0000000..d80acb0
--- /dev/null
@@ -0,0 +1,1698 @@
+/* $Id: swf5compiler.y,v 1.1 2004/02/02 10:12:34 kramm Exp $ */
+
+%start program
+
+%{
+
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include "compile.h"
+#include "action.h"
+#include "assembler.h"
+
+#define YYPARSE_PARAM buffer
+
+Buffer bf, bc;
+
+%}
+
+%union
+{
+  Buffer action;
+  char *str;
+  SWFGetUrl2Method getURLMethod;
+  int op;
+  int intVal;
+  int len;
+  double doubleVal;
+
+  struct
+  {
+    Buffer buffer;
+    int count;
+  } exprlist;
+  struct switchcase   switchcase;
+  struct switchcases switchcases;
+  struct
+  {
+       Buffer obj, ident, memexpr;
+  } lval;
+}
+
+/* tokens etc. */
+
+%token BREAK CONTINUE FUNCTION ELSE SWITCH CASE DEFAULT FOR IN IF WHILE
+%token DO VAR NEW DELETE RETURN END WITH ASM EVAL
+
+%token RANDOM GETTIMER LENGTH CONCAT SUBSTR TRACE INT ORD CHR GETURL
+%token GETURL1 NEXTFRAME PREVFRAME PLAY STOP TOGGLEQUALITY STOPSOUNDS
+
+%token DUP SWAP POP PUSH SETREGISTER CALLFUNCTION CALLMETHOD
+%token AND OR XOR MODULO ADD LESSTHAN EQUALS
+%token INC DEC TYPEOF INSTANCEOF ENUMERATE INITOBJECT INITARRAY GETMEMBER
+%token SETMEMBER SHIFTLEFT SHIFTRIGHT SHIFTRIGHT2 VAREQUALS OLDADD SUBTRACT
+%token MULTIPLY DIVIDE OLDEQUALS OLDLESSTHAN LOGICALAND LOGICALOR NOT
+%token STRINGEQ STRINGLENGTH SUBSTRING GETVARIABLE SETVARIABLE
+%token SETTARGETEXPRESSION  DUPLICATEMOVIECLIP REMOVEMOVIECLIP
+%token STRINGLESSTHAN MBLENGTH MBSUBSTRING MBORD MBCHR
+%token BRANCHALWAYS BRANCHIFTRUE GETURL2 POST GET
+%token LOADVARIABLES LOADMOVIE LOADVARIABLESNUM LOADMOVIENUM
+%token CALLFRAME STARTDRAG STOPDRAG GOTOFRAME SETTARGET
+
+%token NULLVAL
+%token <intVal> INTEGER
+%token <doubleVal> DOUBLE
+%token <intVal> BOOLEAN
+%token <str> REGISTER
+
+/* these two are strdup'ed in compiler.flex, so free them up here */
+%token <str> STRING
+%token <str> IDENTIFIER
+
+%token EQ "=="
+%token LE "<="
+%token GE ">="
+%token NE "!="
+%token LAN "&&"
+%token LOR "||"
+%token INCR "++"
+%token DECR "--"
+%token IEQ "+="
+%token DEQ "/="
+%token MEQ "*="
+%token SEQ "-="
+%token REQ "%="
+%token AEQ "&="
+%token OEQ "|="
+
+%token SHL "<<"
+%token SHR ">>"
+%token SHR2 ">>>"
+%token SHLEQ "<<="
+%token SHREQ ">>="
+%token SHR2EQ ">>>="
+
+
+/* ascending order of ops ..? */
+
+%nonassoc NOELSE
+%nonassoc ELSE
+%left ','
+%right '=' "*=" "/=" "%=" "+=" "-=" "&=" "|=" "^=" ">>=" ">>>=" "<<="
+%right '?' ':'
+%left "&&" "||"
+%left "==" "!="
+%left '<' '>' "<=" ">="
+%left '&' '|' '^'
+%left "<<" ">>" ">>>"
+%left '+' '-'
+%left '*' '/' '%'
+%nonassoc "++" "--"
+%right '!' '~' UMINUS
+%right POSTFIX
+%right TYPEOF
+%nonassoc INSTANCEOF
+%left '.' '[' ']'
+
+
+%type <action> program code
+%type <action> stmt stmts
+%type <action> if_stmt iter_stmt cont_stmt break_stmt return_stmt
+%type <action> with_stmt
+%type <action> switch_stmt
+%type <action> anon_function_decl function_decl anycode
+%type <action> void_function_call function_call method_call
+%type <action> assign_stmt assign_stmts assign_stmts_opt
+%type <action> expr expr_or_obj objexpr expr_opt obj_ref
+%type <action> emptybraces level init_vars init_var primary lvalue_expr
+%type <lval> lvalue
+
+%type <exprlist> expr_list objexpr_list formals_list
+
+%type <switchcase> switch_case
+%type <switchcases> switch_cases
+
+%type <op> assignop incdecop
+%type <getURLMethod> urlmethod
+
+%type <str> identifier
+
+%type <len> opcode opcode_list push_item with push_list
+
+/*
+%type <intVal> integer
+%type <doubleVal> double
+*/
+%%
+
+/* rules */
+
+program
+       : { bf = newBuffer();
+               bc = newBuffer();
+       } code
+               { Buffer b = newBuffer();
+                 bufferWriteConstants(b);
+                 bufferConcat(b, bf);
+                 bufferConcat(b, bc);
+                 *((Buffer *)buffer) = b; }
+       | /* nothing */ { Buffer b = newBuffer(); *((Buffer *)buffer) = b; }
+       ;
+
+code
+       : anycode
+       | code anycode
+       ;
+
+anycode
+       : stmt
+               { bufferConcat(bc, $1); }
+       | function_decl
+               { bufferConcat(bf, $1); }
+       ;
+
+stmts
+       : stmt
+                { $$ = $1; }
+
+       | stmts stmt
+                { $$ = $1;
+                 bufferConcat($$, $2); }
+       ;
+
+emptybraces
+       : '{' '}'       { }
+       ;
+
+stmt
+       : emptybraces                           { $$ = NULL; }
+       | '{' stmts '}'                         { $$ = $2; }
+       | ';'                                   { $$ = NULL; }
+       | assign_stmt ';'                       { $$ = $1; }
+       | if_stmt
+       | iter_stmt
+       | cont_stmt
+       | break_stmt
+       | switch_stmt
+       | return_stmt
+       | with_stmt
+       ;
+
+with_stmt
+       : WITH '(' expr ')' '{' stmts '}'
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_WITH);
+                 bufferWriteS16($$, 2);
+                 bufferWriteS16($$, bufferLength($6));
+                 bufferConcat($$, $6); }
+       ;
+
+// only possible if there is an active CTX_FUNCTION
+// in some contexts, may have to pop a few values ...
+return_stmt
+       : RETURN ';'
+               { int tmp = chkctx(CTX_FUNCTION);
+                 if(tmp < 0)
+                       swf5error("return outside function");
+                 $$ = newBuffer();
+                 while(--tmp >= 0)
+                       bufferWriteOp($$, SWFACTION_POP);
+                 bufferWriteNull($$);
+                 bufferWriteOp($$, SWFACTION_RETURN); }
+
+       | RETURN expr_or_obj ';'
+               { int tmp = chkctx(CTX_FUNCTION);
+                 if(tmp < 0)
+                       swf5error("return outside function");
+                 $$ = newBuffer();
+                 while(--tmp >= 0)
+                       bufferWriteOp($$, SWFACTION_POP);
+                 bufferConcat($$, $2);
+                 bufferWriteOp($$, SWFACTION_RETURN); }
+       ;
+
+assign_stmts
+       : assign_stmt
+       | assign_stmts ',' assign_stmt          { bufferConcat($1, $3); }
+       ;
+
+if_stmt
+       : IF '(' expr ')' stmt ELSE stmt
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_BRANCHIFTRUE);
+                 bufferWriteS16($$, 2);
+                 bufferWriteS16($$, bufferLength($7)+5);
+                 bufferConcat($$, $7);
+                 bufferWriteOp($$, SWFACTION_BRANCHALWAYS);
+                 bufferWriteS16($$, 2);
+                 bufferWriteS16($$, bufferLength($5));
+                 bufferConcat($$, $5); }
+
+       | IF '(' expr ')' stmt          %prec NOELSE
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_LOGICALNOT);
+                 bufferWriteOp($$, SWFACTION_BRANCHIFTRUE);
+                 bufferWriteS16($$, 2);
+                 bufferWriteS16($$, bufferLength($5));
+                 bufferConcat($$, $5); }
+       ;
+
+expr_opt
+       : /* empty */   { $$ = NULL; }
+       | expr          { $$ = $1; }
+       ;
+
+switch_init
+       : SWITCH
+               { addctx(CTX_SWITCH); }
+       ;
+
+switch_stmt
+       : switch_init '(' expr ')' '{'
+         switch_cases '}'
+               { $$ = $3;
+                 bufferResolveSwitch($$, &$6);
+                 bufferResolveJumps($$);
+                 bufferWriteOp($$, SWFACTION_POP);
+                 delctx(CTX_SWITCH);
+ /* FIXME: continue in switch continues surrounding loop, if any */
+       }
+       ;
+
+/* XXX */
+switch_cases
+       : /* empty */
+               { $$.count = 0;
+                 $$.list = 0; }
+
+       | switch_cases switch_case
+               { $$ = $1;
+                 $$.list = (struct switchcase*) realloc($$.list, ($$.count+1) * sizeof(struct switchcase));
+                 $$.list[$$.count] = $2;
+                 $$.count++; }
+       ;
+
+switch_case
+       : CASE expr ':' stmts BREAK ';'
+               { $$.cond = $2;
+                 $$.action = $4;
+                 $$.isbreak = 1; }
+
+       | CASE expr ':' stmts
+               { $$.cond = $2;
+                 $$.action = $4;
+                 $$.isbreak = 0; }
+
+       | DEFAULT ':' stmts
+               { $$.cond = NULL;
+                 $$.action = $3;
+                 $$.isbreak = 0; }
+       ;
+
+
+/* there's GOT to be a better way than this.. */
+
+identifier
+       : IDENTIFIER
+       | NEW           { $$ = strdup("new"); }
+       | DELETE        { $$ = strdup("delete"); }
+       | RANDOM        { $$ = strdup("random"); }
+       | GETTIMER      { $$ = strdup("getTimer"); }
+       | LENGTH        { $$ = strdup("length"); }
+       | CONCAT        { $$ = strdup("concat"); }
+       | SUBSTR        { $$ = strdup("substr"); }
+       | TRACE { $$ = strdup("trace"); }
+       | INT   { $$ = strdup("int"); }
+       | ORD   { $$ = strdup("ord"); }
+       | CHR   { $$ = strdup("chr"); }
+       | GETURL        { $$ = strdup("getURL"); }
+       | GETURL1       { $$ = strdup("getURL1"); }
+       | NEXTFRAME     { $$ = strdup("nextFrame"); }
+       | PREVFRAME     { $$ = strdup("prevFrame"); }
+       | PLAY  { $$ = strdup("play"); }
+       | STOP  { $$ = strdup("stop"); }
+       | TOGGLEQUALITY { $$ = strdup("toggleQuality"); }
+       | STOPSOUNDS    { $$ = strdup("stopSounds"); }
+       | DUP   { $$ = strdup("dup"); }
+       | SWAP  { $$ = strdup("swap"); }
+       | POP   { $$ = strdup("pop"); }
+       | PUSH  { $$ = strdup("push"); }
+       | SETREGISTER   { $$ = strdup("setRegister"); }
+       | CALLFUNCTION  { $$ = strdup("callFunction"); }
+       | CALLMETHOD    { $$ = strdup("callMethod"); }
+       | AND   { $$ = strdup("and"); }
+       | OR    { $$ = strdup("or"); }
+       | XOR   { $$ = strdup("xor"); }
+       | MODULO        { $$ = strdup("modulo"); }
+       | ADD   { $$ = strdup("add"); }
+       | LESSTHAN      { $$ = strdup("lessThan"); }
+       | EQUALS        { $$ = strdup("equals"); }
+       | INC   { $$ = strdup("inc"); }
+       | DEC   { $$ = strdup("dec"); }
+       | TYPEOF        { $$ = strdup("typeof"); }
+       | INSTANCEOF    { $$ = strdup("instanceof"); }
+       | ENUMERATE     { $$ = strdup("enumerate"); }
+       | INITOBJECT    { $$ = strdup("initobject"); }
+       | INITARRAY     { $$ = strdup("initarray"); }
+       | GETMEMBER     { $$ = strdup("getmember"); }
+       | SETMEMBER     { $$ = strdup("setmember"); }
+       | SHIFTLEFT     { $$ = strdup("shiftleft"); }
+       | SHIFTRIGHT    { $$ = strdup("shiftright"); }
+       | SHIFTRIGHT2   { $$ = strdup("shiftright2"); }
+       | VAREQUALS     { $$ = strdup("varequals"); }
+       | OLDADD        { $$ = strdup("oldAdd"); }
+       | SUBTRACT      { $$ = strdup("subtract"); }
+       | MULTIPLY      { $$ = strdup("multiply"); }
+       | DIVIDE        { $$ = strdup("divide"); }
+       | OLDEQUALS     { $$ = strdup("oldequals"); }
+       | OLDLESSTHAN   { $$ = strdup("oldlessthan"); }
+       | LOGICALAND    { $$ = strdup("logicaland"); }
+       | LOGICALOR     { $$ = strdup("logicalor"); }
+       | NOT   { $$ = strdup("not"); }
+       | STRINGEQ      { $$ = strdup("stringeq"); }
+       | STRINGLENGTH  { $$ = strdup("stringlength"); }
+       | SUBSTRING     { $$ = strdup("substring"); }
+       | GETVARIABLE   { $$ = strdup("getvariable"); }
+       | SETVARIABLE   { $$ = strdup("setvariable"); }
+       | SETTARGETEXPRESSION   { $$ = strdup("settargetexpression"); }
+       | DUPLICATEMOVIECLIP    { $$ = strdup("duplicatemovieclip"); }
+       | REMOVEMOVIECLIP       { $$ = strdup("removemovieclip"); }
+       | STARTDRAG     { $$ = strdup("startdrag"); }
+       | STOPDRAG      { $$ = strdup("stopdrag"); }
+       | STRINGLESSTHAN        { $$ = strdup("stringlessthan"); }
+       | MBLENGTH      { $$ = strdup("mblength"); }
+       | MBSUBSTRING   { $$ = strdup("mbsubstring"); }
+       | MBORD { $$ = strdup("mbord"); }
+       | MBCHR { $$ = strdup("mbchr"); }
+       | BRANCHALWAYS  { $$ = strdup("branchalways"); }
+       | BRANCHIFTRUE  { $$ = strdup("branchiftrue"); }
+       | GETURL2       { $$ = strdup("getURL2"); }
+       | POST  { $$ = strdup("post"); }
+       | GET   { $$ = strdup("get"); }
+       | LOADVARIABLES { $$ = strdup("loadvariables"); }
+       | LOADMOVIE     { $$ = strdup("loadmovie"); }
+       ;
+
+formals_list
+       : /* empty */
+               { $$.buffer = newBuffer();
+                 $$.count = 0; }
+
+       | identifier
+               { $$.buffer = newBuffer();
+                 bufferWriteHardString($$.buffer, (byte*)$1, strlen($1)+1);
+                 $$.count = 1; }
+
+       | formals_list ',' identifier
+               { $$ = $1;
+                 bufferWriteHardString($$.buffer, (byte*)$3, strlen($3)+1);
+                 ++$$.count; }
+       ;
+
+function_init
+       : FUNCTION
+               { addctx(CTX_FUNCTION); }
+       ;
+
+function_decl
+       : function_init identifier '(' formals_list ')' stmt
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_DEFINEFUNCTION);
+                 bufferWriteS16($$, strlen($2) +
+                                    bufferLength($4.buffer) + 5);
+                 bufferWriteHardString($$, (byte*) $2, strlen($2)+1);
+                 bufferWriteS16($$, $4.count);
+                 bufferConcat($$, $4.buffer);
+                 bufferWriteS16($$, bufferLength($6));
+                 bufferConcat($$, $6);
+                 delctx(CTX_FUNCTION); }
+       ;
+
+obj_ref
+       : identifier
+               { $$ = newBuffer();
+                 bufferWriteString($$, $1, strlen($1)+1);
+                 free($1); }
+
+       | expr '.' identifier
+               { $$ = $1;
+                 bufferWriteString($$, $3, strlen($3)+1);
+                 bufferWriteOp($$, SWFACTION_GETMEMBER);
+                 free($3); }
+
+       | expr '[' expr ']'
+               { $$ = $1;
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_GETMEMBER); }
+
+       | function_call
+
+       | method_call
+       ;
+
+while_init
+       : WHILE
+               { addctx(CTX_LOOP); }
+       ;
+
+do_init
+       : DO
+               { addctx(CTX_LOOP); }
+       ;
+
+for_init
+       : /* empty */
+               { addctx(CTX_LOOP); }
+       ;
+
+for_in_init
+       : /* empty */
+               { addctx(CTX_FOR_IN); }
+       ;
+
+iter_stmt
+       : while_init '(' expr ')' stmt
+                { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_LOGICALNOT);
+                 bufferWriteOp($$, SWFACTION_BRANCHIFTRUE);
+                 bufferWriteS16($$, 2);
+                 bufferWriteS16($$, bufferLength($5)+5);
+                 bufferConcat($$, $5);
+                 bufferWriteOp($$, SWFACTION_BRANCHALWAYS);
+                 bufferWriteS16($$, 2);
+                 bufferWriteS16($$, -(bufferLength($$)+2));
+                 bufferResolveJumps($$);
+                 delctx(CTX_LOOP); }
+
+       | do_init stmt WHILE '(' expr ')'
+               { if($2)
+                       {       $$ = $2;
+                               bufferConcat($$, $5);
+                       }
+                       else
+                               $$ = $5;
+                 bufferWriteOp($$, SWFACTION_BRANCHIFTRUE);
+                 bufferWriteS16($$, 2);
+                 bufferWriteS16($$, -(bufferLength($$)+2));
+                 bufferResolveJumps($$);
+                 delctx(CTX_LOOP); }
+
+       | FOR '(' assign_stmts_opt ';' expr_opt ';' assign_stmts_opt ')' for_init stmt
+               {
+                 if($3)
+                   $$ = $3;
+                 else
+                   $$ = newBuffer();
+
+                 if($7)
+                 {
+                    bufferWriteOp($$, SWFACTION_BRANCHALWAYS);
+                    bufferWriteS16($$, 2);
+                    bufferWriteS16($$, bufferLength($7));
+                 }
+                 else
+                   $7 = newBuffer();
+
+                 if($5)
+                 {
+                   bufferConcat($7, $5);
+                    bufferWriteOp($7, SWFACTION_LOGICALNOT);
+                    bufferWriteOp($7, SWFACTION_BRANCHIFTRUE);
+                    bufferWriteS16($7, 2);
+                    bufferWriteS16($7, bufferLength($10)+5);
+                  }
+
+                  bufferConcat($7, $10);
+                  bufferWriteOp($7, SWFACTION_BRANCHALWAYS);
+                  bufferWriteS16($7, 2);
+                  bufferWriteS16($7, -(bufferLength($7)+2));
+                  bufferResolveJumps($7);
+
+                  bufferConcat($$, $7);
+                                 delctx(CTX_LOOP);
+                }
+
+       | FOR '(' identifier IN obj_ref ')' for_in_init stmt
+               { Buffer b2, b3;
+                 int tmp;
+
+                 $$ = $5;
+                 bufferWriteOp($$, SWFACTION_ENUMERATE);       
+
+                 b2 = newBuffer();
+                 bufferWriteSetRegister(b2, 0);
+                 bufferWriteOp(b2, SWFACTION_PUSHDATA);
+                 bufferWriteS16(b2, 1);
+                 bufferWriteU8(b2, 2);
+                 bufferWriteOp(b2, SWFACTION_NEWEQUALS);
+                 bufferWriteOp(b2, SWFACTION_BRANCHIFTRUE);
+                 bufferWriteS16(b2, 2);
+
+                 b3 = newBuffer();
+/* basically a lvalue could be used here rather than an ident !!! */
+/* probably by using reg1 for the test rather than reg0 */
+                 bufferWriteString(b3, $3, strlen($3)+1);
+                 bufferWriteRegister(b3, 0);
+                 bufferWriteOp(b3, SWFACTION_SETVARIABLE);
+                 bufferConcat(b3, $8);
+                 bufferWriteS16(b2, bufferLength(b3) + 5);
+                 tmp = bufferLength(b2) + bufferLength(b3) + 5;
+                 bufferConcat($$, b2);
+                 bufferWriteOp(b3, SWFACTION_BRANCHALWAYS);
+                 bufferWriteS16(b3, 2);
+                 bufferWriteS16(b3, -tmp);
+                 bufferResolveJumps(b3);
+                 bufferConcat($$, b3);
+                 delctx(CTX_FOR_IN); }
+
+       | FOR '(' VAR identifier IN obj_ref ')' for_in_init stmt
+               { Buffer b2, b3;
+                 int tmp;
+
+                 $$ = $6;
+                 bufferWriteOp($$, SWFACTION_ENUMERATE);       
+
+                 b2 = newBuffer();
+                 bufferWriteSetRegister(b2, 0);
+                 bufferWriteOp(b2, SWFACTION_PUSHDATA);
+                 bufferWriteS16(b2, 1);
+                 bufferWriteU8(b2, 2);
+                 bufferWriteOp(b2, SWFACTION_NEWEQUALS);
+                 bufferWriteOp(b2, SWFACTION_BRANCHIFTRUE);
+                 bufferWriteS16(b2, 2);
+                 // add size later
+
+                 b3 = newBuffer();
+                 bufferWriteString(b3, $4, strlen($4)+1);
+                 bufferWriteRegister(b3, 0);
+                 bufferWriteOp(b3, SWFACTION_VAREQUALS);
+                 bufferConcat(b3, $9);
+                 bufferWriteS16(b2, bufferLength(b3) + 5);
+                 tmp = bufferLength(b2) + bufferLength(b3) + 5;
+                 bufferConcat($$, b2);
+                 bufferWriteOp(b3, SWFACTION_BRANCHALWAYS);
+                 bufferWriteS16(b3, 2);
+                 bufferWriteS16(b3, -tmp);
+                 bufferResolveJumps(b3);
+                 bufferConcat($$, b3);
+                 delctx(CTX_FOR_IN); }
+       ;
+
+assign_stmts_opt
+       : /* empty */                           { $$ = NULL; }
+       | assign_stmts
+       ;
+
+// continue only makes sense if there is a CTX_LOOP or CTX_FOR_IN
+// on the stack
+cont_stmt
+       : CONTINUE ';'
+               { if(chkctx(CTX_CONTINUE) < 0)
+                       swf5error("continue outside loop");
+                 $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_BRANCHALWAYS);
+                 bufferWriteS16($$, 2);
+                 bufferWriteS16($$, MAGIC_CONTINUE_NUMBER); }
+       ;
+
+// break is possible if there is a CTX_LOOP, CTX_FOR_IN or CTX_SWITCH
+break_stmt
+       : BREAK ';'
+               { int tmp = chkctx(CTX_BREAK);
+                 if(tmp < 0)
+                       swf5error("break outside switch / loop");
+                 $$ = newBuffer();
+                 if(tmp)       /* break out of a for .. in */
+                       bufferWriteOp($$, SWFACTION_POP);
+                 bufferWriteOp($$, SWFACTION_BRANCHALWAYS);
+                 bufferWriteS16($$, 2);
+                 bufferWriteS16($$, MAGIC_BREAK_NUMBER); }
+       ;
+
+urlmethod
+       : /* empty */           { $$ = GETURL_METHOD_NOSEND; }
+
+       | ',' GET               { $$ = GETURL_METHOD_GET; }
+
+       | ',' POST              { $$ = GETURL_METHOD_POST; }
+
+       | ',' STRING            { if(strcmp($2, "GET") == 0)
+                                   $$ = GETURL_METHOD_GET;
+                                 else if(strcmp($2, "POST") == 0)
+                                   $$ = GETURL_METHOD_POST; }
+       ;
+
+level
+       : INTEGER
+               { char *lvlstring = (char*) malloc(12*sizeof(char));
+                 sprintf(lvlstring, "_level%d", $1);
+                 $$ = newBuffer();
+                 bufferWriteString($$, lvlstring, strlen(lvlstring)+1);
+                 free(lvlstring); }
+
+       | expr
+               { $$ = newBuffer();
+                 bufferWriteString($$, "_level", 7);
+                 bufferConcat($$, $1);
+                 bufferWriteOp($$, SWFACTION_STRINGCONCAT); }
+       ;
+
+void_function_call
+       : IDENTIFIER '(' expr_list ')'
+               { $$ = $3.buffer;
+                 bufferWriteInt($$, $3.count);
+                 bufferWriteString($$, $1, strlen($1)+1);
+                 bufferWriteOp($$, SWFACTION_CALLFUNCTION);
+                 bufferWriteOp($$, SWFACTION_POP);
+                 free($1); }
+
+       | DELETE IDENTIFIER
+               { $$ = newBuffer();
+                 bufferWriteString($$, $2, strlen($2)+1);
+                 free($2);
+                 bufferWriteOp($$, SWFACTION_DELETE); }
+
+       | DELETE lvalue_expr '.' IDENTIFIER
+               { $$ = $2;
+                 // bufferWriteOp($$, SWFACTION_GETVARIABLE);
+                 bufferWriteString($$, $4, strlen($4)+1);
+                 free($4);
+                 bufferWriteOp($$, SWFACTION_DELETEVAR); }
+
+       | DELETE lvalue_expr '[' expr ']'
+               { $$ = $2;
+                 // bufferWriteOp($$, SWFACTION_GETVARIABLE);
+                 bufferConcat($$, $4);
+                 // bufferWriteOp($$, SWFACTION_GETVARIABLE);
+                 bufferWriteOp($$, SWFACTION_DELETEVAR); }
+
+       | TRACE '(' expr_or_obj ')'
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_TRACE); }
+
+       | GETURL '(' expr ',' expr urlmethod ')'
+               { $$ = $3;
+                 bufferConcat($$, $5);
+                 bufferWriteOp($$, SWFACTION_GETURL2);
+                 bufferWriteS16($$, 1);
+                 bufferWriteU8($$, $6); }
+
+       | LOADVARIABLES '(' expr ',' expr urlmethod ')'
+               { $$ = $3;
+                 bufferConcat($$, $5);
+                 bufferWriteOp($$, SWFACTION_GETURL2);
+                 bufferWriteS16($$, 1);
+                 bufferWriteU8($$, 0xc0+$6); }
+
+       | LOADVARIABLESNUM '(' expr ',' level urlmethod ')'
+               { $$ = $3;
+                 bufferConcat($$, $5);
+                 bufferWriteOp($$, SWFACTION_GETURL2);
+                 bufferWriteS16($$, 1);
+                 bufferWriteU8($$, 0x80+$6); }
+
+       | LOADMOVIE '(' expr ',' expr urlmethod ')'
+               { $$ = $3;
+                 bufferConcat($$, $5);
+                 bufferWriteOp($$, SWFACTION_GETURL2);
+                 bufferWriteS16($$, 1);
+                 bufferWriteU8($$, 0x40+$6); }
+
+       | LOADMOVIENUM '(' expr ',' level urlmethod ')'
+               { $$ = $3;
+                 bufferConcat($$, $5);
+                 bufferWriteOp($$, SWFACTION_GETURL2);
+                 bufferWriteS16($$, 1);
+                 bufferWriteU8($$, $6); }
+
+       | CALLFRAME '(' expr ')'
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_CALLFRAME);
+                 bufferWriteS16($$, 0); }
+
+       /* startDrag(target, lock, [left, right, top, bottom]) */
+       | STARTDRAG '(' expr ',' expr ')'
+               { $$ = newBuffer();
+                 bufferWriteString($$, "0", 2); /* no constraint */
+                 bufferConcat($$, $5);
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_STARTDRAGMOVIE); }
+
+       | STARTDRAG '(' expr ',' expr ',' expr ',' expr ',' expr ',' expr ')'
+               { $$ = newBuffer();
+                 bufferConcat($$, $7);
+                 bufferConcat($$, $11);
+                 bufferConcat($$, $9);
+                 bufferConcat($$, $13);
+                 bufferWriteString($$, "1", 2); /* has constraint */
+                 bufferConcat($$, $5);
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_STARTDRAGMOVIE); }
+
+       | STOPDRAG '(' ')' /* no args */
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_STOPDRAGMOVIE); }
+
+       /* duplicateMovieClip(target, new, depth) */
+       | DUPLICATEMOVIECLIP '(' expr ',' expr ',' expr ')'
+               { $$ = $3;
+                 bufferConcat($$, $5);
+                 bufferConcat($$, $7);
+                 bufferWriteInt($$, 16384); /* magic number */
+                 bufferWriteOp($$, SWFACTION_ADD);
+                 bufferWriteOp($$, SWFACTION_DUPLICATECLIP); }
+
+       | REMOVEMOVIECLIP '(' expr ')'
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_REMOVECLIP); }
+
+       | GETURL1 '(' STRING ',' STRING ')'
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_GETURL);
+                 bufferWriteS16($$, strlen($3) + strlen($5) + 2);
+                 bufferWriteHardString($$, (byte*)$3, strlen($3));
+                 bufferWriteU8($$, 0);
+                 bufferWriteHardString($$, (byte*)$5, strlen($5));
+                 bufferWriteU8($$, 0); }
+
+       /* v3 actions */
+       | NEXTFRAME '(' ')'
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_NEXTFRAME); }
+               
+       | PREVFRAME '(' ')'
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_PREVFRAME); }
+
+       | PLAY '(' ')'
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_PLAY); }
+
+       | STOP '(' ')'
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_STOP); }
+
+       | STOPSOUNDS '(' ')'
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_STOPSOUNDS); }
+
+       | TOGGLEQUALITY '(' ')'
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_TOGGLEQUALITY); }
+
+       | GOTOFRAME '(' INTEGER ')'
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_GOTOFRAME);
+                 bufferWriteS16($$, 2);
+                 bufferWriteS16($$, $3); }
+
+       | GOTOFRAME '(' STRING ')'
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_GOTOLABEL);
+                 bufferWriteS16($$, strlen($3)+1);
+                 bufferWriteHardString($$, (byte*)$3, strlen($3)+1);
+                 free($3); }
+
+       | GOTOFRAME '(' expr ')'
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_GOTOEXPRESSION);
+                 bufferWriteS16($$, 1);
+                 bufferWriteU8($$, 0); } /* XXX - and stop */
+
+       | SETTARGET '(' STRING ')'
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_SETTARGET);
+                 bufferWriteS16($$, strlen($3)+1);
+                 bufferWriteHardString($$, (byte*)$3, strlen($3)+1);
+                 free($3); }
+
+       | SETTARGET '(' expr ')'
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_SETTARGETEXPRESSION); }
+
+
+       ;
+
+
+function_call
+       : IDENTIFIER '(' expr_list ')'
+               { $$ = $3.buffer;
+                 bufferWriteInt($$, $3.count);
+                 bufferWriteString($$, $1, strlen($1)+1);
+                 bufferWriteOp($$, SWFACTION_CALLFUNCTION);
+                 free($1); }
+
+       | EVAL '(' expr ')'
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_GETVARIABLE); }
+
+       | GETTIMER '(' ')'
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_GETTIMER); }
+
+       | RANDOM '(' expr ')'
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_RANDOM); }
+
+       | LENGTH '(' expr_or_obj ')'
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_STRINGLENGTH); }
+
+       | INT '(' expr ')'
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_INT); }
+
+       | ORD '(' expr ')'
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_ORD); }
+
+       | CHR '(' expr ')'
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_CHR); }
+
+       | CONCAT '(' expr ',' expr ')'
+               { $$ = $3;
+                 bufferConcat($$, $5);
+                 bufferWriteOp($$, SWFACTION_STRINGCONCAT); }
+
+       | SUBSTRING '(' expr ',' expr ',' expr ')'
+               { $$ = $3;
+                 bufferConcat($$, $5);
+                 bufferConcat($$, $7);
+                 bufferWriteOp($$, SWFACTION_SUBSTRING); }
+
+       | TYPEOF '(' expr_or_obj ')'
+               { $$ = $3;
+                 bufferWriteOp($$, SWFACTION_TYPEOF); }
+
+       ;
+
+
+expr_list
+       : /* empty */
+               { $$.buffer = newBuffer();
+                 $$.count = 0; }
+
+       | expr_or_obj
+               { $$.buffer = $1;
+                 $$.count = 1; }
+
+       /* goes backwards. rrgh. */
+       | expr_list ',' expr_or_obj
+               { Buffer tmp = newBuffer();
+                 bufferConcat(tmp, $3);
+                 bufferConcat(tmp, $$.buffer);
+                 $$.buffer = tmp;
+                 ++$$.count;  }
+       ;
+
+anon_function_decl
+       : function_init '(' formals_list ')' stmt
+               { $$ = newBuffer();
+                 bufferWriteOp($$, SWFACTION_DEFINEFUNCTION);
+                 bufferWriteS16($$, bufferLength($3.buffer) + 5);
+                 bufferWriteU8($$, 0); /* empty function name */
+                 bufferWriteS16($$, $3.count);
+                 bufferConcat($$, $3.buffer);
+                 bufferWriteS16($$, bufferLength($5));
+                 bufferConcat($$, $5);
+                 delctx(CTX_FUNCTION); }
+       ;
+
+method_call
+       : lvalue_expr '.' identifier '(' expr_list ')'
+               { $$ = $5.buffer;
+                 bufferWriteInt($$, $5.count);
+                 bufferConcat($$, $1);
+                 bufferWriteString($$, $3, strlen($3)+1);
+                 bufferWriteOp($$, SWFACTION_CALLMETHOD);
+                 free($3); }
+
+       | lvalue_expr '[' expr ']' '(' expr_list ')'
+               { $$ = $6.buffer;
+                 bufferWriteInt($$, $6.count);
+                 bufferConcat($$, $1);
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_CALLMETHOD); }
+       ;
+
+objexpr
+       : identifier ':' expr_or_obj
+               { $$ = newBuffer();
+                 bufferWriteString($$, $1, strlen($1)+1);
+                 bufferConcat($$, $3); }
+       ;
+
+objexpr_list
+       : objexpr
+               { $$.buffer = $1;
+                 $$.count = 1; }
+
+       | objexpr_list ',' objexpr
+               { bufferConcat($$.buffer, $3);
+                 ++$$.count;  }
+       ;
+
+assignop
+       : "+="          { $$ = SWFACTION_NEWADD; }
+       | "-="          { $$ = SWFACTION_SUBTRACT; }
+       | "*="          { $$ = SWFACTION_MULTIPLY; }
+       | "/="          { $$ = SWFACTION_DIVIDE; }
+       | "%="          { $$ = SWFACTION_MODULO; }
+       | "&="          { $$ = SWFACTION_BITWISEAND; }
+       | "|="          { $$ = SWFACTION_BITWISEOR; }
+       | "^="          { $$ = SWFACTION_BITWISEXOR; }
+       | "<<="         { $$ = SWFACTION_SHIFTLEFT; }
+       | ">>="         { $$ = SWFACTION_SHIFTRIGHT; }
+       | ">>>="        { $$ = SWFACTION_SHIFTRIGHT2; }
+       ;
+
+incdecop
+       : "++"          { $$ = SWFACTION_INCREMENT; }
+       | "--"          { $$ = SWFACTION_DECREMENT; }
+       ;
+
+
+/*
+integer
+       : '-' INTEGER %prec UMINUS      { $$ = -$2; }
+       | INTEGER                       { $$ = $1; }
+       ;
+
+double
+       : '-' DOUBLE %prec UMINUS       { $$ = -$2; }
+       | DOUBLE                        { $$ = $1; }
+       ;
+*/
+
+/* resolves an lvalue into a buffer */
+lvalue_expr
+       : lvalue
+               { if($1.obj)
+                 {
+                   $$ = $1.obj;
+
+                   if($1.ident)
+                     bufferConcat($$, $1.ident);
+                   else
+                     bufferConcat($$, $1.memexpr);
+
+                   bufferWriteOp($$, SWFACTION_GETMEMBER);
+                 }
+                 else
+                 {
+                   $$ = $1.ident;
+                   bufferWriteOp($$, SWFACTION_GETVARIABLE);
+                 }
+               }
+       | function_call
+       | method_call
+       ;
+
+/* lvalue - things you can assign to */
+lvalue
+       : identifier
+               { $$.ident = newBuffer();
+                 bufferWriteString($$.ident, $1, strlen($1)+1);
+                 free($1);
+                 $$.obj = 0;
+                 $$.memexpr = 0; }
+
+       | lvalue_expr '.' identifier %prec '.'
+               { $$.obj = $1;
+                 $$.ident = newBuffer();
+                 bufferWriteString($$.ident, $3, strlen($3)+1);
+                 $$.memexpr = 0; }
+
+       | lvalue_expr '[' expr ']' %prec '.'
+               { $$.obj = $1;
+                 $$.memexpr = $3;
+                 $$.ident = 0; }
+       ;
+
+/* these leave a value on the stack */
+
+expr
+       : primary
+
+       | '-' expr %prec UMINUS
+               { $$ = $2;
+                 bufferWriteInt($2, -1);
+                 bufferWriteOp($2, SWFACTION_MULTIPLY); }
+
+       | '~' expr %prec UMINUS
+               { $$ = $2;
+                 bufferWriteInt($2, 0xffffffff);
+                 bufferWriteOp($2, SWFACTION_BITWISEXOR); }
+
+       | '!' expr
+               { $$ = $2;
+                 bufferWriteOp($2, SWFACTION_LOGICALNOT); }
+
+       | expr "||" expr
+               { $$ = $1;
+                 bufferWriteOp($$, SWFACTION_DUP);
+                 bufferWriteOp($$, SWFACTION_BRANCHIFTRUE);
+                 bufferWriteS16($$, 2);
+                 bufferWriteS16($$, bufferLength($3)+1);
+                 bufferWriteOp($$, SWFACTION_POP);
+                 bufferConcat($$, $3); }
+
+       | expr "&&" expr
+               { $$ = $1;
+                 bufferWriteOp($$, SWFACTION_DUP);
+                 bufferWriteOp($$, SWFACTION_LOGICALNOT);
+                 bufferWriteOp($$, SWFACTION_BRANCHIFTRUE);
+                 bufferWriteS16($$, 2);
+                 bufferWriteS16($$, bufferLength($3)+1);
+                 bufferWriteOp($$, SWFACTION_POP);
+                 bufferConcat($$, $3); }
+
+       | expr '*' expr
+               { $$ = $1;
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_MULTIPLY); }
+
+       | expr '/' expr
+               { $$ = $1;
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_DIVIDE); }
+
+       | expr '%' expr
+               { $$ = $1;
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_MODULO); }
+
+       | expr '+' expr
+               { $$ = $1;
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_NEWADD); }
+
+       | expr '-' expr
+               { $$ = $1;
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_SUBTRACT); }
+
+       | expr '&' expr
+               { $$ = $1;
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_BITWISEAND); }
+
+       | expr '|' expr
+               { $$ = $1;
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_BITWISEOR); }
+
+       | expr '^' expr
+               { $$ = $1;
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_BITWISEXOR); }
+
+       | expr '<' expr
+               { $$ = $1;
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_NEWLESSTHAN); }
+
+       | expr '>' expr
+               { $$ = $3;
+                 bufferConcat($$, $1);
+                 bufferWriteOp($$, SWFACTION_NEWLESSTHAN); }
+
+       | expr "<=" expr
+               { $$ = $3;
+                 bufferConcat($$, $1);
+                 bufferWriteOp($$, SWFACTION_NEWLESSTHAN);
+                 bufferWriteOp($$, SWFACTION_LOGICALNOT); }
+
+       | expr ">=" expr
+               { bufferConcat($1, $3);
+                 bufferWriteOp($1, SWFACTION_NEWLESSTHAN);
+                 bufferWriteOp($1, SWFACTION_LOGICALNOT); }
+
+       | expr "==" expr
+               { bufferConcat($1, $3);
+                 bufferWriteOp($1, SWFACTION_NEWEQUALS); }
+
+       | expr "!=" expr
+               { bufferConcat($1, $3);
+                 bufferWriteOp($1, SWFACTION_NEWEQUALS);
+                 bufferWriteOp($1, SWFACTION_LOGICALNOT); }
+
+       | expr "<<" expr
+               { bufferConcat($1, $3);
+                 bufferWriteOp($1, SWFACTION_SHIFTLEFT); }
+
+       | expr ">>" expr
+               { bufferConcat($1, $3);
+                 bufferWriteOp($1, SWFACTION_SHIFTRIGHT); }
+
+       | expr ">>>" expr
+               { bufferConcat($1, $3);
+                 bufferWriteOp($1, SWFACTION_SHIFTRIGHT2); }
+
+       | expr '?' expr ':' expr
+               { bufferWriteOp($1, SWFACTION_BRANCHIFTRUE);
+                 bufferWriteS16($1, 2);
+                 bufferWriteS16($1, bufferLength($5)+5);
+                 bufferConcat($1, $5);
+                 bufferWriteOp($1, SWFACTION_BRANCHALWAYS);
+                 bufferWriteS16($1, 2);
+                 bufferWriteS16($1, bufferLength($3));
+                 bufferConcat($1, $3); }
+
+       | lvalue '=' expr_or_obj
+               { if($1.obj) /* obj[memexpr] or obj.ident */
+                 {
+                   $$ = $1.obj;
+
+                   if($1.ident)
+                     bufferConcat($$, $1.ident);
+                   else
+                     bufferConcat($$, $1.memexpr);
+
+                   bufferConcat($$, $3);
+                   bufferWriteSetRegister($$, 0);
+                   bufferWriteOp($$, SWFACTION_SETMEMBER);
+                   bufferWriteRegister($$, 0);
+                 }
+                 else /* just ident */
+                 {
+                   $$ = $3;
+                   bufferWriteOp($$, SWFACTION_DUP);
+                   bufferConcat($$, $1.ident);
+                   bufferWriteOp($$, SWFACTION_SWAP);
+                   bufferWriteOp($$, SWFACTION_SETVARIABLE);
+                 }
+/* tricky case missing here: lvalue ASSIGN expr */
+/* like in x = y += z; */
+               }
+
+       | expr INSTANCEOF lvalue_expr
+               { $$ = $1;
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_INSTANCEOF); }
+
+       ;
+
+expr_or_obj
+       : expr
+
+       | NEW identifier
+               { $$ = newBuffer();
+                 bufferWriteInt($$, 0);
+                 bufferWriteString($$, $2, strlen($2)+1);
+                 bufferWriteOp($$, SWFACTION_NEW); }
+
+       | NEW identifier '(' expr_list ')'
+               { $$ = $4.buffer;
+                 bufferWriteInt($$, $4.count);
+                 bufferWriteString($$, $2, strlen($2)+1);
+                 bufferWriteOp($$, SWFACTION_NEW); }
+
+       | '[' expr_list ']'
+               { $$ = $2.buffer;
+                 bufferWriteInt($$, $2.count);
+                 bufferWriteOp($$, SWFACTION_INITARRAY); }
+
+       | emptybraces
+               { $$ = newBuffer();
+                 bufferWriteInt($$, 0);
+                 bufferWriteOp($$, SWFACTION_INITOBJECT); }
+
+       | '{' objexpr_list '}'
+               { $$ = $2.buffer;
+                 bufferWriteInt($$, $2.count);
+                 bufferWriteOp($$, SWFACTION_INITOBJECT); }
+
+       ;
+
+primary
+       : function_call
+
+       | anon_function_decl
+
+       | method_call
+
+       | lvalue_expr
+
+       | incdecop lvalue %prec "++"
+               { if($2.obj)
+                 {
+                   if($2.ident)        // expr . identifier
+                   {
+                     $$ = $2.obj;
+                     bufferWriteOp($$, SWFACTION_DUP);       /* a, a */
+                     bufferWriteBuffer($$, $2.ident);        /* a, a, i */
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i, a */
+                     bufferConcat($$, $2.ident);             /* a, i, a, i */
+                     bufferWriteOp($$, SWFACTION_GETMEMBER);
+                     bufferWriteOp($$, $1);
+                     bufferWriteSetRegister($$, 0);
+                     bufferWriteOp($$, SWFACTION_SETMEMBER); /* a.i = a.i+1 */
+                     bufferWriteRegister($$, 0);             /* a.i+1 */
+                   }
+                   else        // expr [ expr ]
+                   {
+                     $$ = $2.memexpr;                        /* i */
+                     bufferConcat($$, $2.obj);               /* i, a */
+                     bufferWriteSetRegister($$, 0);    /* ($2.memexpr can use reg0) */
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i */
+                     bufferWriteOp($$, SWFACTION_DUP);       /* a, i, i */
+                     bufferWriteRegister($$, 0);             /* a, i, i, a */
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i, a, i */
+                     bufferWriteOp($$, SWFACTION_GETMEMBER); /* a, i, a[i] */
+                     bufferWriteOp($$, $1);                  /* a, i, a[i]+1 */
+                     bufferWriteSetRegister($$, 0);
+                     bufferWriteOp($$, SWFACTION_SETMEMBER); /* a[i] = a[i]+1 */
+                     bufferWriteRegister($$, 0);             /* a[i]+1 */
+                   }
+                 }
+                 else  // identifier
+                 {
+                   $$ = newBuffer();
+                   bufferWriteBuffer($$, $2.ident);
+                   bufferWriteOp($$, SWFACTION_GETVARIABLE);
+                   bufferWriteOp($$, $1);
+                   bufferWriteOp($$, SWFACTION_DUP);
+                   bufferConcat($$, $2.ident);
+                   bufferWriteOp($$, SWFACTION_SWAP);
+                   bufferWriteOp($$, SWFACTION_SETVARIABLE);
+                 }
+               }
+
+       | lvalue incdecop %prec POSTFIX
+               { if($1.obj)
+                 {
+                   if($1.ident)
+                   {
+                     $$ = $1.obj;                            /* a */
+                     bufferWriteOp($$, SWFACTION_DUP);       /* a, a */
+                     bufferWriteBuffer($$, $1.ident);        /* a, a, i */
+                     bufferWriteOp($$, SWFACTION_GETMEMBER); /* a, a.i */
+                     bufferWriteSetRegister($$, 0);
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a.i, a */
+                     bufferConcat($$, $1.ident);             /* a.i, a, i */
+                     bufferWriteRegister($$, 0);             /* a.i, a, i, a.i */
+                     bufferWriteOp($$, $2);                  /* a.i, a, i, a.i+1 */
+                     bufferWriteOp($$, SWFACTION_SETMEMBER);
+                   }
+                   else
+                   {
+                     $$ = $1.memexpr;
+                     bufferConcat($$, $1.obj);               /* i, a */
+                     bufferWriteSetRegister($$, 0);
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i */
+                     bufferWriteOp($$, SWFACTION_DUP);       /* a, i, i */
+                     bufferWriteRegister($$, 0);             /* a, i, i, a */
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i, a, i */
+                     bufferWriteOp($$, SWFACTION_GETMEMBER); /* a, i, a[i] */
+                     bufferWriteSetRegister($$, 0);
+                     bufferWriteOp($$, $2);                  /* a, i, a[i]+1 */
+                     bufferWriteOp($$, SWFACTION_SETMEMBER);
+                     bufferWriteRegister($$, 0);             /* a[i] */
+                   }
+                 }
+                 else
+                 {
+                   $$ = newBuffer();
+                   bufferWriteBuffer($$, $1.ident);
+                   bufferWriteOp($$, SWFACTION_GETVARIABLE);
+                   bufferWriteOp($$, SWFACTION_DUP);
+                   bufferWriteOp($$, $2);
+                   bufferConcat($$, $1.ident);
+                   bufferWriteOp($$, SWFACTION_SWAP);
+                   bufferWriteOp($$, SWFACTION_SETVARIABLE);
+                 }
+               }
+
+       | '(' expr ')'
+               { $$ = $2; }
+
+       | '-' INTEGER %prec UMINUS
+               { $$ = newBuffer();
+                 bufferWriteInt($$, -$2); }
+
+       | INTEGER
+               { $$ = newBuffer();
+                 bufferWriteInt($$, $1); }
+
+       | '-' DOUBLE %prec UMINUS
+               { $$ = newBuffer();
+                 bufferWriteDouble($$, -$2); }
+
+       | DOUBLE
+               { $$ = newBuffer();
+                 bufferWriteDouble($$, $1); }
+
+       | BOOLEAN
+               { $$ = newBuffer();
+                 bufferWriteBoolean($$, $1); }
+
+       | NULLVAL
+               { $$ = newBuffer();
+                 bufferWriteNull($$); }
+
+       | STRING
+               { $$ = newBuffer();
+                 bufferWriteString($$, $1, strlen($1)+1);
+                 free($1); }
+       ;
+
+init_vars
+       : init_var
+
+       | init_vars ',' init_var
+               { $$ = $1;
+                 bufferConcat($$, $3); }
+       ;
+
+init_var
+       : identifier '=' expr_or_obj
+               { $$ = newBuffer();
+                 bufferWriteString($$, $1, strlen($1)+1);
+                 bufferConcat($$, $3);
+                 bufferWriteOp($$, SWFACTION_VAREQUALS); }
+
+       | identifier
+               { $$ = newBuffer();
+                 bufferWriteString($$, $1, strlen($1)+1);
+                 bufferWriteOp($$, SWFACTION_VAR); }
+       ;
+
+assign_stmt
+       : ASM '{'
+               { asmBuffer = newBuffer(); }
+         opcode_list '}'
+               { $$ = asmBuffer; }
+
+       | VAR init_vars
+               { $$ = $2; }
+
+       | void_function_call
+
+       | function_call
+               { $$ = $1;
+                 bufferWriteOp($$, SWFACTION_POP); }
+
+       | method_call
+               { $$ = $1;
+                 bufferWriteOp($$, SWFACTION_POP); }
+
+       | incdecop lvalue %prec INCR
+               { if($2.obj)
+                 {
+                   if($2.ident)
+                   {
+                     $$ = $2.obj;                            /* a */
+                     bufferWriteOp($$, SWFACTION_DUP);       /* a, a */
+                     bufferWriteBuffer($$, $2.ident);        /* a, a, i */
+                     bufferWriteOp($$, SWFACTION_GETMEMBER); /* a, a.i */
+                     bufferWriteOp($$, $1);                  /* a, a.i+1 */
+                     bufferConcat($$, $2.ident);             /* a, a.i+1, i */
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i, a.i+1 */
+                     bufferWriteOp($$, SWFACTION_SETMEMBER); /* a.i = a.i+1 */
+                   }
+                   else
+                   {
+                     /* weird contortions so that $2.memexpr can use reg 0 */
+                     $$ = $2.memexpr;                        /* i */
+                     bufferConcat($$, $2.obj);               /* i, a */
+                     bufferWriteSetRegister($$, 0);
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i */
+                     bufferWriteOp($$, SWFACTION_DUP);       /* a, i, i */
+                     bufferWriteRegister($$, 0);             /* a, i, i, a */
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i, a, i */
+                     bufferWriteOp($$, SWFACTION_GETMEMBER); /* a, i, a[i] */
+                     bufferWriteOp($$, $1);                  /* a, i, a[i]+1 */
+                     bufferWriteOp($$, SWFACTION_SETMEMBER); /* a[i] = a[i]+1 */
+                   }
+                 }
+                 else
+                 {
+                   $$ = $2.ident;
+                   bufferWriteOp($$, SWFACTION_DUP);
+                   bufferWriteOp($$, SWFACTION_GETVARIABLE);
+                   bufferWriteOp($$, $1);
+                   bufferWriteOp($$, SWFACTION_SETVARIABLE);
+                 }
+               }
+
+       | lvalue incdecop %prec POSTFIX
+               { if($1.obj)
+                 {
+                   if($1.ident)
+                   {
+                     $$ = $1.obj;                            /* a */
+                     bufferWriteOp($$, SWFACTION_DUP);       /* a, a */
+                     bufferWriteBuffer($$, $1.ident);        /* a, a, i */
+                     bufferWriteOp($$, SWFACTION_GETMEMBER); /* a, a.i */
+                     bufferWriteOp($$, $2);                  /* a, a.i+1 */
+                     bufferConcat($$, $1.ident);             /* a, a.i+1, i */
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i, a.i+1 */
+                     bufferWriteOp($$, SWFACTION_SETMEMBER); /* a.i = a.i+1 */
+                   }
+                   else
+                   {
+                     /* weird contortions so that $1.memexpr can use reg 0 */
+                     $$ = $1.memexpr;  /* i */
+                     bufferConcat($$, $1.obj);               /* i, a */
+                     bufferWriteSetRegister($$, 0);
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i */
+                     bufferWriteOp($$, SWFACTION_DUP);       /* a, i, i */
+                     bufferWriteRegister($$, 0);             /* a, i, i, a */
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i, a, i */
+                     bufferWriteOp($$, SWFACTION_GETMEMBER); /* a, i, a[i] */
+                     bufferWriteOp($$, $2);                  /* a, i, a[i]+1 */
+                     bufferWriteOp($$, SWFACTION_SETMEMBER); /* a[i] = a[i]+1 */
+                   }
+                 }
+                 else
+                 {
+                   $$ = $1.ident;
+                   bufferWriteOp($$, SWFACTION_DUP);   
+                   bufferWriteOp($$, SWFACTION_GETVARIABLE);
+                   bufferWriteOp($$, $2);
+                   bufferWriteOp($$, SWFACTION_SETVARIABLE);
+                 }
+               }
+
+       | lvalue '=' expr_or_obj
+               { if($1.obj)
+                 {
+                   $$ = $1.obj;
+
+                   if($1.ident)
+                     bufferConcat($$, $1.ident);
+                   else
+                     bufferConcat($$, $1.memexpr);
+
+                   bufferConcat($$, $3);
+                   bufferWriteOp($$, SWFACTION_SETMEMBER);
+                 }
+                 else
+                 {
+                   $$ = $1.ident;
+                   bufferConcat($$, $3);
+                   bufferWriteOp($$, SWFACTION_SETVARIABLE);
+                 }
+               }
+
+       | lvalue assignop expr
+               { if($1.obj)
+                 {
+                   if($1.ident)
+                   {
+                     $$ = $1.obj;                            /* a */
+                     bufferWriteOp($$, SWFACTION_DUP);       /* a, a */
+                     bufferWriteBuffer($$, $1.ident);        /* a, a, i */
+                     bufferWriteOp($$, SWFACTION_GETMEMBER); /* a, a.i */
+                     bufferConcat($$, $3);                   /* a, a.i, v */
+                     bufferWriteOp($$, $2);                  /* a, a.i+v */
+                     bufferConcat($$, $1.ident);             /* a, a.i+v, i */
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i, a.i+v */
+                     bufferWriteOp($$, SWFACTION_SETMEMBER); /* a.i = a.i+v */
+                   }
+                   else
+                   {
+                     $$ = $1.memexpr;                        /* i */
+                     bufferConcat($$, $1.obj);               /* i, a */
+                     bufferWriteSetRegister($$, 0);
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i */
+                     bufferWriteOp($$, SWFACTION_DUP);       /* a, i, i */
+                     bufferWriteRegister($$, 0);             /* a, i, i, a */
+                     bufferWriteOp($$, SWFACTION_SWAP);      /* a, i, a, i */
+                     bufferWriteOp($$, SWFACTION_GETMEMBER); /* a, i, a[i] */
+                     bufferConcat($$, $3);                   /* a, i, a[i], v */
+                     bufferWriteOp($$, $2);                  /* a, i, a[i]+v */
+                     bufferWriteOp($$, SWFACTION_SETMEMBER); /* a[i] = a[i]+v */
+                   }
+                 }
+                 else
+                 {
+                   $$ = $1.ident;
+                   bufferWriteOp($$, SWFACTION_DUP);
+                   bufferWriteOp($$, SWFACTION_GETVARIABLE);
+                   bufferConcat($$, $3);
+                   bufferWriteOp($$, $2);
+                   bufferWriteOp($$, SWFACTION_SETVARIABLE);
+                 }
+               }
+       ;
+
+/* assembler stuff */
+
+opcode_list
+       : opcode
+       | opcode_list opcode    { $$ = $1 + $2; }
+       ;
+
+with
+       : WITH
+                               { $$ = bufferWriteOp(asmBuffer,
+                                                    SWFACTION_WITH); }
+         opcode_list END       { $$ = $<len>2 + $3;
+                                 bufferPatchLength(asmBuffer, $3); }
+       ;
+
+push_item
+       : STRING                { $$ = bufferWriteConstantString(asmBuffer,(byte*) $1,
+                                                                strlen($1)+1); }
+
+       | INTEGER               { bufferWriteU8(asmBuffer, PUSH_INT);
+                                 $$ = bufferWriteInt(asmBuffer, $1)+1; }
+
+       | DOUBLE                { bufferWriteU8(asmBuffer, PUSH_DOUBLE);
+                                 $$ = bufferWriteDouble(asmBuffer, $1)+1; }
+
+       | BOOLEAN               { bufferWriteU8(asmBuffer, PUSH_BOOLEAN);
+                                 $$ = bufferWriteU8(asmBuffer, $1)+1; }
+
+       | NULLVAL               { $$ = bufferWriteU8(asmBuffer, PUSH_NULL); }
+
+       | REGISTER              { bufferWriteU8(asmBuffer, PUSH_REGISTER);
+                                 $$ = bufferWriteU8(asmBuffer,
+                                                    (char)atoi($1))+1; }
+       ;
+
+
+push_list
+       : push_item                     { $$ = $1; }
+       | push_list ',' push_item       { $$ += $3; }
+       ;
+
+opcode
+       : PUSH                  { $$ = bufferWriteOp(asmBuffer,
+                                                    SWFACTION_PUSHDATA);
+                                 $$ += bufferWriteS16(asmBuffer, 0); }
+         push_list             { $$ = $<len>2 + $3;
+                                 bufferPatchLength(asmBuffer, $3); }
+
+       | with
+
+       | SETREGISTER REGISTER
+                               { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_SETREGISTER);
+                                 $$ += bufferWriteS16(asmBuffer, 1);
+                                 $$ += bufferWriteU8(asmBuffer,
+                                                     (char)atoi($2)); }
+       /* no args */
+       | CALLFUNCTION          { $$ = bufferWriteOp(asmBuffer,
+                                                    SWFACTION_CALLFUNCTION); }
+       | RETURN                { $$ = bufferWriteOp(asmBuffer,
+                                                    SWFACTION_RETURN); }
+       | CALLMETHOD    { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_CALLMETHOD); }
+       | AND                   { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_BITWISEAND); }
+       | OR                    { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_BITWISEOR); }
+       | XOR                   { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_BITWISEXOR); }
+       | MODULO                { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_MODULO); }
+       | ADD                   { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_NEWADD); }
+       | LESSTHAN              { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_NEWLESSTHAN); }
+       | EQUALS                { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_NEWEQUALS); }
+       | INC                   { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_INCREMENT); }
+       | DEC                   { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_DECREMENT); }
+       | TYPEOF                { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_TYPEOF); }
+       | INSTANCEOF    { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_INSTANCEOF); }
+       | ENUMERATE             { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_ENUMERATE); }
+       | DELETE                { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_DELETE); }
+       | NEW                   { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_NEW); }
+       | INITARRAY             { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_INITARRAY); }
+       | INITOBJECT            { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_INITOBJECT); }
+       | GETMEMBER             { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_GETMEMBER); }
+       | SETMEMBER             { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_SETMEMBER); }
+       | SHIFTLEFT             { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_SHIFTLEFT); }
+       | SHIFTRIGHT            { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_SHIFTRIGHT); }
+       | SHIFTRIGHT2           { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_SHIFTRIGHT2); }
+       | VAR                   { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_VAR); }
+       | VAREQUALS             { $$ = bufferWriteOp(asmBuffer, 
+                                                    SWFACTION_VAREQUALS); }
+
+       /* f4 ops */
+       | OLDADD                { $$ = bufferWriteOp(asmBuffer, SWFACTION_ADD); }
+       | SUBTRACT              { $$ = bufferWriteOp(asmBuffer, SWFACTION_SUBTRACT); }
+       | MULTIPLY              { $$ = bufferWriteOp(asmBuffer, SWFACTION_MULTIPLY); }
+       | DIVIDE                { $$ = bufferWriteOp(asmBuffer, SWFACTION_DIVIDE); }
+       | OLDEQUALS             { $$ = bufferWriteOp(asmBuffer, SWFACTION_EQUAL); }
+       | OLDLESSTHAN           { $$ = bufferWriteOp(asmBuffer, SWFACTION_LESSTHAN); }
+       | LOGICALAND            { $$ = bufferWriteOp(asmBuffer, SWFACTION_LOGICALAND); }
+       | LOGICALOR             { $$ = bufferWriteOp(asmBuffer, SWFACTION_LOGICALOR); }
+       | NOT                   { $$ = bufferWriteOp(asmBuffer, SWFACTION_LOGICALNOT); }
+       | STRINGEQ              { $$ = bufferWriteOp(asmBuffer, SWFACTION_STRINGEQ); }
+       | STRINGLENGTH          { $$ = bufferWriteOp(asmBuffer, SWFACTION_STRINGLENGTH); }
+       | SUBSTRING             { $$ = bufferWriteOp(asmBuffer, SWFACTION_SUBSTRING); }
+       | INT                   { $$ = bufferWriteOp(asmBuffer, SWFACTION_INT); }
+       | DUP                   { $$ = bufferWriteOp(asmBuffer, SWFACTION_DUP); }
+       | SWAP                  { $$ = bufferWriteOp(asmBuffer, SWFACTION_SWAP); }
+       | POP                   { $$ = bufferWriteOp(asmBuffer, SWFACTION_POP); }
+       | GETVARIABLE           { $$ = bufferWriteOp(asmBuffer, SWFACTION_GETVARIABLE); }
+       | SETVARIABLE           { $$ = bufferWriteOp(asmBuffer, SWFACTION_SETVARIABLE); }
+       | SETTARGETEXPRESSION   { $$ = bufferWriteOp(asmBuffer, SWFACTION_SETTARGETEXPRESSION); }
+       | CONCAT                { $$ = bufferWriteOp(asmBuffer, SWFACTION_STRINGCONCAT); }
+       | DUPLICATEMOVIECLIP    { $$ = bufferWriteOp(asmBuffer, SWFACTION_DUPLICATECLIP); }
+       | REMOVEMOVIECLIP       { $$ = bufferWriteOp(asmBuffer, SWFACTION_REMOVECLIP); }
+       | TRACE                 { $$ = bufferWriteOp(asmBuffer, SWFACTION_TRACE); }
+       | STRINGLESSTHAN        { $$ = bufferWriteOp(asmBuffer, SWFACTION_STRINGCOMPARE); }
+       | RANDOM                { $$ = bufferWriteOp(asmBuffer, SWFACTION_RANDOM); }
+       | MBLENGTH              { $$ = bufferWriteOp(asmBuffer, SWFACTION_MBLENGTH); }
+       | ORD                   { $$ = bufferWriteOp(asmBuffer, SWFACTION_ORD); }
+       | CHR                   { $$ = bufferWriteOp(asmBuffer, SWFACTION_CHR); }
+       | GETTIMER              { $$ = bufferWriteOp(asmBuffer, SWFACTION_GETTIMER); }
+       | MBSUBSTRING           { $$ = bufferWriteOp(asmBuffer, SWFACTION_MBSUBSTRING); }
+       | MBORD                 { $$ = bufferWriteOp(asmBuffer, SWFACTION_MBORD); }
+       | MBCHR                 { $$ = bufferWriteOp(asmBuffer, SWFACTION_MBCHR); }
+
+       /* with args */
+       | BRANCHALWAYS STRING   { $$ = bufferWriteOp(asmBuffer, SWFACTION_BRANCHALWAYS);
+                                 $$ += bufferWriteS16(asmBuffer, 2);
+                                 $$ += bufferBranchTarget(asmBuffer, $2); }
+
+       | BRANCHIFTRUE STRING   { $$ = bufferWriteOp(asmBuffer, SWFACTION_BRANCHIFTRUE);
+                                 $$ += bufferWriteS16(asmBuffer, 2);
+                                 $$ += bufferBranchTarget(asmBuffer, $2); }
+       ;
+
+%%
+