%{
/*
** $Id: lua.stx,v 1.11 1997/10/16 10:59:34 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);


/* size of a "normal" jump instruction: OpCode + 1 byte */
#define JMPSIZE	2

/* 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 16

/*
** 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 nupvalues;  /* number of upvalues */
  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 check_pc (int n)
{
  if (currState->pc+n > currState->maxcode)
    currState->maxcode = luaM_growvector(&currState->f->code,
                                currState->maxcode, Byte, codeEM, MAX_INT);
}


static void movecode_up (int d, int s, int n)
{
  while (n--)
    currState->f->code[d+n] = currState->f->code[s+n];
}


static void movecode_down (int d, int s, int n)
{
  int i;
  for (i=0; i<n; i++)
    currState->f->code[d+i] = currState->f->code[s+i];
}


static void code_byte (Byte c)
{
  check_pc(1);
  currState->f->code[currState->pc++] = c;
}


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 int code_oparg_at (int pc, OpCode op, int builtin, int arg, int delta)
{
  deltastack(delta);
  if (arg < builtin) {
    currState->f->code[pc] = op+1+arg;
    return 1;
  }
  else if (arg <= 255) {
    currState->f->code[pc] = op;
    currState->f->code[pc+1] = arg;
    return 2;
  }
  else if (arg <= MAX_WORD) {
    currState->f->code[pc] = op+1+builtin;
    currState->f->code[pc+1] = arg&0xFF;
    currState->f->code[pc+2] = arg>>8;
    return 3;
  }
  else luaY_error("construction too big - unable to compile");
  return 0;   /* to avoid warnings */
}


static int fix_opcode (int pc, OpCode op, int builtin, int arg)
{
  if (arg < builtin) {  /* close space */
    movecode_down(pc+1, pc+2, currState->pc-(pc+2));
    currState->pc--;
  }
  else if (arg > 255) {  /* open space */
    check_pc(1);
    movecode_up(pc+1, pc, currState->pc-pc);
    currState->pc++;
  }
  return code_oparg_at(pc, op, builtin, arg, 0) - 2;
}


static void code_oparg (OpCode op, int builtin, int arg, int delta)
{
  check_pc(3);  /* maximum code size */
  currState->pc += code_oparg_at(currState->pc, op, builtin, arg, delta);
}


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


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)


static void code_neutralop (OpCode op)
{
  code_opcode(op, 0);
}

/* 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(PUSHCONSTANT, 8, 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 20
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(PUSHNUMBER, 3, i, 1);  /* f has an (short) integer value */
  else
    code_constant(real_constant(f));
}


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

