grammar AmmoScript;

options {
  language = Java;
  output = AST;
}

tokens {
  CLASSDEF;
  SUPERIORS;
  DEFINITIONS;
  
  VARDEF;
  METHODDEF;
  PARAMDEFS;
  PARAMDEF;
  
  SOFTLINK;
  //CALL;
  PARAMETER;
  BLOCK;
  
  SWITCH;
  SWITCHCASE;
}

@header {
  package hu.swankey.ammo.common.script;
  import java.util.LinkedList;
  import java.util.Map;
  import java.util.HashMap;
  import hu.swankey.ammo.common.yggdrasil.definition.YAttributeDefinition;
  import hu.swankey.ammo.common.yggdrasil.definition.YMethodDefinition;
  import hu.swankey.ammo.common.yggdrasil.definition.ComplexYClassDefinition;
  import hu.swankey.ammo.common.yggdrasil.basics.Reference;
  
  import hu.swankey.ammo.common.yggdrasil.basics.YObject;
  
  import hu.swankey.ammo.common.yggdrasil.basics.YInteger;
  import hu.swankey.ammo.common.yggdrasil.basics.YString;
  import hu.swankey.ammo.common.script.statements.Block;
  import hu.swankey.ammo.common.script.statements.Call;
  import hu.swankey.ammo.common.script.statements.Constant;
  import hu.swankey.ammo.common.script.statements.DynamicVariable;
  import hu.swankey.ammo.common.script.statements.Expression;
  import hu.swankey.ammo.common.script.statements.Statement;
  import hu.swankey.ammo.common.script.statements.Switch;
  import hu.swankey.ammo.common.script.statements.Variable;
  import hu.swankey.ammo.common.script.yunits.YClass;
  import hu.swankey.ammo.common.script.yunits.YMethod;
 }

@lexer::header { package hu.swankey.ammo.common.script; }

@members {
    private List<String> errors = new LinkedList<String>();
    
    public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
        String hdr = getErrorHeader(e);
        String msg = getErrorMessage(e, tokenNames);
        errors.add(hdr + " " + msg);
    }
    
    public List<String> getErrors() {
        return errors;
    }
    
    
    public Expression createExpr(Expression expr1, String operator, Expression expr2) {
    
      if (expr1 != null){
        Map<String, Expression> map = new HashMap<String, Expression>();
        map.put("value", expr2);  
        return new Call(expr1, operator, map);
      } else
        return expr2;
        
    }    
}


/*------------------------------------------------------------------
 * PARSER RULES
 *------------------------------------------------------------------*/

classDeclaration returns [ComplexYClassDefinition def]
  : level=accessLevel id=IDENTIFIER
  {
    $def = ComplexYClassDefinition.yclass().create($id.text);
    $def.setAccessLevel($level.level);
  }
  
  ( KEYWORD_EXTENDS
    p1=path[false] { $def.addSuperior($p1.link); }
    (COLON p2=path[false] { $def.addSuperior($p2.link); } )*
  )?

  OPENING_PARENTHESIS
    ( var=variableDeclaration  { $def.addElement($var.def); }
      | method=methodDeclaration { $def.addMethod($method.def); }
    )*
  CLOSING_PARENTHESIS
  { $def.setSource($classDeclaration.text); }
  ;
  

variableDeclaration returns [YAttributeDefinition def]
  : accessLevel t=path[false]  id=IDENTIFIER  (ASSIGN expression)? SEMICOLON
  {
    //$def = new YAttributeDefinition($id.text, $t.link.getValue(), null);
    YClass yclass = YClass.getYClass($t.link);
    
    if (yclass == null) {
        yclass = YClass.singleton();
        throw new FailedPredicateException(input, "variableDeclaration", $t.link.getValue() + " not exists!");
    }
    
    $def = YAttributeDefinition.yclass().create($id.text, yclass, null);
    $def.setSource($variableDeclaration.text);
  }
  ;

methodDeclaration returns [YMethodDefinition def]
  @init
  { HashMap<String, YClass> params = new HashMap<String, YClass>(); }
    
  : level=accessLevel type=path[false] id=IDENTIFIER
  OPENING_BRACKET
  (param=parameterDeclaration { params.put($param.name, YClass.getYClass($param.type)); })*
  CLOSING_BRACKET b=block
  
  {
    $def = YMethodDefinition.yclass().create( new YMethod($id.text, YClass.getYClass($type.link), params, null, $b.block) );
    $def.setSource($methodDeclaration.text);
  };


