%{
/*
** $Id: lua.stx,v 1.5 1997/09/24 19:43:11 roberto Exp roberto $
** Syntax analizer and code generator
** See Copyright Notice in lua.h
*/


#include <stdlib.h>

#include "lauxlib.h"
#include "ldo.h"
#include "lfunc.h"
#include "llex.h"
#include "lmem.h"
#include "lopcodes.h"
#include "lparser.h"
#include "lstring.h"
#include "lua.h"
#include "luadebug.h"
#include "lzio.h"


/* to avoid warnings generated by yacc */
int luaY_parse (void);
#define malloc luaM_malloc
#define realloc luaM_realloc
#define free luaM_free


/* maximum number of local variables */
#define MAXLOCALS 32

/* maximum number of variables in a multiple assignment */
#define MAXVAR 32

/* maximum number of nested functions */
#define MAXSTATES  6

/* maximum number of upvalues */
#define MAXUPVALUES 8

/*
** Variable descriptor: if n>0, represents global variable indexed
** by (n-1); if n<0, represents local variable index (-n)-1;
** if n==0, represents an indexed variable (table and index on top of stack)
** Must be long to store negative Word values.
*/
typedef long vardesc;


/* state needed to generate code for a given function */
typedef struct State {
  TProtoFunc *f;  /* current function header */
  int pc;  /* next position to code */
  TaggedString *localvar[MAXLOCALS];  /* store local variable names */
  int stacksize;  /* number of values on activation register */
  int maxstacksize;  /* maximum number of values on activation register */
  int nlocalvar;  /* number of active local variables */
  int nvars;  /* number of entries in f->locvars */
  int maxcode;  /* size of f->code */
  int maxvars;  /* size of f->locvars (-1 if no debug information) */
  int maxconsts;  /* size of f->consts */
  vardesc varbuffer[MAXVAR];  /* variables in an assignment list */
  vardesc upvalues[MAXUPVALUES];  /* upvalues */
} State;

static State *mainState, *currState;




void luaY_syntaxerror (char *s, char *token)
{
  if (token[0] == 0)
    token = "<eof>";
  luaL_verror("%.100s;\n> last token read: \"%.50s\" at line %d in file %.50s",
           s, token, luaX_linenumber, mainState->f->fileName->str);
}


void luaY_error (char *s)
{
  luaY_syntaxerror(s, luaX_lasttoken());
}


static void code_byte (Byte c)
{
  if (currState->pc >= currState->maxcode)
    currState->maxcode = luaM_growvector(&currState->f->code,
                                currState->maxcode, Byte, codeEM, MAX_INT);
  currState->f->code[currState->pc++] = c;
}


static void code_word_at (int pc, int n)
{
  if (n > MAX_WORD)
    luaY_error("construction too big; unable to compile");
  currState->f->code[pc] = n&0xFF;
  currState->f->code[pc+1] = n>>8;
}


static void fix_jump (int pc, OpCode op, int n)
{
  currState->f->code[pc] = op;
  code_word_at(pc+1, n);
}


static void code_word (int n)
{
  if (n > MAX_WORD)
    luaY_error("construction too big; unable to compile");
  code_byte(n&0xFF);
  code_byte(n>>8);
}


static void deltastack (int delta)
{
  currState->stacksize += delta;
  if (currState->stacksize > currState->maxstacksize) {
    if (currState->stacksize > 255)
      luaY_error("expression too complex");
    currState->maxstacksize = currState->stacksize;
  }
}


static void code_opcode (OpCode op, int delta)
{
  code_byte(op);
  deltastack(delta);
}


static void code_opb (OpCode opbyte, int arg, int delta)
{
  code_opcode(opbyte, delta);
  code_byte(arg);
}

static void code_opw (OpCode opbyte, int arg, int delta)
{
  code_opcode(opbyte, delta);
  code_word(arg);
}


static void code_opborw (OpCode opbyte, int arg, int delta)
{
  if (arg <= 255)
    code_opb(opbyte, arg, delta);
  else
    code_opw(opbyte+1, arg, delta);
}


static void code_oparg (OpCode firstop, OpCode opbyte, int arg, int delta)
{
  if (firstop+arg < opbyte)
     code_opcode(firstop+arg, delta);
  else
    code_opborw(opbyte, arg, delta);
}


static void code_pop (OpCode op)
{
  code_opcode(op, -1);
}

/* binary operations get 2 arguments and leave one, so they pop one */
#define code_binop(op)	code_pop(op)


#define code_neutralop(op)	code_byte(op)

/* unary operations get 1 argument and leave one, so they are neutral */
#define code_unop(op)	code_neutralop(op)


static void code_constant (int c)
{
  code_oparg(PUSHCONSTANT0, PUSHCONSTANTB, c, 1);
}


static int next_constant (State *cs)
{
  TProtoFunc *f = cs->f;
  if (f->nconsts >= cs->maxconsts) {
    cs->maxconsts = luaM_growvector(&f->consts, cs->maxconsts, TObject,
                                    constantEM, MAX_WORD);
  }
  return f->nconsts++;
}


static int string_constant (TaggedString *s, State *cs)
{
  TProtoFunc *f = cs->f;
  int c = s->constindex;
  if (!(c < f->nconsts &&
      ttype(&f->consts[c]) == LUA_T_STRING && tsvalue(&f->consts[c]) == s)) {
    c = next_constant(cs);
    ttype(&f->consts[c]) = LUA_T_STRING;
    tsvalue(&f->consts[c]) = s;
    s->constindex = c;  /* hint for next time */
  }
  return c;
}


static void code_string (TaggedString *s)
{
  code_constant(string_constant(s, currState));
}


#define LIM 13
static int real_constant (real r)
{
  /* check whether 'r' has appeared within the last LIM entries */
  TObject *cnt = currState->f->consts;
  int c = currState->f->nconsts;
  int lim = c < LIM ? 0 : c-LIM;
  while (--c >= lim) {
    if (ttype(&cnt[c]) == LUA_T_NUMBER && nvalue(&cnt[c]) == r)
      return c;
  }
  /* not found; create a luaM_new entry */
  c = next_constant(currState);
  cnt = currState->f->consts;  /* 'next_constant' may reallocate this vector */
  ttype(&cnt[c]) = LUA_T_NUMBER;
  nvalue(&cnt[c]) = r;
  return c;
}


static void code_number (real f)
{
  Word i;
  if (f >= 0 && f <= (real)MAX_WORD && (real)(i=(Word)f) == f)
    code_oparg(PUSH0, PUSHBYTE, i, 1);  /* f has an (short) integer value */
  else
    code_constant(real_constant(f));
}


static void flush_record (int n)
{
  if (n > 0)
    code_opb(SETMAP, n, -2*n);
}

static void flush_list (int m, int n)
{
  if (n == 0) return;
  if (m == 0)
    code_opcode(SETLIST0, -n);
  else if (m < 255)
    code_opb(SETLIST, m, -n);
  else
    luaY_error("list constructor too long");
  code_byte(n);
}


static void luaI_registerlocalvar (TaggedString *varname, int line)
{
  if (currState->maxvars != -1) {  /* debug information? */
    if (currState->nvars >= currState->maxvars)
      currState->maxvars = luaM_growvector(&currState->f->locvars,
                                      currState->maxvars, LocVar, "", MAX_WORD);
    currState->f->locvars[currState->nvars].varname = varname;
    currState->f->locvars[currState->nvars].line = line;
    currState->nvars++;
  }
}


static void luaI_unregisterlocalvar (int line)
{
  luaI_registerlocalvar(NULL, line);
}


static void store_localvar (TaggedString *name, int n)
{
  if (currState->nlocalvar+n < MAXLOCALS)
    currState->localvar[currState->nlocalvar+n] = name;
  else
    luaY_error("too many local variables");
  luaI_registerlocalvar(name, luaX_linenumber);
}

static void add_localvar (TaggedString *name)
{
  store_localvar(name, 0);
  currState->nlocalvar++;
}

static void add_varbuffer (vardesc var, int n)
{
  if (n >= MAXVAR)
    luaY_error("variable buffer overflow");
  currState->varbuffer[n] = var;
}


static int aux_localname (TaggedString *n, State *st)
{
  int i;
  for (i=st->nlocalvar-1; i >= 0; i--)
    if (n == st->localvar[i]) return i;  /* local var index */
  return -1;  /* not found */
}


static vardesc singlevar (TaggedString *n, State *st)
{
  int i = aux_localname(n, st);
  if (i == -1) {  /* check shadowing */
    int l;
    for (l=1; l<=(st-mainState); l++)
      if (aux_localname(n, st-l) >= 0)
        luaY_syntaxerror("cannot access a variable in outer scope", n->str);
    return string_constant(n, st)+1;  /* positive value */
  }
  else return -(i+1);  /* negative value */
}


static int indexupvalue (TaggedString *n)
{
  vardesc v = singlevar(n, currState-1);
  int i;
  for (i=0; i<currState->f->nupvalues; i++) {
    if (currState->upvalues[i] == v)
      return i;
  }
  /* new one */
  if (++currState->f->nupvalues > MAXUPVALUES)
    luaY_error("too many upvalues in a single function");
  currState->upvalues[i] = v;  /* i = currState->f->nupvalues - 1 */
  return i;
}


static void pushupvalue (TaggedString *n)
{
  int i;
  if (currState == mainState)
    luaY_error("cannot access upvalue in main");
  if (aux_localname(n, currState) >= 0)
    luaY_syntaxerror("cannot access an upvalue in current scope", n->str);
  i = indexupvalue(n);
  code_oparg(PUSHUPVALUE0, PUSHUPVALUE, i, 1);
}


void luaY_codedebugline (int line)
{
  static int lastline = 0;
  if (lua_debug && line != lastline) {
    code_opw(SETLINE, line, 0);
    lastline = line;
  }
}


static void adjuststack (int n)
{
  if (n > 0)
    code_oparg(POP1-1, POPS, n, -n);  /* POP1-1 = POP0 */
  else if (n < 0)
    code_oparg(PUSHNIL-1, PUSHNILS, -n, -n);  /* PUSHNIL1-1 = PUSHNIL0 */
}


static long adjust_functioncall (long exp, int i)
{
  if (exp <= 0)
    return -exp; /* exp is -list length */
  else {
    int temp = currState->f->code[exp];
    currState->f->code[exp] = i;
    if (i != MULT_RET)
      deltastack(i);
    return temp+i;
  }
}


static void adjust_mult_assign (int vars, long exps)
{
  if (exps > 0) { /* must correct function call */
    int diff = currState->f->code[exps] - vars;
    if (diff < 0)
      adjust_functioncall(exps, -diff);
    else {
      adjust_functioncall(exps, 0);
      adjuststack(diff);
    }
  }
  else adjuststack((-exps)-vars);
}


static void code_args (int dots)
{
  if (!dots)
    code_opb(ARGS, currState->nlocalvar, currState->nlocalvar);
  else {
    code_opb(VARARGS, currState->nlocalvar, currState->nlocalvar+1);
    add_localvar(luaS_new("arg"));
  }
}


static void lua_pushvar (vardesc number)
{
  if (number > 0)  /* global var */
    code_oparg(GETGLOBAL0, GETGLOBALB, number-1, 1);
  else if (number < 0)  /* local var */
    code_oparg(PUSHLOCAL0, PUSHLOCAL, (-number)-1, 1);
  else
    code_pop(GETTABLE);
}


static void storevar (vardesc number)
{
  if (number == 0)  /* indexed var */
    code_opcode(SETTABLE0, -3);
  else if (number > 0)  /* global var */
    code_opborw(SETGLOBALB, number-1, -1);
  else  /* number < 0 - local var */
    code_oparg(SETLOCAL0, SETLOCAL, (-number)-1, -1);
}


/* returns how many elements are left as 'garbage' on the stack */
static int lua_codestore (int i, int left)
{
  if (currState->varbuffer[i] != 0 ||  /* global or local var or */
      left+i == 0) {  /* indexed var without values in between */
    storevar(currState->varbuffer[i]);
    return left;
  }
  else {  /* indexed var with values in between*/
    code_pop(SETTABLE);
    code_byte(left+i);  /* number of elements between table/index and value */
    return left+2;  /* table/index are not poped, since they are not on top */
  }
}


static void codeIf (int thenAdd, int elseAdd)
{
  int elseinit = elseAdd+sizeof(Word)+1;
  if (currState->pc == elseinit) {  /* no else part */
    currState->pc -= sizeof(Word)+1;
    elseinit = currState->pc;
  }
  else
    fix_jump(elseAdd, JMP, currState->pc-(elseAdd+1));
  fix_jump(thenAdd, IFFJMP, elseinit-(thenAdd+1));
}


static void code_shortcircuit (OpCode op, int pos)
{
  int dist = currState->pc - (pos+1);
  if (dist > 255)
    luaY_error("and/or expression too long");
  currState->f->code[pos] = op;
  currState->f->code[pos+1] = dist;
}


static void codereturn (void)
{
  code_neutralop(RETCODE);
  code_byte(currState->nlocalvar);
  currState->stacksize = currState->nlocalvar;
}


static void func_onstack (TProtoFunc *f)
{
  int i;
  int nupvalues = (currState+1)->f->nupvalues;
  int c = next_constant(currState);
  ttype(&currState->f->consts[c]) = LUA_T_PROTO;
  currState->f->consts[c].value.tf = (currState+1)->f;
  for (i=0; i<nupvalues; i++)
    lua_pushvar((currState+1)->upvalues[i]);
  code_opborw(CLOSUREB, c, 1-nupvalues);
}


static void init_state (TaggedString *filename)
{
  TProtoFunc *f = luaF_newproto();
  currState->stacksize = 0;
  currState->maxstacksize = 0;
  currState->nlocalvar = 0;
  currState->f = f;
  f->fileName = filename;
  currState->pc = 0;
  currState->maxcode = 0;
  f->code = NULL;
  currState->maxconsts = 0;
  if (lua_debug) {
    currState->nvars = 0;
    currState->maxvars = 0;
  }
  else
    currState->maxvars = -1;  /* flag no debug information */
  code_byte(0);  /* to be filled with stacksize */
}


static void init_func (void)
{
  if (currState-mainState >= MAXSTATES-1)
    luaY_error("too many nested functions");
  currState++;
  init_state(mainState->f->fileName);
  luaY_codedebugline(luaX_linenumber);
  currState->f->lineDefined = luaX_linenumber;
}



static TProtoFunc *close_func (void)
{
  TProtoFunc *f = currState->f;
  code_neutralop(ENDCODE);
  f->code[0] = currState->maxstacksize;
  f->code = luaM_reallocvector(f->code, currState->pc, Byte);
  f->consts = luaM_reallocvector(f->consts, f->nconsts, TObject);
  if (currState->maxvars != -1) {  /* debug information? */
    luaI_registerlocalvar(NULL, -1);  /* flag end of vector */
    f->locvars = luaM_reallocvector(f->locvars, currState->nvars, LocVar);
  }
  currState--;
  return f;
}


/*
** Parse LUA code.
*/
TProtoFunc *luaY_parser (ZIO *z, char *chunkname)
{
  State state[MAXSTATES];
  currState = mainState = &state[0];
  luaX_setinput(z);
  init_state(luaS_new(chunkname));
  if (luaY_parse ()) lua_error("parse error");
  return close_func();
}


%}


