/***************************************************************
 * SfalLexer
 * @author Antonio Cruz adc2104@columbia.edu
 * @version $Id: grammar-as-semant.g,v 1.28 2007/12/16 07:27:54 admin Exp $
 ***************************************************************/

class SfalLexer extends Lexer;

options {
    k = 2;  
    charVocabulary = '\3'..'\377';
    testLiterals = false;
    exportVocab = Sfal;
}

protected
ALPHA   : 'a'..'z' | 'A'..'Z' | '_' ;
    
protected
DIGIT   : '0'..'9';

protected
HEX		: '0'..'9' | 'A'..'F' | 'a'..'f' ;

HEXNUM	: "0x" HEX HEX HEX HEX HEX HEX ;

ID  options { testLiterals = true; }
        : ALPHA (ALPHA|DIGIT)*
        ;

NUMBER  : (DIGIT)+ ('.' (DIGIT)* )? 
		| '.' (DIGIT)+ 
		; 

WS      : (' ' | '\t')+	
			{ $setType(Token.SKIP); }
        ;

NL      : ('\n' | ('\r' '\n') => '\r' '\n' | '\r') 
			{ $setType(Token.SKIP); newline(); }
        ;

COMMENT : ( 
			"/*" 
				( options {greedy=false;} 
				: (NL)
				| ~( '\n' | '\r' ))* 
			"*/"
			
            | "//" (~( '\n' | '\r' ))* (NL)
          
          ) { $setType(Token.SKIP); }
        ;
        
LPAREN 	: '(';
RPAREN  : ')';
LBRACE  : '{';
RBRACE  : '}';

MULT    : '*';
PLUS    : '+';
MINUS   : '-';
DIV     : '/';
MOD     : '%';

SEMI    : ';';            
ASGN    : '=';
COMMA   : ',';

GE      : ">=";
LE      : "<=";
GT      : '>';
LT      : '<';

EQ      : "==";
NEQ     : "!=";

OR 		: "||";
AND 	: "&&";


/* mx */
STRING  : '"'!
              (  ~('"' | '\n' | '#')
               | ('"'!'"')
              )*
          '"'!
        ;

/* Esterel
StringConstant	: '"'!
					( ~('"' | '\n')
					| ('"'! '"')
					)*
					'"'!
				;
*/

/***************************************************************

	SfalParser 
	
	program -> (statement)*
	
	statement ->
		if-stmt |
		assignment |
		func-call-stmt |
		loop_stmt |
		animator ;
	
	if-stmt -> 'if' '(' expression ')' block-stmt ( 'else' block-stmt )?
	
	block-stmt -> '{' (statement)* '}'
	
	assignment -> l-value ('=') expression ';'
	
	func-call-stmt -> func-call ';'
	
	func-call -> ID '(' expr-list ')'
	
	expr-list -> expression (',' expression)* | e
	
	animator -> 'animate' block-stmt
	
	loop_stmt -> 'loop' '(' expression ')' block-stmt
        
	expression -> logic-term ( '||' logic-term )*
	
	logic-term -> logic-factor ( '&&' logic-factor )*
	
	logic-factor -> ('not')? relat-expr
	
	relat-expr -> arith-expr ( ('>=' | '<=' | '>' | '<' | '==' | '!=') arith-expr )?
	
	arith-expr -> arith-term ( ('+' | '-') arith-term )*
	
	arith-term -> arith-factor ( ('*' | '/' | '%') arith-factor )*
	
	arith-factor -> ('-') r-value
	
	r-value -> l-value | func-call | constant 
	
	l-value -> ID
	
	constant -> NUMBER | HEXNUM

 ***************************************************************/


class SfalParser extends Parser;

options {
    k = 2;
    buildAST = true;
}

tokens {
   PROG;
   STATEMENT;   
   BLOCK_STMT;
   FUNC_CALL;
   EXPR_LIST;
   UPLUS;
   UMINUS;
}


