/*
 再帰的下降構文解析プログラム
 */
#include <stdio.h>
#include <stdlib.h>   /* exit()用 */
#include <ctype.h>    /* isdigit()用 */

/* 大域変数の宣言 */
int token;      /* 入力の先頭のトークンを表す */
int yylval;     /* tokenの属性 */

int column = 0; /* デバッグ用 */   

/* 終端記号に対応するマクロの定義 */
#define ID  256
#define EOL 257

/* 簡易字句解析ルーチン */
int yylex(void) {   
    int c;

    if (token==EOL) { /* 前のトークンが改行だったら */
        column=0;
    }
    
    do {
        c = getchar ();
        column++; 
    } while (c == ' ' || c == '\t'); /* 空白は読み飛ばす */

    if (isalpha(c)) { /* IDは一文字のアルファベット */
        yylval = c;
        return ID;
    }

    if (c == '\n') {
        return EOL;  /* 行末が $（入力の終わり）に対応する */
    }
    
    if (c == EOF) { /* ファイルの終 */
        exit(0); 
    }
    /* 上のどの条件にも合わなければ、文字をそのまま返す。*/
    return c;   /* '(', ')', '*', '[', ']'など */
}

char* tokenName(int t) {
    switch(t) {
    case 256: return "ID";
    case 257: return "EOL";
    default:  return "Unknown";
    }
}

/* token（終端記号）を消費して、次の tokenを読む */
void eat(int t) {       
  if (token == t) {
      token = yylex();
      return;
  } else {
      if (isprint(t)) {
          printf("eat: Character '%c' is expected at column %d ", t, column);
      } else {
          printf("eat: Token %s is expected here at column %d ", tokenName(t), column);
      }
      if (isprint(token)) {
          printf("instead of '%c'.\n", token);
      } else {
          printf("instead of %s.\n", tokenName(token));
      }      
      exit(1);
  }
}

/* エラーメッセージの出力 */
void errMessage(char* place) {
    if (isprint(token)) {
        printf("%s: Unexpected token: '%c' at column %d.\n", place, token, column);
    } else {
        printf("%s: Unexpected token: %s at column %d.\n", place, tokenName(token), column);
    }  
}

/* 関数プロトタイプ宣言 */
void E(void);
void E1(void);    /* 'は Cの関数名に使えないので代わりに 1を使う */
                  /* E'は左再帰を除去する時に導入した非終端記号  */
void X(void);
void X1(void);

/* 
   再帰的構文解析関数群
   文法の各非終端記号に対応する関数
 */

/***********************************************************************
 ここに E(), E1(), X(), X1()を定義してください 
 ***********************************************************************/

/* 各行の処理 */
void ProcessLine(void) {
    E();
    if (token==EOL) { /* 入力がブロックしないように改行は特別扱い */
        printf("Correct!\n");   /* eat(EOL)の前に出力しておく */
    }
    eat(EOL);
}

/* main関数 */
int main(void) {
    printf("Ctrl-cで終了します。\n");
    token = yylex(); /* 最初のトークンを読む */
    while (1 /* 無限ループ */) {
        ProcessLine();  /* 各行を処理する */
    }
}
