電卓プログラム

構文解析ルーチンを利用して、小さな電卓プログラムをつくります。字句解析や式解析の基本手法が含まれています。

/*--------------------------------*/
/*   電卓プログラム minicalc.cpp    */
/*--------------------------------*/
#include 
#include 
#include 
using namespace std;

enum TknKind {
    Print, Lparen, Rparen, Plus, Minus, Multi, Divi,
    Assign, VarName, IntNum, EofTkn, Others
};

struct Token {
    TknKind kind;
    int intVal;
    Token() { kind = Others; intVal = 0; }
    Token(TknKind k, int d=0) { kind = k; intVal = d; }
};

void input();
void statement();
void expression();
void term();
void factor();
Token nextTkn();
int nextCh();
void operate(TknKind op);
void push(int n);
int pop();
void chkTkn(TknKind kd);

#define STK_SIZ 20
int stack[STK_SIZ+1];
int stkct;
Token token;
char buf[80], *bufp;
int ch;
int var[26];
int errF;

int main()
{
    while(1){
        input();
        token = nextTkn();
        if (token.kind == EofTkn) exit(1);
        statement();
        if (errF) cout << " --err--\n";
    }
    return 0;
}

void input()
{
    errF = 0; stkct = 0;
    cin.getline(buf, 80);
    bufp = buf;
    ch = nextCh();
}

void statement()
{
    int vNbr;
    
    switch (token.kind){
        case VarName:
            vNbr = token.intVal;
            token = nextTkn();
            chkTkn(Assign); if (errF) break;
            token = nextTkn();
            expression();
            var[vNbr] = pop();
            break;
        case Print:
            token = nextTkn();
            expression();
            chkTkn(EofTkn); if (errF) break;
            cout << " " << pop() << endl;
            return;
        default:
            errF = 1;
    }
    chkTkn(EofTkn);
}

void expression()
{
    TknKind op;
    
    term();
    while (token.kind == Plus || token.kind==Minus){
        op = token.kind;
        token = nextTkn(); term(); operate(op);
    }
}

void term()
{
    TknKind op;
    
    factor();
    while(token.kind==Multi || token.kind==Divi){
        op = token.kind;
        token = nextTkn(); factor(); operate(op);
    }
}

void factor()
{
    switch (token.kind){
        case VarName:
            push(var[token.intVal]);
            break;
        case IntNum:
            push(token.intVal);
            break;
        case Lparen:
            token = nextTkn();
            expression();
            chkTkn(Rparen);
            break;
        default:
            errF = 1;
    }
    token = nextTkn();
}

Token nextTkn()
{
    TknKind kd = Others;
    int num;
    
    while(isspace(ch))
        ch = nextCh();
    if (isdigit(ch)){
        for (num=0; isdigit(ch); ch = nextCh())
            num = num*10 + (ch-'0');
        return Token(IntNum, num);
    }
    else if (islower(ch)){
        num = ch - 'a';
        ch = nextCh();
        return Token(VarName, num);
    }
    else {
        switch (ch){
            case '(': kd = Lparen; break;
            case ')': kd = Rparen; break;
            case '+': kd = Plus; break;
            case '-': kd = Minus; break;
            case '*': kd = Multi; break;
            case '/': kd = Divi; break;
            case '=': kd = Assign; break;
            case '?': kd = Print; break;
            case '\0': kd = EofTkn; break;
        }
        ch = nextCh();
        return Token(kd);
    }
}

int nextCh()
{
    if (*bufp == '\0') return '\0'; else return *bufp++;
}

void operate(TknKind op)
{
    int d2 = pop(), d1 = pop();
    
    if (op==Divi && d2==0) { cout << " division by 0\n"; errF = 1; }
    if(errF) return;
    switch (op){
        case Plus: push(d1+d2); break;
        case Minus: push(d1-d2); break;
        case Multi: push(d1*d2); break;
        case Divi: push(d1/d2); break;
    }
}

void push(int n)
{
    if (errF)return;
    if (stkct+1 > STK_SIZ) { cout << "stack overflow\n"; exit(1); }
    stack[++stkct] = n;
}

int pop()
{
    if (errF) return 1;
    if (stkct < 1) { cout << "stack underflow\n"; exit(1); }
    return stack[stkct--];
}

void chkTkn(TknKind kd)
{
    if (token.kind != kd) errF = 1;
}