/* $Id: swf5compiler.y,v 1.50 2007/03/21 22:09:05 krechert Exp $ */

%start program

%{

#include <time.h>
#include <string.h>
#include <stdlib.h>
#include "compile.h"
#include "actiontypes.h"
#include "assembler.h"

#define YYPARSE_PARAM buffer
//#define DEBUG 1

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 TARGETPATH 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 GOTOANDSTOP GOTOANDPLAY SETTARGET 
%token GETPROPERTY SETPROPERTY TONUMBER TOSTRING

%token TRY THROW CATCH FINALLY

%token EXTENDS

%token NULLVAL
%token UNDEFINED
%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 EEQ "==="
%token NEE "!=="
%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> try_catch_stmt throw_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 inpart 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
	| try_catch_stmt ';'
	| throw_stmt
	;

throw_stmt
	: THROW expr_or_obj ';'		{ $$ = $2; bufferWriteOp($$, SWFACTION_THROW); }
	;

try_catch_stmt
	: TRY stmt					{ 	$$ = newBuffer();
									bufferWriteOp($$, SWFACTION_TRY);
									bufferWriteS16($$, bufferLength($2)+8);
									bufferWriteU8($$, 0);
									bufferWriteS16($$, bufferLength($2));
									bufferWriteS16($$, 0);
									bufferWriteS16($$, 0);
									bufferWriteU8($$, 0); /* catch name here? - empty string */
									bufferConcat($$, $2);
								 }
	| TRY stmt CATCH '(' identifier ')' stmt		{ $$ = newBuffer();
									bufferWriteOp($$, SWFACTION_TRY);
									bufferWriteS16($$, bufferLength($2)+strlen($5)+bufferLength($7)+8);
									bufferWriteU8($$, 0);
									bufferWriteS16($$, bufferLength($2));
									bufferWriteS16($$, bufferLength($7));
									bufferWriteS16($$, 0);
									bufferWriteHardString($$, $5, strlen($5)+1);
									bufferConcat($$, $2);
									bufferConcat($$, $7);
								}
	| TRY stmt FINALLY stmt		{	$$ = newBuffer();
									bufferWriteOp($$, SWFACTION_TRY);
									bufferWriteS16($$, bufferLength($2)+bufferLength($4)+8);
									bufferWriteU8($$, 0);
									bufferWriteS16($$, bufferLength($2));
									bufferWriteS16($$, bufferLength($4));
									bufferWriteS16($$, 0);
									bufferWriteU8($$, 0); /* catch name here? - empty string */
									bufferConcat($$, $2);
									bufferConcat($$, $4);
								 }
	| TRY stmt CATCH '(' identifier ')' stmt FINALLY stmt	{ $$ = newBuffer();
									bufferWriteOp($$, SWFACTION_TRY);
									bufferWriteS16($$, bufferLength($2)+strlen($5)+bufferLength($7)+bufferLength($9)+8);
									bufferWriteU8($$, 0);
									bufferWriteS16($$, bufferLength($2));
									bufferWriteS16($$, bufferLength($7));
									bufferWriteS16($$, bufferLength($9));
									bufferWriteHardString($$, $5, strlen($5)+1);
									bufferConcat($$, $2);
									bufferConcat($$, $7);
									bufferConcat($$, $9);
								}
	;

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_IF);
		  bufferWriteS16($$, 2);
		  bufferWriteS16($$, bufferLength($7)+5);
		  bufferConcat($$, $7);
		  bufferWriteOp($$, SWFACTION_JUMP);
		  bufferWriteS16($$, 2);
		  bufferWriteS16($$, bufferLength($5));
		  bufferConcat($$, $5); }

	| IF '(' expr ')' stmt		%prec NOELSE
		{ $$ = $3;
		  bufferWriteOp($$, SWFACTION_LOGICALNOT);
		  bufferWriteOp($$, SWFACTION_IF);
		  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.. */
/* What is this for exactly ? */

identifier
	: IDENTIFIER
	| NEW		{ $$ = strdup("new"); }
	| DELETE	{ $$ = strdup("delete"); }
	| TARGETPATH	{ $$ = strdup("targetPath"); }
	| 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"); }
	| EXTENDS	{ $$ = strdup("extends"); }
	| GOTOANDSTOP	{ $$ = strdup("gotoAndStop"); }
	| GOTOANDPLAY	{ $$ = strdup("gotoAndPlay"); }
	;

formals_list
	: /* empty */
		{ $$.buffer = newBuffer();
		  $$.count = 0; }

	| identifier
		{ $$.buffer = newBuffer();
		  bufferWriteHardString($$.buffer, $1, strlen($1)+1);
		  $$.count = 1;
		  free($1); }

	| formals_list ',' identifier
		{ $$ = $1;
		  bufferWriteHardString($$.buffer, $3, strlen($3)+1);
		  ++$$.count;
		  free($3); }
	;

function_init
	: FUNCTION
		{ addctx(CTX_FUNCTION); }
	;

function_decl
	: function_init identifier '(' formals_list ')' stmt
	{
		$$ = newBuffer();
		if(swfVersion > 6)
		{
			// TODO: let user control which flags to use ?
			// Don't preload any variable in registers, or we'll need to track all uses of 
			// those variables in this function context turning them into register accesses
			int flags = 0;
			int num_regs = 0;
			bufferWriteDefineFunction2($$, $2, $4.buffer, $6, flags, num_regs);
		}
		else
		{
			bufferWriteOp($$, SWFACTION_DEFINEFUNCTION);
			bufferWriteS16($$, strlen($2) +
				bufferLength($4.buffer) + 5);
			bufferWriteHardString($$, $2, strlen($2)+1);
			bufferWriteS16($$, $4.count);
			bufferConcat($$, $4.buffer);
			bufferWriteS16($$, bufferLength($6));
			bufferConcat($$, $6);
			delctx(CTX_FUNCTION);
			free($2); // should be done for function2 as well ?
		}
	}
	;

inpart
	: IN obj_ref
		{ $$ = $2; }
	;

obj_ref
	: lvalue
		{ if($1.obj)
		  {
		    $$ = $1.obj;

		    if($1.ident)
		      bufferConcat($$, $1.ident);
		    else
		      bufferConcat($$, $1.memexpr);

		    bufferWriteOp($$, SWFACTION_GETMEMBER);
		  }
		  else
		  {
		    $$ = $1.ident;
		  }
		}
	| 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_IF);
		  bufferWriteS16($$, 2);
		  bufferWriteS16($$, bufferLength($5)+5);
		  bufferConcat($$, $5);
		  bufferWriteOp($$, SWFACTION_JUMP);
		  bufferWriteS16($$, 2);
		  bufferWriteS16($$, -(bufferLength($$)+2));
		  bufferResolveJumps($$);
		  delctx(CTX_LOOP); }

	| do_init stmt WHILE '(' expr ')'
		{ if($2)
			{	$$ = $2;
		  		bufferConcat($$, $5);
			}
			else
				$$ = $5;
		  bufferWriteOp($$, SWFACTION_IF);
		  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_JUMP);
                    bufferWriteS16($$, 2);
                    bufferWriteS16($$, bufferLength($7));
		  }
		  else
		    $7 = newBuffer();

		  if($5)
		  {
		    bufferConcat($7, $5);
                    bufferWriteOp($7, SWFACTION_LOGICALNOT);
                    bufferWriteOp($7, SWFACTION_IF);
                    bufferWriteS16($7, 2);
                    bufferWriteS16($7, bufferLength($10)+5);
                  }

                  bufferConcat($7, $10);
                  bufferWriteOp($7, SWFACTION_JUMP);
                  bufferWriteS16($7, 2);
                  bufferWriteS16($7, -(bufferLength($7)+2));
                  bufferResolveJumps($7);

                  bufferConcat($$, $7);
				  delctx(CTX_LOOP);
                }

	| FOR '(' identifier inpart ')' for_in_init stmt
		{ Buffer b2, b3;
		  int tmp;

		  $$ = $4;
		  bufferWriteOp($$, SWFACTION_ENUMERATE);	

		  b2 = newBuffer();
		  bufferWriteSetRegister(b2, 0);
		  bufferWriteOp(b2, SWFACTION_PUSH);
		  bufferWriteS16(b2, 1);
		  bufferWriteU8(b2, 2);
		  bufferWriteOp(b2, SWFACTION_EQUALS2);
		  bufferWriteOp(b2, SWFACTION_IF);
		  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, $7);
		  bufferWriteS16(b2, bufferLength(b3) + 5);
		  tmp = bufferLength(b2) + bufferLength(b3) + 5;
		  bufferConcat($$, b2);
		  bufferWriteOp(b3, SWFACTION_JUMP);
		  bufferWriteS16(b3, 2);
		  bufferWriteS16(b3, -tmp);
		  bufferResolveJumps(b3);
		  bufferConcat($$, b3);
		  delctx(CTX_FOR_IN);
		  free($3); }

	| FOR '(' VAR identifier inpart ')' for_in_init stmt
		{ Buffer b2, b3;
		  int tmp;

		  $$ = $5;
		  bufferWriteOp($$, SWFACTION_ENUMERATE);	

		  b2 = newBuffer();
		  bufferWriteSetRegister(b2, 0);
		  bufferWriteOp(b2, SWFACTION_PUSH);
		  bufferWriteS16(b2, 1);
		  bufferWriteU8(b2, 2);
		  bufferWriteOp(b2, SWFACTION_EQUALS2);
		  bufferWriteOp(b2, SWFACTION_IF);
		  bufferWriteS16(b2, 2);
		  // add size later

		  b3 = newBuffer();
		  bufferWriteString(b3, $4, strlen($4)+1);
		  bufferWriteRegister(b3, 0);
		  bufferWriteOp(b3, SWFACTION_DEFINELOCAL);
		  bufferConcat(b3, $8);
		  bufferWriteS16(b2, bufferLength(b3) + 5);
		  tmp = bufferLength(b2) + bufferLength(b3) + 5;
		  bufferConcat($$, b2);
		  bufferWriteOp(b3, SWFACTION_JUMP);
		  bufferWriteS16(b3, 2);
		  bufferWriteS16(b3, -tmp);
		  bufferResolveJumps(b3);
		  bufferConcat($$, b3);
		  delctx(CTX_FOR_IN);
		  free($4); }
	;

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_JUMP);
		  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_JUMP);
		  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 ')'
		{
#ifdef DEBUG
		  printf("void_function_call: IDENTIFIER '(' expr_list ')'\n");
#endif
		  $$ = $3.buffer;
		  bufferWriteInt($$, $3.count);
		  bufferWriteString($$, $1, strlen($1)+1);
		  bufferWriteOp($$, SWFACTION_CALLFUNCTION);
		  bufferWriteOp($$, SWFACTION_POP);
		  free($1); }

	| TARGETPATH '(' IDENTIFIER ')'
		{ $$ = newBuffer();
		  bufferWriteString($$, $3, strlen($3)+1);
		  free($3);
		  bufferWriteOp($$, SWFACTION_GETVARIABLE); 
		  bufferWriteOp($$, SWFACTION_TARGETPATH); 
		  bufferWriteOp($$, SWFACTION_POP); }

	| DELETE IDENTIFIER
		{ $$ = newBuffer();
		  bufferWriteString($$, $2, strlen($2)+1);
		  free($2);
		  bufferWriteOp($$, SWFACTION_DELETE2);
		  bufferWriteOp($$, SWFACTION_POP); }

	| DELETE lvalue_expr '.' IDENTIFIER
		{ $$ = $2;
		  // bufferWriteOp($$, SWFACTION_GETVARIABLE);
		  bufferWriteString($$, $4, strlen($4)+1);
		  free($4);
		  bufferWriteOp($$, SWFACTION_DELETE);
		  bufferWriteOp($$, SWFACTION_POP); }

	| DELETE lvalue_expr '[' expr ']'
		{ $$ = $2;
		  // bufferWriteOp($$, SWFACTION_GETVARIABLE);
		  bufferConcat($$, $4);
		  // bufferWriteOp($$, SWFACTION_GETVARIABLE);
		  bufferWriteOp($$, SWFACTION_DELETE); 
		  bufferWriteOp($$, SWFACTION_POP); }

	| TRACE '(' expr_or_obj ')'
		{ $$ = $3;
		  bufferWriteOp($$, SWFACTION_TRACE); }

	| GETURL '(' expr ')'
		{
#ifdef DEBUG
		  printf("void_function_call: GETURL '(' expr ')'\n");
#endif
		  $$ = $3;
		  bufferWriteOp($$, SWFACTION_PUSH);
		  bufferWriteS16($$, 2); bufferWriteS16($$, 0); // two 0 bytes	
		  bufferWriteOp($$, SWFACTION_GETURL2);
		  bufferWriteS16($$, 1);
		  bufferWriteU8($$, 0); }
	
	| GETURL '(' expr ',' expr urlmethod ')'
		{
#ifdef DEBUG
		  printf("void_function_call: GETURL '(' expr ',' expr urlmethod ')'\n");
#endif
		  $$ = $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_STARTDRAG); }

	| 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_STARTDRAG); }

	| STOPDRAG '(' ')' /* no args */
		{ $$ = newBuffer();
		  bufferWriteOp($$, SWFACTION_ENDDRAG); }

	/* 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($$, $3, strlen($3));
		  bufferWriteU8($$, 0);
		  bufferWriteHardString($$, $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); }

	| GOTOANDPLAY '(' INTEGER ')'
		{ $$ = newBuffer();
		  bufferWriteOp($$, SWFACTION_GOTOFRAME);
		  bufferWriteS16($$, 2);
		  bufferWriteS16($$, $3-1); /* GOTOFRAME target is 0-based */
		  bufferWriteOp($$, SWFACTION_PLAY); }

	| GOTOANDSTOP '(' INTEGER ')'
		{ $$ = newBuffer();
		  bufferWriteOp($$, SWFACTION_GOTOFRAME);
		  bufferWriteS16($$, 2);
		  bufferWriteS16($$, $3-1);  /* GOTOFRAME target is 0-based */
		  bufferWriteOp($$, SWFACTION_STOP); }

	| GOTOANDPLAY '(' STRING ')'
		{ $$ = newBuffer();
		  bufferWriteOp($$, SWFACTION_GOTOLABEL);
		  bufferWriteS16($$, strlen($3)+1);
		  bufferWriteHardString($$, $3, strlen($3)+1);
		  free($3); 
		  bufferWriteOp($$, SWFACTION_PLAY); }

	| GOTOANDSTOP '(' STRING ')'
		{ $$ = newBuffer();
		  bufferWriteOp($$, SWFACTION_GOTOLABEL);
		  bufferWriteS16($$, strlen($3)+1);
		  bufferWriteHardString($$, $3, strlen($3)+1);
		  free($3); 
		  bufferWriteOp($$, SWFACTION_STOP); }

	| GOTOANDPLAY '(' expr ')'
		{ $$ = $3;
		  bufferWriteOp($$, SWFACTION_GOTOFRAME2);
		  bufferWriteS16($$, 1);
		  bufferWriteU8($$, 1); } /* XXX - and play */

	| GOTOANDSTOP '(' expr ')'
		{ $$ = $3;
		  bufferWriteOp($$, SWFACTION_GOTOFRAME2);
		  bufferWriteS16($$, 1);
		  bufferWriteU8($$, 0); } /* XXX - and stop */

	| SETTARGET '(' STRING ')'
		{ $$ = newBuffer();
		  bufferWriteOp($$, SWFACTION_SETTARGET);
		  bufferWriteS16($$, strlen($3)+1);
		  bufferWriteHardString($$, $3, strlen($3)+1);
		  free($3); }

	| SETTARGET '(' expr ')'
		{ $$ = $3;
		  bufferWriteOp($$, SWFACTION_SETTARGET2); }


	;


