%{
/*
** $Id: lua.stx,v 1.32 1998/01/12 13:00:51 roberto Exp roberto $
** Syntax analizer and code generator
** See Copyright Notice in lua.h
*/


#include <stdlib.h>
#include <string.h>

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


int luaY_parse (void);


#define MES_LIM(x)	"(limit=" x ")"


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

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

#define MINGLOBAL (MAXLOCALS+1)

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

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

/* maximum number of upvalues */
#define MAXUPVALUES 16
#define SMAXUPVALUES "16"



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

#define isglobal(v)	(MINGLOBAL<=(v))
#define globalindex(v)	((v)-MINGLOBAL)
#define islocal(v)	(0<(v) && (v)<MINGLOBAL)
#define localindex(v)	((v)-1)
#define isdot(v)	(v<0)
#define dotindex(v)	((-(v))-1)

/* state needed to generate code for a given function */
typedef struct FuncState {
  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 */
} FuncState;



#define YYPURE	1


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, L->lexstate->linenumber, L->mainState->f->fileName->str);
}


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


static void check_pc (int n)
{
  FuncState *fs = L->currState;
  if (fs->pc+n > fs->maxcode)
    fs->maxcode = luaM_growvector(&fs->f->code, fs->maxcode,
                                  Byte, codeEM, MAX_INT);
}


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


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


static int code_oparg_at (int pc, OpCode op, int builtin, int arg, int delta)
{
  Byte *code = L->currState->f->code;
  deltastack(delta);
  if (arg < builtin) {
    code[pc] = op+1+arg;
    return 1;
  }
  else if (arg <= 255) {
    code[pc] = op;
    code[pc+1] = arg;
    return 2;
  }
  else if (arg <= MAX_WORD) {
    code[pc] = op+1+builtin;
    code[pc+1] = arg&0xFF;
    code[pc+2] = arg>>8;
    return 3;
  }
  else luaY_error("code too long " MES_LIM("64K"));
  return 0;   /* to avoid warnings */
}