static void flush_list (int m, int n)
{
  if (n == 0) return;
  code_oparg(SETLIST, 1, m, -n);
  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->nupvalues; i++) {
    if (currState->upvalues[i] == v)
      return i;
  }
  /* new one */
  if (++currState->nupvalues > MAXUPVALUES)
    luaY_error("too many upvalues in a single function");
  currState->upvalues[i] = v;  /* i = currState->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(PUSHUPVALUE, 2, i, 1);
}


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


static void adjuststack (int n)
{
  if (n > 0)
    code_oparg(POP, 2, n-1, -n);
  else if (n < 0)
    code_oparg(PUSHNIL, 1, (-n)-1, -n);
}


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


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 nparams, int dots)
{
  currState->nlocalvar += nparams;
  if (!dots)
    code_oparg(ARGS, 0, currState->nlocalvar, currState->nlocalvar);
  else {
    code_oparg(VARARGS, 0, currState->nlocalvar, currState->nlocalvar+1);
    add_localvar(luaS_new("arg"));
  }
}


static void lua_pushvar (vardesc number)
{
  if (number > 0)  /* global var */
    code_oparg(GETGLOBAL, 8, number-1, 1);
  else if (number < 0)  /* local var */
    code_oparg(PUSHLOCAL, 8, (-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_oparg(SETGLOBAL, 8, number-1, -1);
  else  /* number < 0 - local var */
    code_oparg(SETLOCAL, 8, (-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_oparg(SETTABLE, 0, left+i, -1);
    return left+2;  /* table/index are not poped, since they are not on top */
  }
}


static int fix_jump (int pc, OpCode op, int n)
{
  /* jump is relative to position following jump instruction */
  return fix_opcode(pc, op, 0, n-(pc+JMPSIZE));
}


static void fix_upjmp (OpCode op, int pos)
{
  int delta = currState->pc+JMPSIZE - pos;  /* jump is relative */
  if (delta > 255) delta++;
  code_oparg(op, 0, delta, 0);
}


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


static void code_shortcircuit (int pc, OpCode op)
{
  fix_jump(pc, op, currState->pc);
}


static void codereturn (void)
{
  code_oparg(RETCODE, 0, currState->nlocalvar, 0);
  currState->stacksize = currState->nlocalvar;
}


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


static void init_state (TaggedString *filename)
{
  TProtoFunc *f = luaF_newproto();
  currState->stacksize = 0;
  currState->maxstacksize = 0;
  currState->nlocalvar = 0;
  currState->nupvalues = 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> SaveWord, cond, GetPC, SaveWordPop, SaveWordPush
%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, localnamelist, decinit
%type <vInt>  ffieldlist, ffieldlist1, semicolonpart
%type <vInt>  lfieldlist, lfieldlist1
%type <vLong> var, 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 cond THEN block SaveWord elsepart END { codeIf($2, $5); }

       | WHILE GetPC cond DO block END
       {{
         int expsize = $3-$2;
	 int newpos = $2+JMPSIZE;
	 check_pc(expsize);
	 memcpy(&currState->f->code[currState->pc],
                &currState->f->code[$2], expsize);
	 movecode_down($2, $3, currState->pc-$2);
	 newpos += fix_jump($2, JMP, currState->pc-expsize);
	 fix_upjmp(IFTUPJMP, newpos);
       }}

       | REPEAT GetPC block UNTIL expr1
       {
	 fix_upjmp(IFFUPJMP, $2);
	 deltastack(-1);  /* pops condition */
       }

       | 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 { adjust_functioncall($1, 0); }

       | LOCAL localnamelist 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;  /* flag indexed variable */
	  init_func();
	  add_localvar(luaS_new("self"));
	}
		;

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

elsepart : /* empty */
	 | ELSE block
         | ELSEIF cond THEN block SaveWord elsepart { codeIf($2, $5); }
         ;

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

GetPC	: /* empty */ { $$ = currState->pc; }
	;

SaveWord : /* empty */
	{ $$ = currState->pc;
	  check_pc(JMPSIZE);
	  currState->pc += JMPSIZE;  /* open space */
	}
	 ;

SaveWordPop : SaveWord { $$ = $1; deltastack(-1);  /* pop condition */ }
	;

SaveWordPush : SaveWord { $$ = $1; deltastack(1);  /* push a value */ }
	;

cond	:	expr1 SaveWordPop  { $$ = $2; }
	 ;

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		 { adjuststack(-1); $$ = 0; }
     |  functioncall     { $$ = $1; }
     |  FUNCTION { init_func(); } body { func_onstack($3); $$ = 0; }
     |	expr1 AND SaveWordPop expr1 { code_shortcircuit($3, ONFJMP); $$ = 0; }
     |	expr1 OR SaveWordPop expr1 { code_shortcircuit($3, ONTJMP); $$ = 0; }
     ;

table : '{' SaveWordPush fieldlist '}' { fix_opcode($2, CREATEARRAY, 2, $3); }
         ;

functioncall : funcvalue funcParams
	{
	  code_byte(0);  /* save space for opcode */
	  code_byte($1+$2);  /* number of parameters */
	  $$ = currState->pc;
	  code_byte(0);  /* must be adjusted by other rules */
	}
	     ;

funcvalue    : varexp { $$ = 0; }
	     | varexp ':' NAME
	     {
               code_oparg(PUSHSELF, 0, 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, 0); }
	  | DOTS { code_args(0, 1); }
	  | localnamelist { code_args($1, 0); }
	  | localnamelist ',' DOTS { code_args($1, 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	  :	NAME { $$ = singlevar($1, currState); }
	  |	varexp '[' expr1 ']' { $$ = 0; }  /* indexed variable */
	  |	varexp '.' NAME { code_string($3); $$ = 0; }/* ind. var. */
	  ;
		
varexp	: var { lua_pushvar($1); }
	| '%' NAME { pushupvalue($2); }
	;

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

%%