function_call
	: IDENTIFIER '(' expr_list ')'
		{
#ifdef DEBUG
		  printf("function_call: %s '(' expr_list ')'\n", $1);
#endif
		  $$ = $3.buffer;
		  bufferWriteInt($$, $3.count);
		  bufferWriteString($$, $1, strlen($1)+1);
		  bufferWriteOp($$, SWFACTION_CALLFUNCTION);
		  free($1); }

	| TARGETPATH '(' IDENTIFIER ')'
		{ $$ = newBuffer();
		  bufferWriteString($$, $3, strlen($3)+1);
		  free($3);
		  bufferWriteOp($$, SWFACTION_GETVARIABLE); 
		  bufferWriteOp($$, SWFACTION_TARGETPATH); }


	| DELETE IDENTIFIER
		{ $$ = newBuffer();
		  bufferWriteString($$, $2, strlen($2)+1);
		  free($2);
		  bufferWriteOp($$, SWFACTION_DELETE2);  }

	| DELETE lvalue_expr '.' IDENTIFIER
		{ $$ = $2;
		  bufferWriteString($$, $4, strlen($4)+1);
		  free($4);
		  bufferWriteOp($$, SWFACTION_DELETE); }

	| DELETE lvalue_expr '[' expr ']'
		{ $$ = $2;
		  bufferConcat($$, $4);
		  bufferWriteOp($$, SWFACTION_DELETE);  }

	| EVAL '(' expr ')'
		{ $$ = $3;
		  bufferWriteOp($$, SWFACTION_GETVARIABLE); }

	| GETTIMER '(' ')'
		{ $$ = newBuffer();
		  bufferWriteOp($$, SWFACTION_GETTIME); }

	| RANDOM '(' expr ')'
		{ $$ = $3;
		  bufferWriteOp($$, SWFACTION_RANDOMNUMBER); }

	| 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 ')'
		{
#ifdef DEBUG
		  printf("function_call: TYPEOF '(' expr_or_obj ')'\n");
#endif
		  $$ = $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();
		if(swfVersion > 6)
		{
			// TODO: let user control which flags to use ?
			// Don't preload any variable in registers, or we'll need to track all uses of 
			// those variables in this function context turning them into register accesses
			int flags = 0;
			int num_regs = 0;
			bufferWriteDefineFunction2($$, NULL, $3.buffer, $5, flags, num_regs);
		}
		else
		{
			bufferWriteOp($$, SWFACTION_DEFINEFUNCTION);
			bufferWriteS16($$, bufferLength($3.buffer) + 5); /* what's 5 here ? */
			bufferWriteU8($$, 0); /* empty function name */
			bufferWriteS16($$, $3.count);
			bufferConcat($$, $3.buffer);
			bufferWriteS16($$, bufferLength($5));
			bufferConcat($$, $5);
			delctx(CTX_FUNCTION); // should be done for function2 as well ?
		}
	}
	;

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);
		  free($1); }
	;