static int fix_opcode (int pc, OpCode op, int builtin, int arg)
{
  FuncState *fs = L->currState;
  if (arg < builtin) {  /* close space */
    luaO_memdown(fs->f->code+pc+1, fs->f->code+pc+2, fs->pc-(pc+2));
    fs->pc--;
  }
  else if (arg > 255) {  /* open space */
    check_pc(1);
    luaO_memup(fs->f->code+pc+1, fs->f->code+pc, fs->pc-pc);
    fs->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 */
  L->currState->pc += code_oparg_at(L->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 (FuncState *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, FuncState *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, L->currState));
}


#define LIM 20
static int real_constant (real r)
{
  /* check whether 'r' has appeared within the last LIM entries */
  TObject *cnt = L->currState->f->consts;
  int c = L->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(L->currState);
  cnt = L->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)
{
  int i;
  if (f >= 0 && f <= (real)MAX_WORD && (real)(i=(int)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)
{
  FuncState *fs = L->currState;
  if (fs->maxvars != -1) {  /* debug information? */
    if (fs->nvars >= fs->maxvars)
      fs->maxvars = luaM_growvector(&fs->f->locvars, fs->maxvars,
                                    LocVar, "", MAX_WORD);
    fs->f->locvars[fs->nvars].varname = varname;
    fs->f->locvars[fs->nvars].line = line;
    fs->nvars++;
  }
}


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


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

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


/* 
** dotted variables <a.x> must be stored like regular indexed vars <a["x"]>
*/
static vardesc var2store (vardesc var)
{
  if (isdot(var)) {
    code_constant(dotindex(var));
    var = 0;
  }
  return var;
}


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


static int aux_localname (TaggedString *n, FuncState *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, FuncState *st)
{
  int i = aux_localname(n, st);
  if (i == -1) {  /* check shadowing */
    int l;
    for (l=1; l<=(st-L->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)+MINGLOBAL;  /* global value */
  }
  else return i+1;  /* local value */
}


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


static void pushupvalue (TaggedString *n)
{
  int i;
  if (L->currState == L->mainState)
    luaY_error("cannot access upvalue in main");
  if (aux_localname(n, L->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)
{
  if (lua_debug && line != L->lexstate->lastline) {
    code_oparg(SETLINE, 0, line, 0);
    L->lexstate->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 = L->currState->f->code[exp];
    int nparams = L->currState->f->code[exp-1];
    exp += fix_opcode(exp-2, CALLFUNC, 2, nresults);
    L->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 = L->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)
{
  L->currState->nlocalvar += nparams;  /* "self" may already be there */
  nparams = L->currState->nlocalvar;
  if (!dots) {
    L->currState->f->code[1] = nparams;  /* fill-in arg information */
    deltastack(nparams);
  }
  else {
    L->currState->f->code[1] = nparams+ZEROVARARG;
    deltastack(nparams+1);
    add_localvar(luaS_new("arg"));
  }
}


static void lua_pushvar (vardesc var)
{
  if (isglobal(var))
    code_oparg(GETGLOBAL, 8, globalindex(var), 1);
  else if (islocal(var))
    code_oparg(PUSHLOCAL, 8, localindex(var), 1);
  else if (isdot(var))
    code_oparg(GETDOTTED, 8, dotindex(var), 0);
  else
    code_pop(GETTABLE);
}


static void storevar (vardesc var)
{
  if (var == 0)  /* indexed var */
    code_opcode(SETTABLE0, -3);
  else if (isglobal(var))
    code_oparg(SETGLOBAL, 8, globalindex(var), -1);
  else  /* local var */
    code_oparg(SETLOCAL, 8, localindex(var), -1);
}


/* returns how many elements are left as 'garbage' on the stack */
static int lua_codestore (int i, int left)
{
  if (L->currState->varbuffer[i] != 0 ||  /* global or local var or */
      left+i == 0) {  /* indexed var without values in between */
    storevar(L->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 popped, 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 = L->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 (L->currState->pc == elseinit) {  /* no else part */
    L->currState->pc -= JMPSIZE;
    elseinit = L->currState->pc;
  }
  else
    elseinit += fix_jump(elseAdd, JMP, L->currState->pc);
  fix_jump(thenAdd, IFFJMP, elseinit);
}


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


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


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


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


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

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


/*
** Parse Lua code.
*/
TProtoFunc *luaY_parser (ZIO *z)
{
  struct LexState lexstate;
  FuncState state[MAXSTATES];
  L->currState = L->mainState = &state[0];
  L->lexstate = &lexstate;
  luaX_setinput(z);
  init_state(luaS_new(zname(z)));
  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, sexp  /* if != 0, points to function return
					 counter */
%type <vInt>  varlist1, funcParams, funcvalue
%type <vInt>  fieldlist, localnamelist, decinit
%type <vInt>  ffieldlist1, lfieldlist1, ffieldlist, lfieldlist, part
%type <vLong> var, varname, funcname /* vardesc */


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

       | DO block END

       | WHILE GetPC cond DO block END
       {{
	 FuncState *fs = L->currState;
         int expsize = $3-$2;
	 int newpos = $2+JMPSIZE;
	 check_pc(expsize);
	 memcpy(fs->f->code+fs->pc, fs->f->code+$2, expsize);
	 luaO_memdown(fs->f->code+$2, fs->f->code+$3, fs->pc-$2);
	 newpos += fix_jump($2, JMP, fs->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
       {
         L->currState->nlocalvar += $2;
         adjust_mult_assign($2, $3);
       }

       | FUNCTION funcname body { storevar($2); }
       ;

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

funcname : varname { $$ = $1; init_func(); }
	|  fvarname '.' fname
	{
          $$ = 0;  /* flag indexed variable */
	  init_func();
	}
	| fvarname ':' fname
	{
          $$ = 0;  /* flag indexed variable */
	  init_func();
	  add_localvar(luaS_new("self"));
	}
		;

fvarname : varname { lua_pushvar($1); } ;

fname	: NAME { code_string($1); } ;

body :  '(' parlist ')' chunk END { func_onstack(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 */ { $$ = L->currState->pc; } ;

SaveWord : /* empty */
	{ $$ = L->currState->pc;
	  check_pc(JMPSIZE);
	  L->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;}
     |  sexp             { $$ = $1;  /* simple expressions */ }
     |  table 		 { $$ = 0; }
     |  NUMBER           { code_number($1); $$ = 0; }
     |  STRING           { code_string($1); $$ = 0; }
     |	NIL		 { adjuststack(-1); $$ = 0; }
     |  FUNCTION { init_func(); } body { $$ = 0; }
     |	expr1 AND SaveWordPop expr1 { code_shortcircuit($3, ONFJMP); $$ = 0; }
     |	expr1 OR SaveWordPop expr1 { code_shortcircuit($3, ONTJMP); $$ = 0; }
     ;

sexp1	: sexp { adjust_functioncall($1, 1); } ;

sexp	: var { lua_pushvar($1); $$ = 0; }
	| '%' NAME { pushupvalue($2); $$ = 0; }
	|  functioncall { $$ = $1; }
	;

var	  :	varname { $$ = $1; }
	  |	sexp1 '[' expr1 ']' { $$ = 0; }  /* indexed variable */
	  |	sexp1 '.' NAME { $$ = (-string_constant($3, L->currState))-1; }
	  ;

varname	  :	NAME { $$ = singlevar($1, L->currState); } ;

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 */
	  $$ = L->currState->pc;
	  code_byte(0);  /* must be adjusted by other rules */
	}
	     ;

funcvalue    : sexp1 { $$ = 0; }
	     | sexp1 ':' NAME
	     {
               code_oparg(PUSHSELF, 8, string_constant($3, L->currState), 1);
               $$ = 1;
	     }
	     ;

funcParams :	'(' exprlist ')' { $$ = adjust_functioncall($2, 1); }
	|	table  { $$ = 1; }
	|	STRING { code_string($1); $$ = 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 {
            L->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 :  part { $$ = abs($1); }
	| part ';' part
	{
	  if ($1*$3 > 0)  /* repeated parts? */
	    luaY_error("invalid constructor syntax");
	  $$ = abs($1)+abs($3);
	}
	;

part : /* empty */ { $$ = 0; }
	| ffieldlist { $$ = $1; }
	| lfieldlist { $$ = $1; }
	;

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

ffieldlist : ffieldlist1 lastcomma
	{
	  flush_record($1%RFIELDS_PER_FLUSH);
	  $$ = -$1;  /* negative signals a "record" part */
	}
	;

lfieldlist : lfieldlist1 lastcomma
	{
	  flush_list($1/LFIELDS_PER_FLUSH, $1%LFIELDS_PER_FLUSH);
	  $$ = $1;
	}
	;

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

ffield      : ffieldkey '=' expr1 ;

ffieldkey   : '[' expr1 ']'
	    | fname
	    ;

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

localnamelist : NAME {store_localvar($1, 0); $$ = 1;}
	      | localnamelist ',' NAME { store_localvar($3, $1); $$ = $1+1; }
	      ;

decinit	  : /* empty */  { $$ = 0; }
	  | '=' exprlist1 { $$ = $2; }
	  ;

%%