text: grammar Jnu;
  options {
    output = AST;              // build trees
    ASTLabelType = JnuAST;
  }
  
  tokens {
    METHOD_DECL; // function definition
    ARG_DECL;    // parameter
    BLOCK;
    MEMBERS;     // class body
    VAR_DECL;
    FIELD_DECL;
    CALL;
    ELIST;       // expression list
    EXPR; 	   // root of an expression
    ASSIGN='=';
    EXTENDS;
  }
  
  compilationUnit
      :   ( classDefinition | varDeclaration | methodDeclaration )+ EOF
      ;
  
  // START: class
  classDefinition
      :   'class' ID superClass? '{' classMember+ '}' ';'
          -> ^('class' ID superClass? ^(MEMBERS classMember+))
      ;
  superClass
  	:	':' 'public' ID -> ^(EXTENDS ID)
  	;
  // END: class
  
  classMember
  	:	type ID ('=' expression)? ';' -> ^(FIELD_DECL type ID expression?)
  	|	methodDeclaration
  	|	'public' ':' -> // throw away; just making input valid C++
  	;
  	
  // START: method
  methodDeclaration
      :   type ID '(' formalParameters? ')' block
          -> ^(METHOD_DECL type ID formalParameters? block)
      ;
  // END: method
  
  formalParameters
      :   type ID (',' type ID)* -> ^(ARG_DECL type ID)+
      ;
  
  type:   'float'
      |   'int'
      |	'void'
      |	ID // class type name
      ;
  
  // START: block
  block
      :   '{' statement* '}' -> ^(BLOCK statement*)
      ;
  // END: block
  
  // START: var
  varDeclaration
      :   type ID ('=' expression)? ';' -> ^(VAR_DECL type ID expression?)
      ;
  // END: var
  
  statement
      :   block
      |	varDeclaration
      |   'return' expression? ';' -> ^('return' expression?)
      |   postfixExpression // handles function calls like f(i);
          (   '=' expression -> ^('=' postfixExpression expression)
          |   -> ^(EXPR postfixExpression)
          )
          ';' 
      |	';' -> // empty statement      
      ;
  
  expressionList
      :   expression (',' expression)* -> ^(ELIST expression+)
      |   -> ELIST
      ;
  
  expression
      :   addExpression -> ^(EXPR addExpression)
      ;
      
  addExpression
  	:	postfixExpression ('+'^ postfixExpression)*
  	;
  
  // START: call
  postfixExpression
      :   (primary->primary)
      	(	options {backtrack=true;}
  		:	'.' ID '(' expressionList ')' -> ^(CALL ^('.' $postfixExpression ID))
  		|	'.' ID						  -> ^('.' $postfixExpression ID)
  		|	'(' expressionList ')'        -> ^(CALL $postfixExpression)
  		)*
      ;
  // END: call
  
  suffix[CommonTree expr]
  options {backtrack=true;}
  	:	'.' ID '(' expressionList ')' -> ^(CALL ^('.' {$expr} ID))
  	|	'.' ID						  -> ^('.' {$expr} ID)
  	|	'(' expressionList ')'        -> ^(CALL {$expr})
  	;
  	
  primary
      :   'this'
      |	'super'
      |	ID
      |   INT
      |   '(' expression ')' -> expression
      ;
  
  // LEXER RULES
  
  ID  :   LETTER (LETTER | '0'..'9')*
      ;
  
  fragment
  LETTER  :   ('a'..'z' | 'A'..'Z')
      ;
  
  INT :   '0'..'9'+
      ;
  
  WS  :   (' '|'\r'|'\t'|'\n') {$channel=HIDDEN;}
      ;
  
  SL_COMMENT
      :   '//' ~('\r'|'\n')* '\r'? '\n' {$channel=HIDDEN;}
      ;
  