parameterDeclaration returns [SoftLink type, String name]
  : t=path[false] id=IDENTIFIER { $type=$t.link; $name=$id.text; };
  
  
block returns [Block block]
  @init { $block = new Block(); }
  : OPENING_PARENTHESIS (s=statement { $block.addStatement( $s.statement); }  )*
    CLOSING_PARENTHESIS
  ;


accessLevel returns [int level]
  : KEYWORD_PRIVATE {$level=3;}
  | KEYWORD_PROTECTED {$level=2;}
  | KEYWORD_PUBLIC {$level=1;};


  
switch_ returns [Switch sw]
  @init { Map<Expression, Statement> cases = new HashMap<Expression, Statement>(); }
  : SWITCH OPENING_BRACKET switcher=expression CLOSING_BRACKET
  
  OPENING_PARENTHESIS
  (c=switchCase   {cases.put( $c.expression, $c.statement);}  )*
  CLOSING_PARENTHESIS
  
  { $sw = new Switch($switcher.expression, cases, null);  };


switchCase returns [Expression expression, Statement statement]
  : CASE f=expression KETTOSPONT s=statement { $expression = $f.expression; $statement = $s.statement; };
 
statement returns [Statement statement]
  : b=block { $statement = $b.block; }
  | expression ';' {$statement = $expression.expression; }
  | sw=switch_ { $statement = $sw.sw; }
;
 
 
//expression returns [YObject yobject]
//  : f=field { $yobject = $f.field; }
//  | c=call { $yobject = YMethodDefinition.yclass().create($c.call); }
//  ;

  
//call returns [AbstractCall call]
//  : rc=regularCall { $call = $rc.call; }
//  | as=assignment { $call = $as.call; }
//  | add=addition { $call = $add.call; }
//  ;
  
//addition returns [StaticCall call]
//  : value1=expression ADDITION value2=expression
//  {
//    Map<String, YObject> parameters = new HashMap<String, YObject>();
//    parameters.put( Assign.KEY_TARGET, $target.link);
//    $call = new StaticCall($value1.expression, "+", parameters);
//  };



expression returns [Expression expression]
  @init { Variable var = null; }
  : (variable '=' { var=$variable.variable; } )? additiveExpression
  { $expression = createExpr(var, "=", $additiveExpression.expression); }
  ;
  
  
additiveExpression returns [Expression expression]
  @init { String op = null; }
  : m=multiplicativeExpression { $expression = $m.expression; }
    (  ('+' {op = "+";}|'-' {op = "-";}) 
    m=multiplicativeExpression { $expression = createExpr($expression, op, $m.expression); }  )*;

  
multiplicativeExpression returns [Expression expression]
  @init { String op = null; }
  : p=primary { $expression = $p.expression; }
    (   ('*' {op = "*";}|'/' {op = "/";}|'%' {op = "\%";})
    p=primary { $expression = createExpr($expression, op, $p.expression); } )*;  
  

primary returns [Expression expression]
    : literal { $expression = $literal.constantVar; }
    | p=path[false] { $expression = new DynamicVariable($p.link); }
    | c=call { $expression = $c.call; }
    ;
    

call returns [Call call]
  : path[true] OPENING_BRACKET parameterList CLOSING_BRACKET
    { $call = new Call( new DynamicVariable($path.link), $path.target, $parameterList.map); };
    
parameterList returns [Map<String, Expression> map]
  @init { $map = new HashMap<String, Expression>(); }
  : (param1=parameter   { $map.put(param1.name, param1.expression); }
    (COLON param2=parameter { $map.put(param2.name, param2.expression); })*
    )?
  ;

parameter returns [String name, Expression expression]
  : i=IDENTIFIER KETTOSPONT e=expression {$name = $i.text; $expression = $e.expression; };    
    
  
literal returns [Constant constantVar]
  : num=number { $constantVar = new Constant($num.intField); }
  | str=string { $constantVar = new Constant($str.strField); }
  ;
  

variable returns [Variable variable]
  : p=path[false] { $variable = new DynamicVariable($p.link); };

path[boolean separated] returns [SoftLink link, String target]
  //: id=IDENTIFIER {String path = $id.text;}
  //(DELIMITER id=IDENTIFIER {path += YObject.DELIMITER + $id.text;})*{$link = new SoftLink("Generated SoftLink", path);}
  
  @init {String path = null;}