objexpr_list
	: objexpr
		{ $$.buffer = $1;
		  $$.count = 1; }

	| objexpr_list ',' objexpr
		{ bufferConcat($$.buffer, $3);
		  ++$$.count;  }
	;

assignop
	: "+="		{ $$ = SWFACTION_ADD2; }
	| "-="		{ $$ = 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);
		  free($3);
		  $$.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 LOR expr
		{ $$ = $1;
		  bufferConcat($$, $3);
		  bufferWriteOp($$, SWFACTION_LOGICALOR); }

	| expr LAN expr
		{ $$ = $1;
		  bufferConcat($$, $3);
		  bufferWriteOp($$, SWFACTION_LOGICALAND); }

	| 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_ADD2); }

	| 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_LESS2); }

	| expr '>' expr
		{ $$ = $3;
		  bufferConcat($$, $1);
		  bufferWriteOp($$, SWFACTION_LESS2); }

	| expr "<=" expr
		{ $$ = $3;
		  bufferConcat($$, $1);
		  bufferWriteOp($$, SWFACTION_LESS2);
		  bufferWriteOp($$, SWFACTION_LOGICALNOT); }

	| expr ">=" expr
		{ bufferConcat($1, $3);
		  bufferWriteOp($1, SWFACTION_LESS2);
		  bufferWriteOp($1, SWFACTION_LOGICALNOT); }

	| expr "==" expr
		{ bufferConcat($1, $3);
		  bufferWriteOp($1, SWFACTION_EQUALS2); }

	| expr "===" expr
		{ bufferConcat($1, $3);
		  bufferWriteOp($1, SWFACTION_STRICTEQUALS); }

	| expr "!=" expr
		{ bufferConcat($1, $3);
		  bufferWriteOp($1, SWFACTION_EQUALS2);
		  bufferWriteOp($1, SWFACTION_LOGICALNOT); }

	| expr "!==" expr
		{ bufferConcat($1, $3);
		  bufferWriteOp($1, SWFACTION_STRICTEQUALS); 
		  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_IF);
		  bufferWriteS16($1, 2);
		  bufferWriteS16($1, bufferLength($5)+5);
		  bufferConcat($1, $5);
		  bufferWriteOp($1, SWFACTION_JUMP);
		  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_PUSHDUP);
		    bufferConcat($$, $1.ident);
		    bufferWriteOp($$, SWFACTION_STACKSWAP);
		    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
		{
#ifdef DEBUG
		  printf("NEW %s\n", $2);
#endif
		  $$ = newBuffer();
		  bufferWriteInt($$, 0);
		  bufferWriteString($$, $2, strlen($2)+1);
		  free($2);
		  bufferWriteOp($$, SWFACTION_NEWOBJECT); }

	| NEW IDENTIFIER '(' expr_list ')'
		{
#ifdef DEBUG
		  printf("NEW %s '(' expr_list ')'\n", $2);
#endif
		  $$ = $4.buffer;
		  bufferWriteInt($$, $4.count);
		  bufferWriteString($$, $2, strlen($2)+1);
		  free($2);
		  bufferWriteOp($$, SWFACTION_NEWOBJECT); }

	| NEW lvalue_expr '.' IDENTIFIER
		{
#ifdef DEBUG
		  printf("NEW lvalue_expr '.' %s\n", $4);
#endif
		  $$ = newBuffer();
		  bufferWriteInt($$, 0);
		  bufferConcat($$, $2);
		  bufferWriteString($$, $4, strlen($4)+1);
		  free($4);
		  bufferWriteOp($$, SWFACTION_NEWMETHOD); }

	| NEW lvalue_expr '[' expr ']'
		{
#ifdef DEBUG
		  printf("NEW lvalue_expr '[' expr ']'\n");
#endif
		  $$ = newBuffer();
		  bufferWriteInt($$, 0);
		  bufferConcat($$, $2);
		  bufferConcat($$, $4);
		  bufferWriteOp($$, SWFACTION_NEWMETHOD); }


	| NEW lvalue_expr '.' IDENTIFIER '(' expr_list ')'
		{
#ifdef DEBUG
		  printf("NEW lvalue_expr '.' %s '(' expr_list ')'\n", $4);
#endif
		  $$ = $6.buffer;
		  bufferWriteInt($$, $6.count);
		  bufferConcat($$, $2);
		  bufferWriteString($$, $4, strlen($4)+1);
		  free($4);
		  bufferWriteOp($$, SWFACTION_NEWMETHOD); }

	| NEW lvalue_expr '[' expr ']' '(' expr_list ')'
		{
#ifdef DEBUG
		  printf("NEW lvalue_expr '[' expr ']' '(' expr_list ')'\n");
#endif
		  $$ = $7.buffer;
		  bufferWriteInt($$, $7.count);
		  bufferConcat($$, $2);
		  bufferConcat($$, $4);
		  bufferWriteOp($$, SWFACTION_NEWMETHOD); }

	| '[' 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
	: anon_function_decl

	| lvalue_expr

	| incdecop lvalue %prec "++"
		{ if($2.obj)
		  {
		    if($2.ident)	// expr . identifier
		    {
		      $$ = $2.obj;
		      bufferWriteOp($$, SWFACTION_PUSHDUP);	      /* a, a */
		      bufferWriteBuffer($$, $2.ident);        /* a, a, i */
		      bufferWriteOp($$, SWFACTION_STACKSWAP);      /* 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_STACKSWAP);      /* a, i */
		      bufferWriteOp($$, SWFACTION_PUSHDUP);	      /* a, i, i */
		      bufferWriteRegister($$, 0);	      /* a, i, i, a */
		      bufferWriteOp($$, SWFACTION_STACKSWAP);      /* 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_PUSHDUP);
		    bufferConcat($$, $2.ident);
		    bufferWriteOp($$, SWFACTION_STACKSWAP);
		    bufferWriteOp($$, SWFACTION_SETVARIABLE);
		  }
		}

	| lvalue incdecop %prec POSTFIX
		{ if($1.obj)
		  {
		    if($1.ident)
		    {
		      $$ = $1.obj;	                      /* a */
		      bufferWriteOp($$, SWFACTION_PUSHDUP);	      /* a, a */
		      bufferWriteBuffer($$, $1.ident);        /* a, a, i */
		      bufferWriteOp($$, SWFACTION_GETMEMBER); /* a, a.i */
		      bufferWriteSetRegister($$, 0);
		      bufferWriteOp($$, SWFACTION_STACKSWAP);      /* 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_STACKSWAP);      /* a, i */
		      bufferWriteOp($$, SWFACTION_PUSHDUP);	      /* a, i, i */
		      bufferWriteRegister($$, 0);             /* a, i, i, a */
		      bufferWriteOp($$, SWFACTION_STACKSWAP);      /* 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_PUSHDUP);
		    bufferWriteOp($$, $2);
		    bufferConcat($$, $1.ident);
		    bufferWriteOp($$, SWFACTION_STACKSWAP);
		    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($$); }

	| UNDEFINED
		{ $$ = newBuffer();
		  bufferWriteUndef($$); }

	| STRING
		{ $$ = newBuffer();
		  bufferWriteString($$, $1, strlen($1)+1);
		  free($1); }
	| lvalue assignop expr
		{ if($1.obj)
		  {
		    if($1.ident)
		    {
		      $$ = $1.obj;			      /* a */
		      bufferWriteOp($$, SWFACTION_PUSHDUP);	      /* 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_STACKSWAP);      /* a, i, a.i+v */
		      bufferWriteSetRegister($$, 0);
		      bufferWriteOp($$, SWFACTION_SETMEMBER); /* a.i = a.i+v */
		      bufferWriteRegister($$, 0);
		    }
		    else
		    {
		      $$ = $1.memexpr;			      /* i */
		      bufferConcat($$, $1.obj);		      /* i, a */
		      bufferWriteSetRegister($$, 0);
		      bufferWriteOp($$, SWFACTION_STACKSWAP);      /* a, i */
		      bufferWriteOp($$, SWFACTION_PUSHDUP);	      /* a, i, i */
		      bufferWriteRegister($$, 0);	      /* a, i, i, a */
		      bufferWriteOp($$, SWFACTION_STACKSWAP);      /* 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 */
		      bufferWriteSetRegister($$, 0);
		      bufferWriteOp($$, SWFACTION_SETMEMBER); /* a[i] = a[i]+v */
		      bufferWriteRegister($$, 0);
		    }
		  }
		  else
		  {
		    $$ = $1.ident;
		    bufferWriteOp($$, SWFACTION_PUSHDUP);
		    bufferWriteOp($$, SWFACTION_GETVARIABLE);
		    bufferConcat($$, $3);
		    bufferWriteOp($$, $2);
		    bufferWriteSetRegister($$, 0);
		    bufferWriteOp($$, SWFACTION_SETVARIABLE);
		    bufferWriteRegister($$, 0);
		  }
		}
	;