%union
{
  int vInt;
  real vReal;
  char *pChar;
  long vLong;
  TaggedString *pTStr;
  TProtoFunc *pFunc;
}

%start chunk

%token WRONGTOKEN
%token NIL
%token IF THEN ELSE ELSEIF WHILE DO REPEAT UNTIL END
%token RETURN
%token LOCAL
%token FUNCTION
%token DOTS
%token <vReal> NUMBER
%token <pTStr>  NAME STRING

%type <vInt> PrepJump, PrepJumpPop, PrepJumpSC
%type <vLong> exprlist, exprlist1  /* if > 0, points to function return
	counter (which has list length); if <= 0, -list lenght */
%type <vLong> functioncall, expr  /* if != 0, points to function return
					 counter */
%type <vInt>  varlist1, funcParams, funcvalue
%type <vInt>  fieldlist, localdeclist, decinit
%type <vInt>  ffieldlist, ffieldlist1, semicolonpart
%type <vInt>  lfieldlist, lfieldlist1
%type <vInt>  parlist1, par
%type <vLong> var, singlevar, funcname  /* vardesc */
%type <pFunc> body


%left AND OR
%left EQ NE '>' '<' LE GE
%left CONC
%left '+' '-'
%left '*' '/'
%left UNARY NOT
%right '^'