//  ( id=IDENTIFIER DELIMITER {path += $id.text + YObject.DELIMITER;} )* id=IDENTIFIER
  
  : id=IDENTIFIER {$target = $id.text; }
    (DELIMITER id=IDENTIFIER
      {
        if (path == null)
          path = $target;
        else
          path += YObject.DELIMITER + $target;
          
       $target = $id.text; }
     )*
        
//      {
//      if (separated)
//        $target = $id.text;
//      else
//        path += $id.text;
//        
//      if (path == "")
//        path = "this";
//      
//      $link = new SoftLink(path);
//      }

    {
      if (separated)
        $link = new SoftLink(path);
      else
        $link = new SoftLink( (path != null ? path + YObject.DELIMITER + $target : $target));
        
    }
    
  ;
  
//identifier
//  : IDENTIFIER;
  
//identifier returns [String text]
//  //: IDENTIFIER;
//  : path { $text = $path.link.getValue(); };

number returns [YInteger intField] 
  : num=NUMBER { $intField = new YInteger( Integer.parseInt($num.text) ); };
  
//identifier returns [SoftLink link]
//  : id=IDENTIFIER { $link = new SoftLink($id.text); };

//identifier returns [String text]
//  : id=IDENTIFIER {$text = $id.text; };



string returns [YString strField]
  : str=STRING { String value = $str.text; $strField = new YString(value, value.substring(1, value.length()-1) ); };


assignmentOperator
 : '=';

/*------------------------------------------------------------------
// * LEXER RULES
 *------------------------------------------------------------------*/

KEYWORD_PRIVATE
  : 'private';
KEYWORD_PROTECTED
  : 'protected';
KEYWORD_PUBLIC
  : 'public';
KEYWORD_EXTENDS
  : 'extends';
  
SWITCH  : 'switch';
CASE  : 'case';


  
WHITESPACE 
  : ( '\t' | ' ' | '\r' | '\n'| '\u000C')+  { $channel = HIDDEN; } ;

NUMBER : (MINUS)? DIGIT+;
IDENTIFIER : IDENTIFIER_SYMBOL+;
STRING  : QUOTATION_MARK STRING_SYMBOL* QUOTATION_MARK;


  
fragment STRING_SYMBOL 
  : IDENTIFIER_SYMBOL | CONTROL_SYMBOL | WHITESPACE | ESCAPE;
  
fragment IDENTIFIER_SYMBOL
  : DIGIT | LETTER | NONCONTROL_SYMBOL;  
  
fragment ESCAPE
  : ESCAPE_SYMBOL (STRING_SYMBOL | QUOTATION_MARK);
  
//fragment STRING_SYMBOL
//  : DIGIT | LETTER | NONCONTROL_SYMBOL | CONTROL_SYMBOL;
  
//fragment CHARACTER 
//  : DIGIT | LETTER | NONCONTROL_SYMBOL | ESCAPE_SYMBOL | QUOTATION_MARK;

  fragment CONTROL_SYMBOL
  : ASSIGN | ADDITION | MINUS | MULTIPLICATION | DIVISION | MODULUS | OPENING_PARENTHESIS | CLOSING_PARENTHESIS | OPENING_BRACKET | CLOSING_BRACKET | COLON | KETTOSPONT | SEMICOLON | DELIMITER;
  
ASSIGN  : '=';
ADDITION: '+';
MINUS:  '-';
MULTIPLICATION: '*';
DIVISION: '/';
MODULUS : '%';


OPENING_PARENTHESIS
  : '{';
CLOSING_PARENTHESIS
  : '}';

OPENING_BRACKET
  : '(' ;
CLOSING_BRACKET 
  : ')' ; 
  
COLON : ',';
KETTOSPONT
  : ':';
SEMICOLON
  : ';';

DELIMITER 
  : '.';  
  
  

  fragment NONCONTROL_SYMBOL 
  : '_' | '#';
  
  fragment ESCAPE_SYMBOL 
  : '\\';
  
  fragment QUOTATION_MARK 
  : '"' ;
  
  fragment DIGIT 
  : '0'..'9' ;
  
  fragment LETTER
  : 'a'..'z' | 'A'..'Z'; //| 'á' | 'Á' | 'é' | 'É' | 'í' | 'Í' | 'ó' | 'Ó' | 'ö' | 'Ö' | 'ü' | 'Ü' | 'ú' | 'Ú';
  
 
  