/*******************************************************************************
* Copyright (c) 2010-2012 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
/**
* Javacc grammar used to produce a parse tree.  Based on lua 5.2 syntax.
*
*/

options {
  STATIC = false;
  JDK_VERSION = "1.3";
  ERROR_REPORTING = true;
  UNICODE_INPUT = true;
  DEBUG_LOOKAHEAD = false;
  DEBUG_PARSER = false;
  DEBUG_TOKEN_MANAGER = false;
  OUTPUT_DIRECTORY = "src/jse/org/luaj/vm2/parser";
}

PARSER_BEGIN(LuaParser)
package org.luaj.vm2.parser;
import org.luaj.vm2.*;
import org.luaj.vm2.ast.*;
import java.util.*;

public class LuaParser {
  static {
  	LuaValue.valueOf(true);
  }

  public static void main(String args[]) throws ParseException {
    LuaParser parser = new LuaParser(System.in);
    parser.Chunk();
  }
  
  private static Exp.VarExp assertvarexp(Exp.PrimaryExp pe) throws ParseException {
  	if (!pe.isvarexp()) 
		throw new ParseException("expected variable");
	return (Exp.VarExp) pe; 
  }
  
  private static Exp.FuncCall assertfunccall(Exp.PrimaryExp pe) throws ParseException {
  	if (!pe.isfunccall()) 
		throw new ParseException("expected function call");
	return (Exp.FuncCall) pe; 
  }
  
  public SimpleCharStream getCharStream() {
  	return jj_input_stream;
  }
  
  private long LineInfo() {
  	return ((long) jj_input_stream.getBeginLine() << 32) | jj_input_stream.getBeginColumn();
  }

  private void L(SyntaxElement e, long startinfo) {
  	e.beginLine = (int) (startinfo >> 32);
  	e.beginColumn = (short) startinfo;
  	e.endLine = token.endLine;
  	e.endColumn = (short) token.endColumn;
  }

  private void L(SyntaxElement e, Token starttoken) {
  	e.beginLine = starttoken.beginLine;
  	e.beginColumn = (short) starttoken.beginColumn;
  	e.endLine = token.endLine;
  	e.endColumn = (short) token.endColumn;
  }
  
}

PARSER_END(LuaParser)

/* WHITE SPACE */

SKIP :
{
  " " | "\t" | "\n" | "\r" | "\f"
}

/* COMMENTS and LONG STRINGS */

MORE :
{
  "--[[": IN_LC0
| "--[=[": IN_LC1
| "--[==[": IN_LC2
| "--[===[": IN_LC3
| < "--[====" ("=")* "[" > : IN_LCN
| "[[" : IN_LS0
| "[=[" : IN_LS1
| "[==[" : IN_LS2
| "[===[" : IN_LS3
| < "[====" ("=")* "[" > : IN_LSN
| "--"   : IN_COMMENT
}

<IN_COMMENT> SPECIAL_TOKEN :
{
  <COMMENT: (~["\n","\r"])* ("\n"|"\r"|"\r\n")? > : DEFAULT
}

<IN_LC0> SPECIAL_TOKEN : { <LONGCOMMENT0: "]]" > : DEFAULT }
<IN_LC1> SPECIAL_TOKEN : { <LONGCOMMENT1: "]=]" > : DEFAULT }
<IN_LC2> SPECIAL_TOKEN : { <LONGCOMMENT2: "]==]" > : DEFAULT }
<IN_LC3> SPECIAL_TOKEN : { <LONGCOMMENT3: "]===]" > : DEFAULT }
<IN_LCN> SPECIAL_TOKEN : { <LONGCOMMENTN: "]====" ("=")* "]" > : DEFAULT }

<IN_LS0> TOKEN : { <LONGSTRING0: "]]" > : DEFAULT }
<IN_LS1> TOKEN : { <LONGSTRING1: "]=]" > : DEFAULT }
<IN_LS2> TOKEN : { <LONGSTRING2: "]==]" > : DEFAULT }
<IN_LS3> TOKEN : { <LONGSTRING3: "]===]" > : DEFAULT }
<IN_LSN> TOKEN : { <LONGSTRINGN: "]====" ("=")* "]" > : DEFAULT }

<IN_LC0,IN_LC1,IN_LC2,IN_LC3,IN_LCN,IN_LS0,IN_LS1,IN_LS2,IN_LS3,IN_LSN> MORE :
{
  < ~[] >
}


/* RESERVED WORDS AND LITERALS */