%% /* beginning of rules section */


chunk    : statlist ret
         ;

statlist : /* empty */
	 | statlist stat sc
	 ;

sc	 : /* empty */ | ';' ;

stat   : IF expr1 THEN PrepJumpPop block PrepJump elsepart END
	{ codeIf($4, $6); }

       | WHILE {$<vInt>$=currState->pc;} expr1 DO PrepJumpPop block END
       {
	 code_opborw(UPJMPB, currState->pc+1 - ($<vInt>2), 0);
         fix_jump($5, IFFJMP, currState->pc - ($5+1));
       }

       | REPEAT {$<vInt>$=currState->pc;} block UNTIL expr1
       {
         code_opborw(IFFUPJMPB, currState->pc+1 - ($<vInt>2), -1);
       }

       | varlist1 '=' exprlist1
       {{
	  int i;
          int left = 0;
	  adjust_mult_assign($1, $3);
	  for (i=$1-1; i>=0; i--)
	    left = lua_codestore(i, left);
          adjuststack(left);  /* remove eventual 'garbage' left on stack */
       }}
       | functioncall
       | LOCAL localdeclist decinit
       {
         currState->nlocalvar += $2;
         adjust_mult_assign($2, $3);
       }
       | FUNCTION funcname body
       {
	 func_onstack($3);
	 storevar($2);
       }
       ;

