/*

再帰的下向き構文解析プログラム
（以下の文法に対する構文解析プログラム）
   Expr    -> CON
            | FID '(' List ')'
   List    -> Expr Rest
   Rest    -> ',' Expr
            | ε

-- 以下は終端記号: 字句解析部で処理
   CON     -> '0' | '1' | ... | '9'   --  一桁の数のみ
   FID     -> '+' | '-' | '*' | '!'


-- 予測型構文解析表
                CON             FID             (               )               ,               $
   Expr         CON             FID '(' List ')'
   List         Expr Rest       Expr Rest
   Rest                                                         ε              ',' Expr 

 */

#include <stdio.h>
#include <stdlib.h>   /* exit()用 */
#include <ctype.h>    /* isdigit()用 */

/* 大域変数の宣言 */
int token;      /* 入力の先頭のトークンを表す */
int yylval;     /* tokenの属性 */

int column = 0; /* デバッグ用 */ 

/* 終端記号に対応するマクロの定義 */
#define CON 256
#define FID 257

/* 簡易字句解析ルーチン */
int yylex(void) {   
    int c;

    if (token == '\n') { /* 前のトークンが改行だったら */
        column=0;
    }

    do {
        c = getchar ();
        column++;
    } while (c == ' ' || c == '\t');

    if (isdigit (c)) {
        yylval = c-'0'; /* 数字から数へ変換 */
        return CON;
    }

    if (c == '+' || c== '-' || c == '*' ||c =='!') {
        yylval = c;
        return FID;
    }

    if (c == EOF) { /* ファイルの終 */
        exit(0); 
    }
    /* 上のどの条件にも合わなければ、文字をそのまま返す。*/
    return c;   /* '(', ')', ',', '\n' など */
}

char* tokenName(int t) {    /* デバッグ用 */
    switch (t) {
    case '\n': return "(End of Line)";
    case 256:  return "CON";
    case 257:  return "FID";
    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 at column %d ",
                   tokenName(t), column);
        }
        if (isprint(token)) {
            printf("instead of '%c'.\n", token);
        } else {
            printf("instead of %s (code %d).\n", tokenName(token), 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 (code %d) at column %d.\n", 
               place, tokenName(token), token, column);
    }  
}
/* 関数プロトタイプ宣言 */
void Expr(void);
void List(void);
void Rest(void);

/* 
   再帰的構文解析関数群
   文法の各非終端記号に対応する関数
 */
void Expr(void) {
    switch (token) {
    case CON:
        eat(CON); break;
    case FID:    
        eat(FID); eat('('); List(); eat(')'); break;
    default:
        errMessage("Expr");
        exit(1);
        break;
    }
}

void List(void) {
    if (token == CON || token == FID ) {
        Expr(); Rest();
    } else {
        errMessage("List");
        exit(1);
    }
}

void Rest(void) {
    switch (token) {
    case ',':
        eat(','); Expr(); break;
    case ')':
        /* do nothing */ break;
    default:
        errMessage("Rest");
        exit(1);
        break;
    }
}

/* 各行の処理 */
void processLine(void) {
    Expr();
    if (token == '\n') { /* 入力がブロックしないように改行は特別扱い */
        printf("Correct!\n");   /* eat('\n')の前に出力しておく */
    }
    eat('\n');
}

/* main関数 */
int main(void) {
    printf("Ctrl-cで終了します。\n");
    token = yylex(); /* 最初のトークンを読む */
    while (1 /* 無限ループ */) {
        processLine();  /* 各行を処理する */
    }
}
