/*@bgen(jjtree) Generated By:JJTree: Do not edit this line. Mini.jj */
/*@egen*/                                                                                            

PARSER_BEGIN(MiniParser)
package Mini;

public class MiniParser/*@bgen(jjtree)*/implements MiniParserTreeConstants/*@egen*/ {/*@bgen(jjtree)*/
  protected static JJTMiniParserState jjtree = new JJTMiniParserState();

/*@egen*/
  private static Token expr_token;

  final static void jjtreeOpenNodeScope(Node n) {}
  final static void jjtreeCloseNodeScope(Node n) {((SimpleNode)n).closeNode();}
}

PARSER_END(MiniParser)

SKIP : /* WHITE SPACE */
{
	" "
|	"\t"
|	"\n"
|	"\r"
|	"\f"
}

/* Single-line Comments
 */
MORE :
{
  "--" : SINGLE_LINE_COMMENT_STATE
}

<SINGLE_LINE_COMMENT_STATE> SPECIAL_TOKEN :
{
  <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n" > : DEFAULT
}

<SINGLE_LINE_COMMENT_STATE> MORE :
{
  < ~[] >
}

/* A program consists of a number of function declarations with a
 * distinguished function `main' that starts the program.
 */
void Program() : {/*@bgen(jjtree) Program */
  ASTProgram jjtn000 = new ASTProgram(JJTPROGRAM);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/}
{/*@bgen(jjtree) Program */
        try {
/*@egen*/
	(FunDecl())*
	<EOF>/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

/* "FUN" Ident() "(" NameList() ")" = Expr()
 */
void FunDecl() :
{/*@bgen(jjtree) FunDecl */
  ASTFunDecl jjtn000 = new ASTFunDecl(JJTFUNDECL);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/
  String   s;
  Token    t;
}
{/*@bgen(jjtree) FunDecl */
        try {
/*@egen*/
	t = "FUN"  { jjtn000.setPosition(t.beginLine, t.beginColumn); }

	Ident()

	<LPAREN> 
	[
	 Ident() (<COMMA> Ident())*
	]
	<RPAREN>

	<ASSIGN>

	Expr()/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/ /* Body expression */
}

void Expr() :
{/*@bgen(jjtree) Expr */
  ASTExpr jjtn000 = new ASTExpr(JJTEXPR);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/
  int kind=-1;
  int un_op=-1;
}
{/*@bgen(jjtree) Expr */
        try {
/*@egen*/
  	IfExpr()
|
	LetExpr()
|
	Term() [kind = AddOp() Expr() { jjtn000.setKind(kind); }] 
|
	un_op = UnOp() { jjtn000.setUnOp(un_op); } Expr()/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/ 
}

/*
 * The disambiguating algorithm of JavaCC automatically binds dangling
 * else's to the innermost if statement. The LOOKAHEAD specification
 * is to tell JavaCC that we know what we are doing.
 */
void IfExpr() :
{/*@bgen(jjtree) IfExpr */
  ASTIfExpr jjtn000 = new ASTIfExpr(JJTIFEXPR);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/
  Token t=null;
}
{/*@bgen(jjtree) IfExpr */
        try {
/*@egen*/
	t = "IF"  { jjtn000.setPosition(t.beginLine, t.beginColumn); }
	Expr() "THEN" Expr() [ LOOKAHEAD(1) "ELSE" Expr() ] "FI"/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

void LetExpr() :
{/*@bgen(jjtree) LetExpr */
  ASTLetExpr jjtn000 = new ASTLetExpr(JJTLETEXPR);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/
  Token t=null;
}
{/*@bgen(jjtree) LetExpr */
        try {
/*@egen*/
	t = "LET" { jjtn000.setPosition(t.beginLine, t.beginColumn); }
	(Ident() <ASSIGN> Expr())+ "IN" Expr()/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

Token FunAppl() :
{/*@bgen(jjtree) FunAppl */
  ASTFunAppl jjtn000 = new ASTFunAppl(JJTFUNAPPL);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/
  Token t=null;
}
{/*@bgen(jjtree) FunAppl */
        try {
/*@egen*/
	t = Ident() { jjtn000.setPosition(t.beginLine, t.beginColumn); }
	<LPAREN>
	[Expr() (<COMMA> Expr())*] <RPAREN>/*@bgen(jjtree)*/
        {
          jjtree.closeNodeScope(jjtn000, true);
          jjtc000 = false;
          jjtreeCloseNodeScope(jjtn000);
        }
/*@egen*/
	{ return t; }/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
  
}

void Term():
{/*@bgen(jjtree) Term */
  ASTTerm jjtn000 = new ASTTerm(JJTTERM);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/
  int kind=-1;
}
{/*@bgen(jjtree) Term */
        try {
/*@egen*/
	Factor() [kind = MultOp() { jjtn000.setKind(kind); } Term()]/*@bgen(jjtree)*/
        {
          jjtree.closeNodeScope(jjtn000, true);
          jjtc000 = false;
          jjtreeCloseNodeScope(jjtn000);
        }
/*@egen*/
	{ jjtn000.setPosition(expr_token.beginLine, expr_token.beginColumn); }/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

void Factor() :
{/*@bgen(jjtree) Factor */
  ASTFactor jjtn000 = new ASTFactor(JJTFACTOR);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/
  int kind=-1;
}
{/*@bgen(jjtree) Factor */
        try {
/*@egen*/
	Element() [kind = CmpOp() { jjtn000.setKind(kind); } Factor()]/*@bgen(jjtree)*/
        {
          jjtree.closeNodeScope(jjtn000, true);
          jjtc000 = false;
          jjtreeCloseNodeScope(jjtn000);
        }
/*@egen*/
	{ jjtn000.setPosition(expr_token.beginLine, expr_token.beginColumn); }/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

void Element()       : {}
{
/* expr_token is a global variable used to remember the position of an Expr() node 
*/
  LOOKAHEAD(2)
	expr_token = FunAppl()
|
	expr_token = Ident()
|
	expr_token = Integer()
|
	expr_token = <LPAREN> Expr() <RPAREN>
}

Token Integer() : 
{/*@bgen(jjtree) Integer */
  ASTInteger jjtn000 = new ASTInteger(JJTINTEGER);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/
  int num;
  Token t; // Contains lexem and line/column number
}
{/*@bgen(jjtree) Integer */
        try {
/*@egen*/
	t = <INTEGER>/*@bgen(jjtree)*/
        {
          jjtree.closeNodeScope(jjtn000, true);
          jjtc000 = false;
          jjtreeCloseNodeScope(jjtn000);
        }
/*@egen*/
	{ 
          jjtn000.setValue(Integer.parseInt(t.image));
          jjtn000.setPosition(t.beginLine, t.beginColumn);
	  return t;
  	}/*@bgen(jjtree)*/
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

Token Ident() : 
{/*@bgen(jjtree) Ident */
  ASTIdent jjtn000 = new ASTIdent(JJTIDENT);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/
  String name;
  Token t; // Contains lexem and line/column number
}
{/*@bgen(jjtree) Ident */
        try {
/*@egen*/
	(t = <TRUE> | t = <FALSE> | t = <READ> | t = <WRITE> | 
	 t = <IDENT>)/*@bgen(jjtree)*/
        {
          jjtree.closeNodeScope(jjtn000, true);
          jjtc000 = false;
          jjtreeCloseNodeScope(jjtn000);
        }
/*@egen*/
	{ 
	  jjtn000.setName(t.image);
	  jjtn000.setPosition(t.beginLine, t.beginColumn);
	  return t;
	}/*@bgen(jjtree)*/
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

int AddOp()       :
{
  Token t=null;
}
{
	(t = <PLUS> | t = <MINUS> | t = <OR>)
  {
    return t.kind;
  }
}

int MultOp()       :
{
  Token t=null;
}
{
	(t = <MULT> | t = <DIV> | t = <MOD> | t = <AND>)
  {
    return t.kind;
  }
}

int CmpOp()       :
{
  Token t=null;
}
{
	(t = <EQ> | t = <NEQ> | t = <LEQ> | t = <GEQ> | t = <GT> | t = <LT>)
  {
    return t.kind;
  }
}

int UnOp()       :
{
  Token t=null;
}
{
	(t = <MINUS> | t = <NOT>)
  {
    return t.kind;
  }
}


TOKEN : /* Boolean and arithmetic operands */
{
	< GT  : ">" >
|
	< LT  : "<" >
|
	< GEQ : ">=" >
|
	< LEQ : "<=" >
|
	< EQ  : "==" >
|
	< NEQ : "!=" >
|
	< NOT : "!" >
|
	< FALSE : "FALSE" >
|
	< TRUE : "TRUE" >
|
	< AND : "AND" >
|
	< OR : "OR" >
|
	< PLUS : "+">
|
	< MINUS : "-">
|
	< MULT : "*">
|
	< MOD : "%">
|
	< DIV : "/">
|
	< LPAREN : "(">
|
	< RPAREN : ")">
|
	< ASSIGN : "=">
|
	< COMMA : ",">
|
	< READ : "READ">
|
	< WRITE : "WRITE">
}

/* Has to be and the, otherwise every string wil become an <IDENT> token
 * Who knows why ...
 */
TOKEN : /* LITERALS */
{
	< #DIGIT:   ["0"-"9"] >
|
	< #LETTER:  ["a"-"z", "A"-"Z"] >
|
	< IDENT:    <LETTER> (<LETTER> | <DIGIT> | "_")* >
|
	< INTEGER:  (<DIGIT>)+ >
|
	< STRING:   "\"" (~["\"", "\n", "\r"])* "\""  >
}