block    : {$<vInt>$ = currState->nlocalvar;} chunk
         {
           adjuststack(currState->nlocalvar - $<vInt>1);
	   for (; currState->nlocalvar > $<vInt>1; currState->nlocalvar--)
	     luaI_unregisterlocalvar(luaX_linenumber);
         }
         ;

funcname	: var { init_func(); $$ = $1; }
		| varexp ':' NAME
	{
	  code_string($3);
          $$ = 0;  /* indexed variable */
	  init_func();
	  add_localvar(luaS_new("self"));
	}
		;

body :  '(' parlist ')' chunk END { $$ = close_func(); }
		;

elsepart : /* empty */
	 | ELSE block
         | ELSEIF expr1 THEN PrepJumpPop block PrepJump elsepart
	{ codeIf($4, $6); }
         ;

ret	: /* empty */
        | RETURN exprlist sc
          {
	   adjust_functioncall($2, MULT_RET);
           codereturn();
          }
	;

PrepJump : /* empty */
	 {
	   $$ = currState->pc;
	   code_byte(0);  /* open space */
	   code_word(0);
         }
	 ;

PrepJumpSC : /* empty */
	{ $$ = currState->pc; code_opcode(0, -1); code_byte(0); }
	;

PrepJumpPop : PrepJump	{ $$ = $1; deltastack(-1);  /* pop condition */ }
	 ;