TOKEN :
{
  <AND: "and">
| <BREAK: "break">
| <DO: "do">
| <ELSE: "else">
| <ELSEIF: "elseif">
| <END: "end">
| <FALSE: "false">
| <FOR: "for">
| <FUNCTION: "function">
| <GOTO: "goto">
| <IF: "if">
| <IN: "in">
| <LOCAL: "local">
| <NIL: "nil">
| <NOT: "not">
| <OR: "or">
| <RETURN: "return">
| <REPEAT: "repeat">
| <THEN: "then">
| <TRUE: "true">
| <UNTIL: "until">
| <WHILE: "while">
}

/* LITERALS */

TOKEN :
{
  < NAME: ["a"-"z", "A"-"Z", "_"] (["a"-"z", "A"-"Z", "_", "0"-"9"])* >
| < NUMBER: <HEX> | <FLOAT> >
| < #FLOAT: <FNUM> (<EXP>)? >
| < #FNUM: (<DIGIT>)+ "." (<DIGIT>)* | "." (<DIGIT>)+ | (<DIGIT>)+ >
| < #DIGIT: ["0"-"9"] >
| < #EXP: ["e","E"] (["+","-"])? (<DIGIT>)+ >
| < #HEX: "0" ["x","X"] <HEXNUM> (<HEXEXP>)? >
| < #HEXNUM: (<HEXDIGIT>)+ "." (<HEXDIGIT>)* | "." (<HEXDIGIT>)+ | (<HEXDIGIT>)+ >
| < #HEXDIGIT: ["0"-"9","a"-"f","A"-"F"] >
| < #HEXEXP: ["e","E","p","P"] (["+","-"])? (<DIGIT>)+ >
| < STRING: "\"" (<QUOTED> | ~["\\","\""])* "\"" >
| < CHARSTRING: "'" (<QUOTED> | ~["\\","'"])* "'" >
| < #QUOTED: <DECIMAL> | <UNICODE> | <CHAR> >
| < #DECIMAL:   "\\" ["0"-"9"] (["0"-"9"])? (["0"-"9"])? >
| < DBCOLON: "::" >
| < #UNICODE: "\\" "u" <HEXDIGIT> <HEXDIGIT> <HEXDIGIT> <HEXDIGIT> >
| < #CHAR: "\\" (~[]) >
| < #LF: ("\n" | "\r" | "\r\n") >
}

/** Root production. */
Chunk Chunk():
{
	Block b;
	Chunk c;
	long i = LineInfo();
}
{
	( "#" { token_source.SwitchTo(IN_COMMENT); } )? b=Block() <EOF> { c=new Chunk(b); L(c,i); return c; }
}

Block Block():
{
	Block b = new Block();
	Stat s;
	long i = LineInfo();
}
{
	(s=Stat() {b.add(s);} )* (s=ReturnStat() {b.add(s);} )? { L(b,i); return b; } 
}

Stat Stat():
{
	Block b,b2;
	Exp e,e2,e3=null;
	Stat s;
	FuncName fn;
	FuncBody fb;
	Token n;
	List<Name> nl;
	List<Exp> el=null;
	long i = LineInfo();
}
{
	";" { return null; }
|	s=Label() { L(s,i); return s; } 
|	<BREAK> { s=Stat.breakstat(); L(s,i); return s; }
|	<GOTO> n=<NAME> { s=Stat.gotostat(n.image); L(s,i); return s; }
|	<DO> b=Block() <END> { s=Stat.block(b); L(s,i); return s; }
|	<WHILE> e=Exp() <DO> b=Block() <END> { s=Stat.whiledo(e,b); L(s,i); return s; }
|	<REPEAT> b=Block() <UNTIL> e=Exp() { s=Stat.repeatuntil(b,e); L(s,i); return s; }
|	s=IfThenElse() { L(s,i); return s; }
|	LOOKAHEAD(3) <FOR> n=<NAME> "=" e=Exp() "," e2=Exp() ( "," e3=Exp() )? <DO> b=Block() <END>	{ s=Stat.fornumeric(n.image,e,e2,e3,b); L(s,i); return s; }
|	<FOR> nl=NameList() <IN> el=ExpList() <DO> b=Block() <END> { s=Stat.forgeneric(nl,el,b); L(s,i); return s; }
|	<FUNCTION> fn=FuncName() fb=FuncBody() { s=Stat.functiondef(fn,fb); L(s,i); return s; }
|	LOOKAHEAD(2) <LOCAL> <FUNCTION> n=<NAME> fb=FuncBody() { s=Stat.localfunctiondef(n.image,fb); L(s,i); return s; }
|	<LOCAL> nl=NameList() ( "=" el=ExpList() )?  { s=Stat.localassignment(nl,el); L(s,i); return s; }
|	s=ExprStat() { L(s,i); return s; }
}

