package yacs.parser;

import java.util.HashMap;

import yacs.domain.*;

import java_cup.runtime.*;

action code {:
  /* this is where the action code goes */

  /** der erzeugte Ausdruck */
  public Expression expression;

  /** HashtMap zum "Wiederfinden" der generierten Variablen */
  public HashMap actionVariablesMap = new HashMap();

  /** temporäre Variable zum Zwischenspeichern */
  public Variable tmpVariable;

:};

parser code {:
 /* this is where the parser code goes */

  /** HashtMap zum Zwischenspeichern der bereits vorhandenen Variablen */
  public HashMap parserVariablesMap = new HashMap();

 /**
  * Hinzufügen bereits vorhandener Variablen. Anstatt neue Variablen
  * zu erzeugen, werden so die Referenzen bestehender Objekte
  * verwendet.
  * @param variablesMap HashMap
  */
  public void addVariablesMap(HashMap variablesMap) {
    this.parserVariablesMap.putAll(variablesMap);
  }

 /**
  * Gibt den gescannten und geparsten Ausdruck als Expression zurück.
  * @return Expression
  */
  public Expression expression() {
    return action_obj.expression;
  }

:};

init with {:

  // Die in "parser" bereits befindlichen Variablenreferenzen in das
  // "action_obj" übertragen. Dies muss hier separat geschehen, da
  // erst während der "init"-Phase das "action_obj" instantiiert ist.
  action_obj.actionVariablesMap.putAll(this.parserVariablesMap);

:};

/* Terminals (tokens returned by the scanner). */
terminal          UMINUS, SEMI, LPAREN, RPAREN, COMMA, LBRACKET, RBRACKET;
terminal          PLUS, MINUS, TIMES, DIVIDE;
terminal          EQUAL, NOT_EQUAL, GREATER, LOWER, GREATER_EQUAL, LOWER_EQUAL;
terminal Integer  NUMBER;
terminal Double   FLOAT;
terminal String   VARIABLE;

/* Non Terminals */
non terminal             expr_all;
non terminal Expression  expr_list, expr, expr_part;

/* Precedences */
precedence left EQUAL, NOT_EQUAL, GREATER, LOWER, GREATER_EQUAL, LOWER_EQUAL;
precedence left PLUS, MINUS;
precedence left TIMES, DIVIDE;
precedence left UMINUS;

/* The grammar */

expr_all   ::= expr_list:e
               {:
                System.out.println(" => "+e.toString());
                this.expression = e;
               :}
             ;

expr_list  ::= expr:e
               {: RESULT = e; :}
             | expr_list:l expr:r
               {: RESULT = new BinaryOperator(sym.SEMI, ";", l, r); :}
             ;

expr       ::= expr_part:l EQUAL expr_part:r SEMI
               {: RESULT = new BinaryOperator(sym.EQUAL, "=", l, r); :}
             | expr_part:l NOT_EQUAL expr_part:r SEMI
               {: RESULT = new BinaryOperator(sym.NOT_EQUAL, "!=", l, r); :}
             | expr_part:l GREATER expr_part:r SEMI
               {: RESULT = new BinaryOperator(sym.GREATER, ">", l, r); :}
             | expr_part:l LOWER expr_part:r SEMI
               {: RESULT = new BinaryOperator(sym.LOWER, "<", l, r); :}
             | expr_part:l GREATER_EQUAL expr_part:r SEMI
               {: RESULT = new BinaryOperator(sym.GREATER_EQUAL, ">=", l, r); :}
             | expr_part:l LOWER_EQUAL expr_part:r SEMI
               {: RESULT = new BinaryOperator(sym.LOWER_EQUAL, "<=", l, r); :}
             ;

expr_part  ::= NUMBER:n
               {: RESULT = new Constant(n); :}
             | LBRACKET FLOAT:lo COMMA FLOAT:hi RBRACKET
               {: RESULT = new Constant(lo, hi); :}
             | LBRACKET NUMBER:lo COMMA NUMBER:hi RBRACKET
               {: RESULT = new Constant(lo.doubleValue(), hi.doubleValue()); :}
             | LBRACKET FLOAT:lo COMMA NUMBER:hi RBRACKET
               {: RESULT = new Constant(lo.doubleValue(), hi.doubleValue()); :}
             | LBRACKET NUMBER:lo COMMA FLOAT:hi RBRACKET
               {: RESULT = new Constant(lo.doubleValue(), hi.doubleValue()); :}
             | VARIABLE:v
               {: tmpVariable = (Variable)actionVariablesMap.get(v);
                  if (tmpVariable == null) {
                    // Variable mit leerer Domäne instantiieren:
                    tmpVariable = new Variable(v, new Domain());
                    actionVariablesMap.put(v, tmpVariable);
                  }
                  RESULT = tmpVariable;
               :}
             | expr_part:l PLUS expr_part:r
               {: RESULT = new BinaryOperator(sym.PLUS, "+", l, r); :}
             | expr_part:l MINUS expr_part:r
               {: RESULT = new BinaryOperator(sym.MINUS, "-", l, r); :}
             | expr_part:l TIMES expr_part:r
               {: RESULT = new BinaryOperator(sym.TIMES, "*", l, r); :}
             | expr_part:l DIVIDE expr_part:r
               {: RESULT = new BinaryOperator(sym.DIVIDE, "/", l, r); :}
             | MINUS expr_part:e
               {: RESULT = new UnaryOperator(sym.UMINUS, "-", e); :}
               %prec UMINUS
             | LPAREN expr_part:e RPAREN
               {: RESULT = e; :}
             ;