program
        : ( statement )* 
          {#program = #([PROG, "PROG"], program); }
        ;
    
statement
        : if_stmt
        | assignment
        | func_call_stmt
        | loop_stmt
        | animator
        ;
    
if_stmt
        : "if"^ LPAREN! expression RPAREN! (block_stmt | statement)
            (options {greedy = true;}: "else"! (block_stmt | statement) )?
        ;

block_stmt
        : LBRACE! (statement)* RBRACE!
            { #block_stmt = #([BLOCK_STMT, "BLOCK_STMT"], block_stmt); }
        ;
        
assignment
        : l_value ( ASGN^ ) expression SEMI!
        ;

func_call_stmt
        : func_call SEMI!     
        ;
    
func_call
        : ID LPAREN! expr_list RPAREN! 
            {#func_call = #([FUNC_CALL, "FUNC_CALL"], func_call); }
        ;
    
expr_list
        : expression ( COMMA! expression )* 
            {#expr_list = #([EXPR_LIST, "EXPR_LIST"], expr_list); }
            
        | /* necessary for function with no arguments */
        	{#expr_list = #([EXPR_LIST,"EXPR_LIST"], expr_list); }
        ;   
    
animator
        : "animate"^ block_stmt
        ;

loop_stmt
        : "loop"^ LPAREN! expr_list RPAREN! block_stmt
        ;


/***************************************************************

	Expressions

 ***************************************************************/
        
expression
        : logic_term ( OR^ logic_term )*
        ;
    
logic_term
        : logic_factor ( AND^ logic_factor )*
        ;
    
logic_factor
        : ("not"^)? relat_expr
        ;
    
relat_expr
        : arith_expr ( (GE^ | LE^ | GT^ | LT^ | EQ^ | NEQ^) arith_expr )?
        ;
    
arith_expr
        : arith_term ( (PLUS^ | MINUS^) arith_term )*
        ;
    
arith_term
        : arith_factor ( (MULT^ | LDV^ | MOD^ | DIV^) arith_factor )*
        ;

arith_factor
        : PLUS! r_value 
            {#arith_factor = #([UPLUS, "UPLUS"], arith_factor); } 
            
        | MINUS! r_value 
            {#arith_factor = #([UMINUS, "UMINUS"], arith_factor); }
        
        | r_value 
        ;

r_value
        : l_value 
        | func_call 
        | NUMBER 
        | STRING
        | "true"
        | "false"
        | HEXNUM
        | LPAREN! expression RPAREN!
        ;

l_value
        : ID
        ;
        
        
/***************************************************************

	TreeWalker

 ***************************************************************/
class SfalWalker extends TreeParser;

options {
    importVocab = Sfal;
}

{
  SymbolTable top = null;
  Validator validator = new Validator(true);
  Translator translator = null;
  Debug log = new Debug(false);
  boolean isAnimator = false;
  boolean isIfBlock = false;
  StringBuffer programBuffer = new StringBuffer();
}

program
{
	String s = null;
	/*programBuffer.append("package {\n" +
		"import flash.display.Sprite;\n" +
        "import flash.text.TextField;\n" +
        "import flash.events.Event;\n\n" +
        "public class {0} extends Sprite {\n\n" +
        "public function {0}() {\n"
        );*/
    }
    
	: #(PROG 
		{ 
			SymbolTable saved_environment = top; top = new SymbolTable(top); 
			translator = new Translator(top);
		}
      
		(s=stmt)* 
		
		{ top = saved_environment; }
		)
	{
	
	programBuffer.append("addEventListener(Event.ENTER_FRAME, animate);");
	programBuffer.append("\n}");
	
	
	programBuffer.append("public function animate(event:Event):void {");
	
	programBuffer.append(translator.translateAnimator());
	
	programBuffer.append("\n}");
	
	// System.out.println("GRAMMAR:TOP: " + top);
	
	programBuffer.append(translator.translateSymbolTable());
	
	
	programBuffer.append("\n} ");
	programBuffer.append("\n} ");
	}
	;
	
stmt returns [String s]
	{
		s = null;
		String s1=null, s2=null, s3=null; // e=null; 
		Expr e1, e2;
		java.util.LinkedList l = new java.util.LinkedList();
	}
	
	:	#(ASGN e1=expr e2=expr ) 
		{
			/* ID = FUNC_CALL | ID | LITERAL | EXPR */
			
			// System.err.println("ASGN: E1: " + e1.toString() + " E2: "  + e2.toString());
			
			//-------------------------------------------------
			// Handle SymbolTable 
			//-------------------------------------------------
			
			if (e2.type.equals("FUNC_CALL")) {
				
				// System.out.println("in symtable already: " + e1.value + ": " + top.get(e1.value));
				
				
				/* local variable declarations */
				boolean shoulddeclare = 
					/* variable e1.value not in symboltable yet */
					(top.get(e1.value) == null)
					
					/* This means we're in a new scope, declare
					local variables if necessary */ 
					&& (top.outer != null); 
					
				/* Map ID to the return type of the function call */
				top.put(e1.value,
					(String) Meta.FUNC_TO_TYPE.get(e2.value), null);
					
				if (shoulddeclare) {
				
					if (!isAnimator) {
						programBuffer.append(translator.translateSymbolTableLocal());
					}
					else {
						translator.buildAnimator(translator.translateSymbolTableLocal());
					}
					
				}
			}
			else {
			
				
				/* 
				Map ID to "type" associated to the node name, e.g. "Number", "Sprite"
				If e2 is an ID, get the type from the SymbolTable
				*/
				
				if (e2.type.equals("ID")) {
					SymbolTable.SymTabEntry t = (SymbolTable.SymTabEntry) top.get(e2.value);
					if (t==null) throw new RecognitionException("undeclared right value ID: " 
						+ e2.value);
				}
				else {
					top.put(e1.value, (String) Meta.NODE_TO_TYPE.get(e2.type), e2.value);
					
					if (!isAnimator) {
						programBuffer.append(translator.translateSymbolTableLocal());
					}
					else {
						translator.buildAnimator(translator.translateSymbolTableLocal());
					}
					
				}
				
				
			}
			
			// System.out.println("GRAMMAR:TOP: " + top);
			
			//-------------------------------------------------
			// END Handle SymbolTable 
			//-------------------------------------------------
			
			if (isIfBlock) {/* do nothing */}
			else {
				if (!isAnimator) {
					programBuffer.append(
					translator.translate(ASGN, e1.toString() + e2.toString()));	
				}
				else {
					translator.buildAnimator(
					translator.translate(ASGN, e1.toString() + e2.toString()));
				}
			}
			
			
			if (!validator.validate(ASGN, e1, e2, top))
        		throw new RecognitionException("BAD Assignment: " + e1.error);
			
			
			// return this for if statement
			s = translator.translate(ASGN, e1.toString() + e2.toString());
			log.trace(top);
		}
	| 	#("if" 	{
				isIfBlock=true;
				StringBuffer ifBuffer = new StringBuffer("if (");
				} 
			e1=expr {ifBuffer.append(e1).append(") {");}
			s2=stmt 
			{	ifBuffer.append(s2).append('}');
				if (_t != null) {
					ifBuffer.append(" else {");
				}
			}
			(s3=stmt)? ) {
				/*if (_t != null) {
					ifBuffer.append("}");
				}*/
				
			if (!isAnimator) {
				programBuffer.append(
				ifBuffer.toString());	
			}
			else {
				translator.buildAnimator(
				ifBuffer.toString());
			}
			isIfBlock=false;
			
			}
		
	| 	#("loop" { } 
			l=expr_list { 
			
			if (!validator.validateLoop(l))
        	throw new RecognitionException("BAD LOOP format");
				
			programBuffer.append(translator.translate(1001, 
				((Expr)l.get(0)).toString() + 
				((Expr)l.get(1)).toString() + 
				((Expr)l.get(2)).toString())); }
				
			s2=stmt ) 
	
	| 	#(BLOCK_STMT 	{if (!isAnimator) programBuffer.append("{");}
			
			{ 
			SymbolTable saved_environment = top; 
			top = new SymbolTable(top); 
			/* Update the translator w/ the SymbolTable for the new scope, 
			in case we need to declare local variables */
			translator.symTable=top;
			}
			
			(s=stmt)* ) {if (!isAnimator) programBuffer.append("}");}
			
			{ 
			top = saved_environment; 
			/* Update the translator with the saved SymbolTable */
			translator.symTable=top;
			}
	
	|	#("animate" {isAnimator=true;}
			s2=stmt  ) {isAnimator=false;}
	;


expr returns [Expr e]
	{ 
		e = null;
		String s; 
		Expr a, b; 
		java.util.LinkedList l;
	}

	: 	#(FUNC_CALL ID l=expr_list) 
		{ 
			e = new Expr("FUNC_CALL");
			e.value = #ID.getText();
			e.values = l;
			
			if (!validator.validate(FUNC_CALL, e, top)) 
				throw new RecognitionException("BAD FUNC_CALL: " + e.value);
		}
	| 	#(MULT a=expr b=expr)	
		{
			e = new Expr("*");
			e.child1=a;
    		e.child2=b;
    		
    		// System.out.println("MULT: " + e);
			// System.out.println("Top: " + top);
		
			if (!validator.validate(MULT, e, top)) 
				throw new RecognitionException("RE: *: " + e.error);
		}
    | 	#(DIV a=expr b=expr)	
    	{
    		e = new Expr("/");
			e.child1=a;
    		e.child2=b;
			if (!validator.validate(DIV, e, top)) 
				throw new RecognitionException("RE: /: " + e.error);
		}
    | 	#(MOD a=expr b=expr)	
    	{
    		e = new Expr("%");
			e.child1=a;
    		e.child2=b;
    		if (!validator.validate(MOD, e, top)) 
				throw new RecognitionException("RE: %: " + e.error);
		}
    | 	#(PLUS a=expr b=expr) 
    	{
    		e = new Expr("+");
    		e.child1=a;
    		e.child2=b;
    		if (!validator.validate(PLUS, e, top)) 
				throw new RecognitionException("RE: +: " + e.error);
		}
	| 	#(MINUS a=expr b=expr)
		{
			e = new Expr("-");
			e.child1=a;
    		e.child2=b;
			/* if (!validator.validate(MINUS, e, top)) 
				throw new RecognitionException("RE: -: " + e.error); */
		}
	| 	#(UMINUS a=expr) 		
		{
			e = new Expr("U-");
			e.child1=a;
			
			/* fix
			if (!validator.validate(UMINUS, e, top)) 
				throw new RecognitionException("RE: U-: " + e.error); */
		}
	|	#(AND a=expr b=expr)	
		{
			e = new Expr("&&");
			e.child1=a;
    		e.child2=b;
			if (!validator.validate(AND, e, top)) 
				throw new RecognitionException("RE: &&: " + e.error);
		} 
	| 	#(OR a=expr b=expr)	
		{
			e = new Expr("||");
			e.child1=a;
    		e.child2=b;
			if (!validator.validate(OR, e, top)) 
				throw new RecognitionException("RE: ||: " + e.error);
		} 
	| 	#(GT a=expr b=expr) 	
		{
			e = new Expr(">");
			e.child1=a;
    		e.child2=b;
    		
    		/* fix
			if (!validator.validate(GT, e, top)) 
				throw new RecognitionException("RE: >: " + e.error); */
		}
	| 	#(LT a=expr b=expr) 	
		{
			e = new Expr("<");
			e.child1=a;
    		e.child2=b;
    		/* fix
			if (!validator.validate(LT, e, top)) 
				throw new RecognitionException("RE: <: " + e.error); */
		}
	| 	#(EQ a=expr b=expr)	
		{
			e = new Expr("==");
			e.child1=a;
    		e.child2=b;
			if (!validator.validate(EQ, e, top)) 
				throw new RecognitionException("RE: ==: " + e.error);
		}
	| 	#(NEQ a=expr b=expr)	
		{
			e = new Expr("!=");
			e.child1=a;
    		e.child2=b;
			if (!validator.validate(NEQ, e, top)) 
				throw new RecognitionException("RE: !=: " + e.error);
		}
	| 	#(GE a=expr b=expr)	
		{
			e = new Expr(">=");
			e.child1=a;
    		e.child2=b;
			if (!validator.validate(GE, e, top)) 
				throw new RecognitionException("RE: >=: " + e.error);
		}
	| 	#(LE a=expr b=expr)	
		{
			e = new Expr("<=");
			e.child1=a;
    		e.child2=b;
			if (!validator.validate(LE, e, top)) 
				throw new RecognitionException("RE: <=: " + e.error);
		}
	
	| 	num:NUMBER		
		{
			e = new Expr("Number");
			e.value=num.getText();
		}
	|	str:STRING 		
		{
			e = new Expr("String");
			e.value=str.getText();
		}
	|	hex:HEXNUM 		
		{
			e = new Expr("Hex");
			e.value=hex.getText();
		}
	|	"true" 			
		{
			e = new Expr("true");
			e.value="true";
		}
	|	"false" 		
		{
			e = new Expr("false");
			e.value = "false";
		}
	| 	id:ID 			
		{
			/* since untyped do this in the ASGN stmt
			s = top.get(id.getText()) == null ? null : id.getText();
			if (s == null) {
				/* don't want to do this with an expression list 
				there, we are only reading the id
				otherwise, we can be declaring it 
				
				top.put(id.getText(), "ID");
			}*/
			
			e = new Expr("ID");
			e.value = id.getText();
			
			/*
			System.out.println("SymbolTable: ");
			System.out.println(top);*/
		}
	
	; 


expr_list returns [java.util.LinkedList l]
{
	l = new java.util.LinkedList();
	Expr e = null;
}
	: 	#(EXPR_LIST
		(e=expr 	{ l.addLast(e); })* )
	;
   
        
