options { 
  IGNORE_CASE = true; // ignore upper/lower case
  STATIC = true; // generate static methods
  UNICODE_INPUT = true; // support Unicode in input
}

PARSER_BEGIN(FridgeParser)

package javakurs.uebung07;

/**
 * {@link FridgeParser} is the natural language interface to the FCM (fridge
 * content management) system in class {@link Fridge}.
 * 
 * @author Ulrich Schaefer, DFKI
 * @version $Id: FridgeParser.jj,v 1.2 2010/12/06 18:18:16 steffen Exp $
 */
public class FridgeParser {

  /**
   * Contains the FCM object.
   */
  private static FridgeEx2 fridge = null;
  
  /**
   * Contains flag to indicate end of program.
   */
  private static boolean quit = false;


  /**
   * Start method for running the natural language I/O to and from the
   * fridge via stdin/stdout. The for loop is executed forever; exit through
   * the 'Ende' command calling fridge.finish() -> System.exit()
   *
   * @param args not used
   */
  public static void main(String[] args) {

    FridgeParser parser = new FridgeParser(System.in);
    fridge = new FridgeEx2();
    while (true) {
      // loop forever - until break is executed  
      try {
        // start symbol/rule
        Input();
        if (quit) {
          break;
        }
      } catch (ParseException pe) {
        String tstring = pe.currentToken.image;
        // in the ParserException context, Token is always set,
        // but its image may be null
        if (tstring == null) {
          tstring = "";
        }
        else {
          tstring = tstring + " ";
        }
        String parserMessage =
        "Ich habe Deine Eingabe nur bis zu '" + tstring + "' verstanden.";
        fridge.message(parserMessage);  
        // needed by JavaCC-generated static code:
        ReInit(System.in); 
      }
    }
    // shutdown fridge
    fridge.finish(); 
  }
}

PARSER_END(FridgeParser)


// Lexer definition
SKIP :
{
  " "
| "\r"
| "\t"
}

TOKEN :
{ // fixed tokens (reserved words)
  < AUF : "auf" >
| < BERECHNE :
    "berechne"
  | "berechnen" >
| < BESTAND :
    "bestand"
  | "inhalt" >
| < DOT : "." >
| < EINKAUFSLISTE : "einkaufsliste" >
| < ENDE :
    "schluss"
  | "aus"
  | "ende"
  | "beenden"
  | "beende"
  | "tsch\u00fcss" >
| < ES : "es" >
| < IST : "ist" >
| < LF : "\n" >
| < MINUS : "-" >
| < PLUS : "+" >
| < QM : "?" >
| < SPAET : "sp\u00e4t" >
| < WIE : "wie" >
| < WIEDERSEHEN : "wiedersehen" >
| < WIEVIEL : "wieviel" >
// new entries for Uebung 4
| < BESTANDSLISTE : "bestandsliste" >
| < DA : "da" >
| < DIR : "dir" >
| < EINKAUFEN :
    "kaufen"
  | "einkaufen" >
| < ENTNEHME : "entnehme" >
| < GEBE : "gebe" >
| < HABEN : "haben" >
| < HIER : "hier" >
| < ICH : "ich" >
| < MUSS : "muss" >
| < NOCH : "noch" >
| < SIND : "sind" >
| < SOLL : "soll" >
| < UHR : "uhr" >
| < WAS : "was" >
| < WIR : "wir" >
| < ZEIGE : "zeige" >

// Extensions for Uebung 5 Start
| < ALS : "als" >
| < DAS : "das" >
| < IN : "in" >
| < MEHR : "mehr" >
| < HEUTE : "heute" >
| < WENIGER : "weniger" >
| < WETTER : "wetter" >
| < WIRD : "wird" >
| < WOVON : "wovon" >
// preterminal symbols
| < PRODUKT_OR_CITY : (<LETTER>)+ >
// Extensions for Uebung 5 End

| < NUM :
    (< DIGIT >)+
    (
      ( "." | "," ) (< DIGIT >)+
    )? >
// open class for letters in product names
|  < #LETTER : ["a"-"z", "A"-"Z", "\u00c0"-"\u00ff"] >
|  < #DIGIT : ["0"-"9"] >
}


void Input() :
/* parses [Command_or_Question() (?|.)] <LF> */
{ // variable initialization
}
{ // RHS
  (
    (
      Command_or_Question()
      (
        < DOT >
      | < QM >
      )?
      < LF >
    )+
  | 
    ( // special handling of exit commands:
      < ENDE >
    | < AUF > < WIEDERSEHEN >
    | < EOF >
    )
    {
      quit = true;
    }
  )
  { // post action 
    if (quit) {
      return;
    }
  }
}