expr1	 : expr { adjust_functioncall($1, 1); }
	 ;
				
expr :  '(' expr ')'     { $$ = $2; }
     |  expr1 EQ  expr1	 { code_binop(EQOP);   $$ = 0; }
     |	expr1 '<' expr1	 { code_binop(LTOP);   $$ = 0; }
     |	expr1 '>' expr1	 { code_binop(GTOP);   $$ = 0; }
     |	expr1 NE  expr1	 { code_binop(NEQOP);  $$ = 0; }
     |	expr1 LE  expr1	 { code_binop(LEOP);   $$ = 0; }
     |	expr1 GE  expr1	 { code_binop(GEOP);   $$ = 0; }
     |	expr1 '+' expr1  { code_binop(ADDOP);  $$ = 0; }
     |	expr1 '-' expr1  { code_binop(SUBOP);  $$ = 0; }
     |	expr1 '*' expr1  { code_binop(MULTOP); $$ = 0; }
     |	expr1 '/' expr1  { code_binop(DIVOP);  $$ = 0; }
     |	expr1 '^' expr1  { code_binop(POWOP);  $$ = 0; }
     |	expr1 CONC expr1 { code_binop(CONCOP);  $$ = 0; }
     |	'-' expr1 %prec UNARY	{ code_unop(MINUSOP); $$ = 0;}
     |	NOT expr1	 { code_unop(NOTOP);  $$ = 0;}
     | table { $$ = 0; }
     |  varexp           { $$ = 0;}
     |  NUMBER           { code_number($1); $$ = 0; }
     |  STRING           { code_string($1); $$ = 0; }
     |	NIL		 {code_opcode(PUSHNIL, 1); $$ = 0; }
     |  functioncall     { $$ = $1; }
     | FUNCTION { init_func(); } body { func_onstack($3); $$ = 0; }
     |	expr1 AND PrepJumpSC expr1 { code_shortcircuit(ONFJMP, $3); $$ = 0; }
     |	expr1 OR PrepJumpSC expr1 { code_shortcircuit(ONTJMP, $3); $$ = 0; }
     ;

table :
     { $<vInt>$ = currState->pc+1; code_opw(CREATEARRAY, 0, 1); }
       '{' fieldlist '}'
     { code_word_at($<vInt>1, $3); }
         ;