Stat IfThenElse():
{
	Block b,b2,b3=null;
	Exp e,e2;
	List<Exp> el=null;
	List<Block> bl=null; 
}
{
	<IF> e=Exp() 
		<THEN> b=Block() 
		(<ELSEIF> e2=Exp() <THEN> b2=Block() { 
			if (el==null) el=new ArrayList<Exp>(); 
			if (bl==null) bl=new ArrayList<Block>(); 
			el.add(e2);
			bl.add(b2);
		} )* 
		(<ELSE> b3=Block())? <END> 
			{ return Stat.ifthenelse(e,b,el,bl,b3); }
}

Stat ReturnStat():
{
	List<Exp> el=null;
	Stat s;
	long i = LineInfo();
}
{
	<RETURN> ( el=ExpList() )? ( ";" )? { s=Stat.returnstat(el); L(s,i); return s; }
}

Stat Label():
{
	Token n;
}
{
	<DBCOLON> n=<NAME> <DBCOLON> { return Stat.labelstat(n.image); }
}

Stat ExprStat():
{
	Exp.PrimaryExp p;
	Stat s=null;
	long i = LineInfo();
}
{
	p=PrimaryExp() ( s=Assign(assertvarexp(p)) )?
	{ if (s==null) { s=Stat.functioncall(assertfunccall(p)); } L(s,i); return s; }
}

Stat Assign(Exp.VarExp v0):
{
	List<Exp.VarExp> vl = new ArrayList<Exp.VarExp>();
	vl.add(v0);
	Exp.VarExp ve;
	List<Exp> el;
	Stat s;
	long i = LineInfo();
}
{
	( "," ve=VarExp() { vl.add(ve); } )* "=" el=ExpList() { s=Stat.assignment(vl,el); L(s,i); return s; }
}

Exp.VarExp VarExp():
{
	Exp.PrimaryExp p;
}
{
	p=PrimaryExp() { return assertvarexp(p); }	
}

FuncName FuncName():
{
	Token n;
	FuncName f;
}
{
	n=<NAME> {f=new FuncName(n.image);} 
		( "." n=<NAME> {f.adddot(n.image);} )* 
		( ":" n=<NAME> {f.method=n.image;} )? 
			{L(f,n); return f;}
}

Exp.PrimaryExp PrefixExp():
{
	Token n;
	Exp e;
	Exp.PrimaryExp p;
	long i = LineInfo();
}
{
	n=<NAME>			{ p=Exp.nameprefix(n.image); L(p,i); return p; }
|	"(" e=Exp() ")"		{ p=Exp.parensprefix(e); L(p,i); return p; }
}

Exp.PrimaryExp PrimaryExp(): 
{	
	Exp.PrimaryExp p;
	long i = LineInfo();
}
{
	p=PrefixExp() ( LOOKAHEAD(2) p=PostfixOp(p) )* { L(p,i); return p; }
}

Exp.PrimaryExp PostfixOp(Exp.PrimaryExp lhs):
{
	Token n;
	Exp e;
	FuncArgs a;
	Exp.PrimaryExp p;
	long i = LineInfo();
}
{
	"." n=<NAME>				{ p=Exp.fieldop(lhs, n.image); L(p,i); return p; } 
|	"[" e=Exp() "]"				{ p=Exp.indexop(lhs, e); L(p,i); return p; }
|	":" n=<NAME> a=FuncArgs()	{ p=Exp.methodop(lhs, n.image,a); L(p,i); return p; } 
|	a=FuncArgs() 				{ p=Exp.functionop(lhs, a); L(p,i); return p; }
}

FuncArgs FuncArgs():
{
	List<Exp> el=null;
	TableConstructor tc;
	LuaString s;
	FuncArgs a;
	long i = LineInfo();
}
{
	"(" ( el=ExpList() )? ")"	{ a=FuncArgs.explist(el); L(a,i); return a; }
|	tc=TableConstructor()		{ a=FuncArgs.tableconstructor(tc); L(a,i); return a; }
|	s=Str()						{ a=FuncArgs.string(s); L(a,i); return a; }
}

List<Name> NameList():
{
	List<Name> l = new ArrayList<Name>();
	Token name;
}
{
	name=<NAME> {l.add(new Name(name.image));} ( LOOKAHEAD(2) "," name=<NAME> {l.add(new Name(name.image));} )* {return l;}
}

List<Exp> ExpList():
{
	List<Exp> l = new ArrayList<Exp>();
	Exp e;
}
{
	e=Exp() {l.add(e);} ( "," e=Exp() {l.add(e);} )* {return l;}
}