void Command_or_Question() :
/* parses
   Schluss|aus|Ende|beenden|beende|Tsch\u00fcss|Auf Wiedersehen 
   Wie sp\u00e4t ist es
   Berechne Expr()
   (Haben wir|(Ist|Sind)) [noch] [<Num>] <Produkt> da
   Hier (ist|sind) [<Num>] <Produkt>
   Bestandliste
   Bestand <Produkt>
   Ich (gebe dir|entnehme) [<Num>] <Produkt>
   Wieviel... -> Wieviel_Regel()
   Was... -> Was_Regel() 
*/
{ // variable initialization
  Token prod = null;
  Token numb = null;
  int sign = 1;
}
{ // big RHS
  (
    (
      < BERECHNE > Expr()
    )
    { // action see Expr()
    }
  |
    (
      (
        < HABEN > < WIR >
      |
        (
          < IST >
        | < SIND >
        )
      )
      [ < NOCH > ] [ numb = < NUM > ] prod = < PRODUKT_OR_CITY > [ < DA > ]
    )
    {
      fridge.checkStock(numb, prod);
    }
  |
    (
      < HIER >
      (
        < IST >
      | < SIND >
      )
      [ numb = < NUM > ] prod = < PRODUKT_OR_CITY >
    )
    {
      fridge.modifyStock(numb, prod, sign);
    }
  |
    (
      < ICH >
      (
        < GEBE > < DIR >
      | < ENTNEHME >
        {
          sign = - 1;
        }
      )
      [ numb = < NUM > ] prod = < PRODUKT_OR_CITY >
    )
    {
      fridge.modifyStock(numb, prod, sign);
    }
  |
    (
      [ < ZEIGE > ]
      (
        < BESTAND > [ prod = < PRODUKT_OR_CITY > ]
      | < BESTANDSLISTE >
      )
    )
    {
      fridge.listStock(prod);
    }
  | (< EINKAUFSLISTE >)
    {
      fridge.buyList();
    }
  |
    (
      < WIEVIEL > Wieviel_Regel()
    )
  |
    (
      < WAS > Was_Regel()
    )
  |
    // Uebung 5 Start
    (
      < WIE > Wie_Regel()
    )
  | 
    (
      < WOVON >
      (
        < IST >
      | < SIND >
      )
      [ < NOCH > ]
      [
        (
          < WENIGER >
          {
            sign = - 1;
          }
        | < MEHR >
          {
            sign = 0;
          }
        )
        < ALS >
      ]
      numb = < NUM > < DA >
    )
    {
    fridge.filterStock(sign, numb.image);
    }
    // Uebung 5 End
  )
  // no post action
}


void Was_Regel() :
/* parses
   Was...
   - (muss|soll) ich einkaufen
   - ist Expr()
*/
{ // variable initialization
}
{ // RHS
  (
    (
      (
        < MUSS >
      | < SOLL >
      )
      < ICH > < EINKAUFEN >
    )
    {
      fridge.buyList();
    }
  |
    (
      < IST > Expr()
    )
  )
  // no post action
}


void Wieviel_Regel() :
/* parses
   Wieviel...
   - Uhr ist es
   - <Produkt> (ist|sind) [noch] da
   - ist Expr()
*/
{ // variable initialization
  Token prod;
}
{ // RHS
  (
    (< UHR > < IST > < ES >)
    {
      fridge.printTime();
    }
  |
    (
      prod = < PRODUKT_OR_CITY >
      (
        < IST >
      | < SIND >
      )
      [ < NOCH > ] < DA >
    )
    {
      fridge.askStock(prod.image);
    }
  |
    (
      < IST > Expr()
    )
  )
  // no post action
}

// Uebung 5 Start
void Wie_Regel() :
/* parses
  Wie...
  spät ist es
  ist|wird das Wetter
*/
{ // variable initialization
  Token city = null;
}
{ // RHS
  (
    (< SPAET > < IST > < ES >)
    {
      fridge.printTime();
    }
    |  
    (
      (
        < IST >
      | < WIRD >
      )
      < DAS > < WETTER > [ < HEUTE > ] [ < IN > city = < PRODUKT_OR_CITY > ]
    )
    {
      fridge.printWeather(city);
    }
  )
} 
// Uebung 5 End

void Expr() :
/* chain calculation rule; parses
   <Num> ( (+|-) <Num>)* */
{ // varible initialization
  Token tnumber;
  double result;
  double dnum;
  StringBuilder verbatim = new StringBuilder();
}
{ // RHS
  tnumber = < NUM >
  {
    result = fridge.parseNumber(tnumber.image);
    verbatim.append(fridge.formatNumber(result));
  }
  (
    (< PLUS > tnumber = < NUM >)
    {
      dnum = fridge.parseNumber(tnumber.image);
      result += dnum;
      verbatim.append(" plus " + fridge.formatNumber(dnum));
    }
  | (< MINUS > tnumber = < NUM >)
    {
      dnum = fridge.parseNumber(tnumber.image);
      result -= dnum;
      verbatim.append(" minus " + fridge.formatNumber(dnum));
    }
  )*
  { // post action
    fridge.message(verbatim.toString() + " ist " + fridge.formatNumber(result));
  }
}