functioncall : funcvalue funcParams
	{
	  code_opcode(CALLFUNC, -($1+$2+1));  /* ajdust counts results */
	  code_byte($1+$2);
	  $$ = currState->pc;
	  code_byte(0);  /* may be adjusted by other rules */
	}
	     ;

funcvalue    : varexp { $$ = 0; }
	     | varexp ':' NAME
	     {
               code_opborw(PUSHSELFB, string_constant($3, currState), 1);
               $$ = 1;
	     }
	     ;

funcParams :	'(' exprlist ')'
	{ $$ = adjust_functioncall($2, 1); }
	|	table  { $$ = 1; }
	;

exprlist  :	/* empty */		{ $$ = 0; }
	  |	exprlist1		{ $$ = $1; }
	  ;
		
exprlist1 :  expr	{ if ($1 != 0) $$ = $1; else $$ = -1; }
	  |  exprlist1 ',' { $<vLong>$ = adjust_functioncall($1, 1); } expr
	{
	  if ($4 == 0) $$ = -($<vLong>3 + 1);  /* -length */
	  else
	  {
            currState->f->code[$4] = $<vLong>3;  /* store list length */
	    $$ = $4;
	  }
	}
	  ;

parlist  :	/* empty */ { code_args(0); }
	  |	parlist1    { code_args($1); }
	  ;
		
parlist1 :	par		  { $$ = $1; }
	  |	parlist1 ',' par
	{
	  if ($1)
            luaY_error("invalid parameter list");
          $$ = $3;
	}
	  ;

par : NAME	{ add_localvar($1); $$ = 0; }
    | DOTS	{ $$ = 1; }
    ;
		
fieldlist  : lfieldlist
  	     { flush_list($1/LFIELDS_PER_FLUSH, $1%LFIELDS_PER_FLUSH); }
	     semicolonpart
	     { $$ = $1+$3; }
	   | ffieldlist1 lastcomma
	     { $$ = $1; flush_record($1%RFIELDS_PER_FLUSH); }
	   ;

semicolonpart : /* empty */
	        { $$ = 0; }
	      | ';' ffieldlist
	        { $$ = $2; flush_record($2%RFIELDS_PER_FLUSH); }
	      ;

lastcomma  : /* empty */
	   | ','
	   ;

ffieldlist  : /* empty */ { $$ = 0; }
            | ffieldlist1 lastcomma { $$ = $1; }
            ;

ffieldlist1 : ffield			{$$=1;}
	   | ffieldlist1 ',' ffield	
		{
		  $$=$1+1;
		  if ($$%RFIELDS_PER_FLUSH == 0)
	            flush_record(RFIELDS_PER_FLUSH);
		}
	   ;

ffield      : ffieldkey '=' expr1
           ;

ffieldkey   : '[' expr1 ']'
	    | NAME { code_string($1); }
	    ;

lfieldlist  : /* empty */ { $$ = 0; }
	    | lfieldlist1 lastcomma { $$ = $1; }
	    ;

lfieldlist1 : expr1  {$$=1;}
	    | lfieldlist1 ',' expr1
		{
		  $$=$1+1;
		  if ($$%LFIELDS_PER_FLUSH == 0)
		    flush_list($$/LFIELDS_PER_FLUSH - 1, LFIELDS_PER_FLUSH);
		}
            ;

varlist1  :	var			
	  {
	    $$ = 1;
	    add_varbuffer($1, 0);
	  }
	  |	varlist1 ',' var	
	  {
	    add_varbuffer($3, $1);
	    $$ = $1+1;
	  }
	  ;
		
var	  :	singlevar { $$ = $1; }
	  |	varexp '[' expr1 ']' { $$ = 0; }  /* indexed variable */
	  |	varexp '.' NAME { code_string($3); $$ = 0; }/* ind. var. */
	  ;
		
singlevar :	NAME { $$ = singlevar($1, currState); }
	  ;

varexp	: var { lua_pushvar($1); }
	| '%' NAME { pushupvalue($2); }
	;

localdeclist  : NAME {store_localvar($1, 0); $$ = 1;}
     	  | localdeclist ',' NAME
	    {
	     store_localvar($3, $1);
	     $$ = $1+1;
	    }
	  ;
		
decinit	  : /* empty */  { $$ = 0; }
	  | '=' exprlist1 { $$ = $2; }
	  ;

%%