// Taschenrechner mit rekursivem Abstieg, Luxusversion
// Programmgerüst als Angabe
//
// Aufruf: rechner
// (der Input wird vom Terminal gelesen)
// 
// Klaus Kusche, 2010


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>

// Setze das globale c auf das nächste "echte" Zeichen des Inputs
void nextc(void);
// Gib einen Syntaxfehler an der aktuellen Position aus und beende das Programm
void error(void);
// Lies einen Variablennamen (1 Kleinbuchstabe), liefere dessen Index
int getvar(void);
// Lies eine Gleitkomma-Zahl, ohne Vorzeichen
double value(void);
// Lies einen Operand: Zahl, Variable oder Klammerausdruck,
// |...| für Betrag und [...] für ganzzahliger Anteil,
// Sqrt(...) als Beispiel für eine Funktion,
// oder ! für letztes Ergebnis, E für Euler'sche Zahl, P für Pi
// Alles ev. mit - davor
double operand(void);
// Lies einen Faktor eines Produktes: Ein Operand oder eine Potenz
double factor(void);
// Lies einen Term einer Summe:
// Ein Faktor oder Produkt bzw. Quotient von Faktoren
double term(void);
// Lies einen Ausdruck: Ein Summand oder Summe bzw. Differenz von Summanden
double expr(void);
// Lies, berechne und drucke eine Zeile: Leer, Ausdruck oder Zuweisung
double line(void);


// Nächstes zu bearbeitendes Zeichen (int wegen EOF)
// Das ist unser "1 Zeichen LookAhead"
// Muss am Anfang '\n' sein, um das Lesen der ersten Zeile auszulösen
int c = '\n';
// Zeilen- und Spaltennummer (für Fehlermeldungen)
int lineNr = 0, colNr = 0;

// Speicher für die Variablen a-z
double var[26];

// Speicher für das Ergebnis der letzten Zeile
double lastVal;

// Setze das globale c auf das nächste "echte" Zeichen des Inputs:
// Überspringe Zwischenräume
// Am Ende des Files ist c die Konstante "EOF"
void nextc(void)
{
  if (c == '\n') {
    ++lineNr;
    colNr = 0;
  }
  do {
    c = getchar();
    ++colNr;
  }
  while ((c == ' ') || (c == '\t'));
}

// Gib einen Syntaxfehler an der aktuellen Position aus und beende das Programm
void error(void)
{
  if (c == EOF) {
    printf("Line %d: Parse error at end of input\n", lineNr);
  } else if (c == '\n') {
    printf("Line %d, col %d: Parse error at end of line\n", lineNr, colNr);
  } else {
    printf("Line %d, col %d: Parse error before %c\n", lineNr, colNr, c);
  }
  exit(EXIT_FAILURE);
}

// Lies einen Variablennamen (1 Kleinbuchstabe), liefere dessen Index
int getvar(void)
{
  int ind;

  if ((c < 'a') || (c > 'z')) {
    error();
  }
  ind = c - 'a';
  nextc();
  return ind;
}

// Lies eine Gleitkomma-Zahl, ohne Vorzeichen
// <value> ::= <digit> { <digit> } [ . { <digit> } ]
//             [ ( e | E ) [ + | - ] <digit> { < digit> } ]
double value(void)
{
  double val = 0;
  double frac = 0.1;
  int expon = 0;
  int esgn = 1;

  if (!isdigit(c)) {
    error();
  }
  do {
    val = val * 10 + (c - '0');
    nextc();
  } while (isdigit(c));
  if (c == '.') {
    nextc();
    while (isdigit(c)) {
      val += frac * (c - '0');
      frac /= 10;
      nextc();
    }
  }
  if ((c == 'e') || (c == 'E')) {
    nextc();
    if (c == '+') {
      nextc();
    } else if (c == '-') {
      esgn = -1;
      nextc();
    }
    if (!isdigit(c)) {
      error();
    }
    do {
      expon = expon * 10 + (c - '0');
      nextc();
    } while (isdigit(c));
    val *= pow(10.0, ((double) (esgn * expon)));
  }
  return val;
}

// Lies einen Operand: Zahl, Variable oder Klammerausdruck,
// |...| für Betrag und [...] für ganzzahliger Anteil,
// Sqrt(...) als Beispiel für eine Funktion,
// oder ! für letztes Ergebnis, E für Euler'sche Zahl, P für Pi
// Alles ev. mit - davor
// <operand> ::= [ - ] ( ! | E | P | Sqrt "(" <expr> ")" |
//                       "(" <expr> ")" | "|" <expr> "|" | "[" <expr> "]" |
//                       <value> | <var> )
double operand(void)
{
...
}

// Lies einen Faktor eines Produktes: Ein Operand oder eine Potenz
// Achtung: ^ bindet von rechts nach links, darum Rekursion, nicht Schleife!
// <factor> ::= <operand> [ ^ <factor> ]
double factor(void)
{
...
}

// Lies einen Term einer Summe:
// Ein Faktor oder Produkt bzw. Quotient von Faktoren
// <term> ::= <factor> { ( * | / ) <factor> }
double term(void)
{
...
}

// Lies einen Ausdruck: Ein Summand oder Summe bzw. Differenz von Summanden
// <expr> ::= <term> { ( - | + ) <term> }
double expr(void)
{
...
}

// Lies, berechne und drucke eine Zeile: Leer, Ausdruck oder Zuweisung
// <line> ::= [ <expr> [ = <var> ] ]
double line(void)
{
...
}

// Hauptprogramm: Berechne und drucke einen Ausdruck pro Zeile
// ev. mit Zuweisung
// <input> ::= { <line> \n } EOF
int main(void)
{
...
}
