parser grammar VSLParser;

options {
  language = Java;
  tokenVocab = VSLLexer;
  k = 1;
}

@header {
  package TP2;
  import TP2.asd.Program.*;
  import TP2.asd.*;
  import TP2.asd.Interface.*;
}

// On syntax error, raise exception rather than silently recovery
@rulecatch {
  catch (RecognitionException ex) {
    reportError(ex) ;
    throw(ex) ; 
    }
}

program returns [ProgramImp p] :
  func=functions
  EOF
  {$p = new ProgramImp($func.out);}
;

liste_param returns [ArrayList<VarImp> out]
  @init{
    $out = new ArrayList<VarImp>();
  }: 
      ii1=ident {$out.add(new VarImp($ii1.out));}
      (VIRGULE ii2=ident  {$out.add(new VarImp($ii2.out));})*
    
  ; 

functions returns [ArrayList<Function> out]
  @init{
    $out = new ArrayList<Function>();
  }:
    (
    (function {
      $out.add($function.out);
    })
    |(proto {
      $out.add($proto.out);
    })
    )+
;

proto returns [Function out]:
  PROTOTYPE t=type i=ident ParO 
  (
    v=liste_param ParF
    {
      $out=new PrototypeImp($t.return_type,$i.out, $v.out);
    }
    | ParF
    {      
      $out=new PrototypeImp($t.return_type,$i.out, new ArrayList<>());
    }
  )
  ; 

function returns [Function out]:
  FUNCTION t=type i=ident ParO 
    (
      v=liste_param ParF instr= instruction
        {
          $out=new FunctionImp($t.return_type, $i.out, $v.out, $instr.out);
        }
      | ParF instr= instruction
        {
          $out=new FunctionImp($t.return_type, $i.out, new ArrayList<>(), $instr.out);
        }
    )
;

list_decls returns [ArrayList<Declaration> out]
  @init{
    $out = new ArrayList<>(); 
  }:
    (declaration {
      $out.add($declaration.out);
    })+
;

declaration returns [Declaration out]
  @init{
    ArrayList<VarDeclImp> vars = new ArrayList<>();
  }:
  t=type v1=var_decl 
    { 
      vars.add($v1.out); 
    }
    (VIRGULE v2=var_decl 
      { 
        vars.add($v2.out); 
      }
    )*
    {
      $out= new DeclarationImp($t.return_type, vars);
    }
;

var_decl returns [VarDeclImp out]:
  id=ident 
  (CroO e=expression CroF
    {$out= new VarDeclImp($id.out,$e.out);}
  |
    {$out= new VarDeclImp($id.out, null);}
  )
;

list_instr returns [ArrayList<Instruction> out]
  @init{
    $out = new ArrayList<>(); 
  }:
    (instruction {
      $out.add($instruction.out);
    })+
;

instruction returns [Instruction out]:
  //RETURN
  RETURN e=expression 
    {
    $out =new Return_instrImp($e.out);}
  | //BLOC
  BacO 
    (dec=list_decls instr2=list_instr
      {
        $out=new BlocDecImp($dec.out,$instr2.out);
      }
    | instr3=list_instr
      { $out= new BlocImp($instr3.out);
      } 
    ) BacF
  | //ASSIGN
  i=ident 
   (ASSIGN e=expression 
    {
      $out = new AssignImp($i.out, $e.out);
    }
    |ParO (para=list_expression)? ParF
    {
      $out = new VoidFunctionImp($i.out,($para.out != null) ? $para.out : new ArrayList<>());
    }
   )
  | // PRINT
  PRINT 
  {ArrayList<Object> printer= new ArrayList<Object>();}
    (t1=TEXT
      {
        String text= $t1.getText();
        printer.add(text.substring(1,text.length()-1));
      }
    |e1=expression
      {
        printer.add($e1.out);
      })
    (VIRGULE (t2=TEXT
      {
        String text2= $t2.getText();
        printer.add(text2.substring(1,text2.length()-1));
      }
    | e2=expression
        {
        printer.add($e2.out);
        }
    ))*
    {$out = new PrintImp(printer);}
  | //READ
  READ i1=ident 
    {
      ArrayList<VarImp> read= new ArrayList<VarImp>();
      read.add(new VarImp($i1.out));
    }(VIRGULE i2=ident
    {
      read.add(new VarImp($i2.out));
    })*
    {$out = new ReadImp(read);}
  | //IF THEN ELSE FIN
  IF ex1=expression THEN ins1=instruction 
   (FIN 
      {$out= new IfThenImp($ex1.out, $ins1.out);}
    | ELSE ins2=instruction FIN
      {$out= new IfThenElseImp($ex1.out, $ins1.out,$ins2.out); }
   )
  | //WHILE
  WHILE exp1=expression DO ins3=instruction DONE 
    {$out = new WhileImp($exp1.out,$ins3.out);}
  ;

list_expression returns [ArrayList<Expression> out ]
  @init
  {$out = new ArrayList<Expression>(); }:
    e1= expression
    {out.add($e1.out);} 
    ((VIRGULE e2=expression)
      {out.add($e2.out);}
    )*
;
//Priorité lit(val, const ou paranthese) -> td_exp (*/%) -> exp (+-)
expression returns [Expression out]:
//Fix LL1 and compare add and sub first
  left=td_expression {
    $out = $left.out;
  }
//maybe it will be binop(s) with add/sub here
//if it is mul or div, they are all gonna be left=td_expression[table] to handle
// have to make $out,$right.out to make a loop
  (
    (PLUS right=td_expression{$out = new BinopExpressionImp(Op.PLUS,$out,$right.out);})
    | //MINUS
    (MINUS right=td_expression{$out = new BinopExpressionImp(Op.MINUS,$out,$right.out);})
  )*
;

td_expression returns [Expression out]:
    left=lit {
      $out = $left.out;
    }
  (
    (DIV right=lit{$out = new BinopExpressionImp(Op.DIV,$out,$right.out);})
    | //MOD
    (MOD right=lit{$out = new BinopExpressionImp(Op.MOD,$out,$right.out);})
    | //TIMES
    (TIMES right=lit{$out = new BinopExpressionImp(Op.TIMES,$out,$right.out);})
  )*
;

lit  returns [Expression out]:
    MINUS n=lit {
        $out = new BinopExpressionImp(Op.MINUS, new ConstImp(0), $n.out);
      }
    |
    NUMBER {
      $out = new ConstImp($NUMBER.int);
    }
    | ParO e=expression ParF{
      $out=$e.out;
    }
    | t=ident
      ( 
        ParO (para=list_expression)? ParF {$out = new CallImp($t.out,($para.out !=null) ?$para.out : new ArrayList<>());}
        | {$out=new VarImp($t.out);}
      )
  ;

ident returns [String out]:
    IDENT { $out = $IDENT.getText(); }
;

type returns [Type return_type]:
  TYPE_INT { $return_type = new Type_intImp(); }
  |TYPE_VOID { $return_type = new Type_voidImp(); }
;