Exp SimpleExp():
{
	Token n;
	LuaString s;
	Exp e;
	TableConstructor c;
	FuncBody b;
	long i = LineInfo();
}
{
	<NIL>					{ e=Exp.constant(LuaValue.NIL); L(e,i); return e; } 		
|	<TRUE>					{ e=Exp.constant(LuaValue.TRUE); L(e,i); return e; }
|	<FALSE>					{ e=Exp.constant(LuaValue.FALSE); L(e,i); return e; }
|	n=<NUMBER>				{ e=Exp.numberconstant(n.image); L(e,i); return e; }
|	s=Str()					{ e=Exp.constant(s); L(e,i); return e; }
|	"..."					{ e=Exp.varargs(); L(e,i); return e; } 
|	c=TableConstructor()	{ e=Exp.tableconstructor(c); L(e,i); return e; }
|	b=FunctionCall() 		{ e=Exp.anonymousfunction(b); L(e,i); return e; }
|	e=PrimaryExp()			{ return e; } 
}

LuaString Str():
{}
{
	<STRING>		{ return Str.quoteString(token.image); } 
|	<CHARSTRING>	{ return Str.charString(token.image); }
|	<LONGSTRING0>	{ return Str.longString(token.image); }
|	<LONGSTRING1>	{ return Str.longString(token.image); }
|	<LONGSTRING2>	{ return Str.longString(token.image); }
|	<LONGSTRING3>	{ return Str.longString(token.image); }
|	<LONGSTRINGN>	{ return Str.longString(token.image); }
}

Exp Exp():
{
	Exp e,s;
	int op;
	long i = LineInfo();
}
{
	( e=SimpleExp() | op=Unop() s=Exp() {e=Exp.unaryexp(op,s);}) 
		(LOOKAHEAD(2) op=Binop() s=Exp() {e=Exp.binaryexp(e,op,s);} )* { L(e,i); return e; }
}

FuncBody FunctionCall():
{
	FuncBody b;
	long i = LineInfo();
}
{
	<FUNCTION> b=FuncBody() { L(b,i); return b; }
}

FuncBody FuncBody():
{
	ParList pl=null;
	Block b;
	FuncBody f;
	long i = LineInfo();
}
{
	"(" ( pl=ParList() )? ")" b=Block() <END> { f=new FuncBody(pl,b); L(f,i); return f; }
}

ParList ParList():
{
	List<Name> l=null;
	boolean v=false;
	ParList p;
	long i = LineInfo();
}
{
	l=NameList() ( "," "..." { v=true; } )? { p=new ParList(l,v); L(p,i); return p; } 
|	"..." {  p=new ParList(null,true); L(p,i); return p; }
}

TableConstructor TableConstructor():
{
	TableConstructor c = new TableConstructor();
	List<TableField> l = null;
	long i = LineInfo();
}
{
	"{" ( l=FieldList() {c.fields=l;} )? "}" { L(c,i); return c; }
}

List<TableField> FieldList():
{
	List<TableField> l = new ArrayList<TableField>();
	TableField f;
}
{
	f=Field() {l.add(f);} (LOOKAHEAD(2) FieldSep() f=Field() {l.add(f);})* (FieldSep())? { return l; }
}

TableField Field():
{
	Token name;
	Exp exp,rhs;
	TableField f;
	long i = LineInfo();
}
{
	"[" exp=Exp() "]" "=" rhs=Exp()         { f=TableField.keyedField(exp,rhs); L(f,i); return f; }
|	LOOKAHEAD(2) name=<NAME> "=" rhs=Exp()  { f=TableField.namedField(name.image,rhs); L(f,i); return f; }
|	rhs=Exp()                               { f=TableField.listField(rhs); L(f,i); return f; }
}

void FieldSep():
{}
{
	"," | ";"
}

int Binop():
{}
{
	"+"   { return Lua.OP_ADD; }
|	"-"   { return Lua.OP_SUB; } 
|	"*"   { return Lua.OP_MUL; } 
|	"/"   { return Lua.OP_DIV; } 
|	"^"   { return Lua.OP_POW; } 
|	"%"   { return Lua.OP_MOD; } 
|	".."  { return Lua.OP_CONCAT; } 
|	"<"   { return Lua.OP_LT;  } 
|	"<="  { return Lua.OP_LE;  } 
|	">"   { return Lua.OP_GT;  } 
|	">="  { return Lua.OP_GE;  }
|	"=="  { return Lua.OP_EQ;  } 
|	"~="  { return Lua.OP_NEQ; } 
|	<AND> { return Lua.OP_AND; } 
|	<OR>  { return Lua.OP_OR;  }
}

int Unop():
{}
{
	"-"   { return Lua.OP_UNM; }
|	<NOT> { return Lua.OP_NOT; } 
|	"#"   { return Lua.OP_LEN; }
}