init_vars
	: init_var

	| init_vars ',' init_var
		{ $$ = $1;
		  bufferConcat($$, $3); }
	;

init_var
	: identifier '=' expr_or_obj
		{ $$ = newBuffer();
		  bufferWriteString($$, $1, strlen($1)+1);
		  free($1);
		  bufferConcat($$, $3);
		  bufferWriteOp($$, SWFACTION_DEFINELOCAL); }

	| identifier
		{ $$ = newBuffer();
		  bufferWriteString($$, $1, strlen($1)+1);
		  free($1);
		  bufferWriteOp($$, SWFACTION_DEFINELOCAL2); }
	;

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_PUSHDUP);	      /* 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_STACKSWAP);      /* 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_STACKSWAP);      /* a, i */
		      bufferWriteOp($$, SWFACTION_PUSHDUP);	      /* a, i, i */
		      bufferWriteRegister($$, 0);	      /* a, i, i, a */
		      bufferWriteOp($$, SWFACTION_STACKSWAP);      /* 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_PUSHDUP);
		    bufferWriteOp($$, SWFACTION_GETVARIABLE);
		    bufferWriteOp($$, $1);
		    bufferWriteOp($$, SWFACTION_SETVARIABLE);
		  }
		}

	| lvalue incdecop %prec POSTFIX
		{ if($1.obj)
		  {
		    if($1.ident)
		    {
		      $$ = $1.obj;			      /* a */
		      bufferWriteOp($$, SWFACTION_PUSHDUP);       /* 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_STACKSWAP);      /* 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_STACKSWAP);      /* a, i */
		      bufferWriteOp($$, SWFACTION_PUSHDUP);       /* a, i, i */
		      bufferWriteRegister($$, 0);             /* a, i, i, a */
		      bufferWriteOp($$, SWFACTION_STACKSWAP);      /* 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_PUSHDUP);	
		    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_PUSHDUP);	      /* 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_STACKSWAP);      /* 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_STACKSWAP);      /* a, i */
		      bufferWriteOp($$, SWFACTION_PUSHDUP);	      /* a, i, i */
		      bufferWriteRegister($$, 0);	      /* a, i, i, a */
		      bufferWriteOp($$, SWFACTION_STACKSWAP);      /* 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_PUSHDUP);
		    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, $1,
								 strlen($1)+1); }

	| INTEGER		{ $$ = bufferWriteInt(asmBuffer, $1); }

	| DOUBLE		{ $$ = bufferWriteDouble(asmBuffer, $1); }

	| BOOLEAN		{ bufferWriteU8(asmBuffer, PUSH_BOOLEAN);
				  $$ = bufferWriteU8(asmBuffer, $1)+1;
				  bufferPatchPushLength(asmBuffer, 2); }

	| NULLVAL		{ $$ = bufferWriteU8(asmBuffer, PUSH_NULL);
				  bufferPatchPushLength(asmBuffer, 1); }

	| UNDEFINED		{ $$ = bufferWriteU8(asmBuffer, PUSH_UNDEF);
				  bufferPatchPushLength(asmBuffer, 1); }

	| REGISTER		{ bufferWriteU8(asmBuffer, PUSH_REGISTER);
				  $$ = bufferWriteU8(asmBuffer,
						     (char)atoi($1))+1;
				  bufferPatchPushLength(asmBuffer, 2); }
	;


push_list
	: push_item			{ $$ = $1; }
	| push_list ',' push_item	{ $$ = $1 + $3; }
	;

opcode
	: PUSH 			{ $$ = bufferWritePushOp(asmBuffer);
				  $$ += bufferWriteS16(asmBuffer, 0); }
	  push_list		{ $$ = $<len>2 + $3;
				  bufferPatchLength(asmBuffer, $3); }

	| with

	| SETREGISTER REGISTER
				{ $$ = bufferWriteOp(asmBuffer, 
						     SWFACTION_STOREREGISTER);
				  $$ += 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_ADD2); }
	| LESSTHAN		{ $$ = bufferWriteOp(asmBuffer, 
						     SWFACTION_LESS2); }
	| EQUALS		{ $$ = bufferWriteOp(asmBuffer, 
						     SWFACTION_EQUALS2); }
	| 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_NEWOBJECT); }
	| 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_DEFINELOCAL2); }
	| EXTENDS		{ $$ = bufferWriteOp(asmBuffer, 
						     SWFACTION_EXTENDS); }

	/* 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_PUSHDUP); }
	| SWAP			{ $$ = bufferWriteOp(asmBuffer, SWFACTION_STACKSWAP); }
	| POP			{ $$ = bufferWriteOp(asmBuffer, SWFACTION_POP); }
	| GETVARIABLE		{ $$ = bufferWriteOp(asmBuffer, SWFACTION_GETVARIABLE); }
	| SETVARIABLE		{ $$ = bufferWriteOp(asmBuffer, SWFACTION_SETVARIABLE); }
	| GETPROPERTY		{ $$ = bufferWriteOp(asmBuffer, SWFACTION_GETPROPERTY); }
	| SETPROPERTY		{ $$ = bufferWriteOp(asmBuffer, SWFACTION_SETPROPERTY); }
	| TONUMBER		{ $$ = bufferWriteOp(asmBuffer, SWFACTION_TONUMBER); }
	| TOSTRING		{ $$ = bufferWriteOp(asmBuffer, SWFACTION_TOSTRING); }
	| SETTARGETEXPRESSION	{ $$ = bufferWriteOp(asmBuffer, SWFACTION_SETTARGET2); }
	| 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_RANDOMNUMBER); }
	| MBLENGTH		{ $$ = bufferWriteOp(asmBuffer, SWFACTION_MBLENGTH); }
	| ORD			{ $$ = bufferWriteOp(asmBuffer, SWFACTION_ORD); }
	| CHR			{ $$ = bufferWriteOp(asmBuffer, SWFACTION_CHR); }
	| GETTIMER		{ $$ = bufferWriteOp(asmBuffer, SWFACTION_GETTIME); }
	| MBSUBSTRING		{ $$ = bufferWriteOp(asmBuffer, SWFACTION_MBSUBSTRING); }
	| MBORD			{ $$ = bufferWriteOp(asmBuffer, SWFACTION_MBORD); }
	| MBCHR			{ $$ = bufferWriteOp(asmBuffer, SWFACTION_MBCHR); }

	/* with args */
	| BRANCHALWAYS STRING	{ $$ = bufferWriteOp(asmBuffer, SWFACTION_JUMP);
				  $$ += bufferWriteS16(asmBuffer, 2);
				  $$ += bufferBranchTarget(asmBuffer, $2); }

	| BRANCHIFTRUE STRING	{ $$ = bufferWriteOp(asmBuffer, SWFACTION_IF);
				  $$ += bufferWriteS16(asmBuffer, 2);
				  $$ += bufferBranchTarget(asmBuffer, $2); }

	;

%%

