#include <vector>
#include <iostream>
#include <assert.h>

using namespace std;

#define MAX_ID_LENGTH 20

#define ID        1
#define DIGIT    2
#define EQUAL    3
#define PLUS    4
#define MUL        5
#define SUB        6

 

#define INPUT_CODE "var = 20 - 59"

char* KeyWord[] =
{
    "print",
    "status",
};


typedef struct _TOKEN_
{
    int         type;
    char        str[MAX_ID_LENGTH];
    int         value;
}Token;

typedef vector<Token> Tokens;
Tokens g_tokens;
int g_tokenIndex = 0;

int ParseStatement();
int ParseExpresstion();
int ParseTerm();
int ParseFactor();

void PushBackToken(int type, const char* str)
{
    Token token = { 0 };
    token.type = type;
    token.value = 0;
    strcpy(token.str, str);

    g_tokens.push_back(token);
}


bool IsEnd()
{
    return g_tokenIndex == g_tokens.size();
}

Token& GetNextToken()
{
    if ( g_tokenIndex <= g_tokens.size() - 1 )
    {
        if ( g_tokens[g_tokenIndex].type == ID )
        {
            for(int i = 0; i < g_tokens.size(); ++i)
            {
                if ( 0 == strcmp(g_tokens[g_tokenIndex].str, g_tokens[i].str) )
                {
                    g_tokenIndex++;
                    return g_tokens[i];
                }
            }
        }
        return g_tokens[g_tokenIndex++];
    }
    else
    {
        std::cout << "out of range" << std::endl;
        Token token = { 0 };
        return token;
    }
}

void UnGetNextToken()
{
    --g_tokenIndex;
}

void ShowAllTokens()
{
    for (int i = 0; i < g_tokens.size(); i++)
    {
        cout << g_tokens[i].str;
    }
}

 

 

int ParseStatement()
{
    Token& token = GetNextToken();

    if ( ID != token.type )
    {
        cout << "statement should begin with identifier" << endl;
    }

    Token tmpToken = GetNextToken();
    if ( EQUAL != tmpToken.type )
    {
        cout << "= lost" << endl;
    }

    token.value = ParseExpresstion();

    return token.value;
}

int ParseExpresstion()
{
    int sum = ParseTerm();
    while (true)
    {
        if ( IsEnd() )
        {
            return sum;
        }

        Token token = GetNextToken();
        if ( PLUS == token.type )
        {
            sum += ParseTerm();
        }
        else if ( SUB == token.type )
        {
            sum -= ParseTerm();
        }
        //todo: add "-"
        else
        {
            UnGetNextToken();
            break;
        }
    }

    return sum;
}


int ParseTerm()
{
    int mulResult = ParseFactor();
    while ( true )
    {
        if ( IsEnd() )
        {
            return mulResult;
        }
        Token token = GetNextToken();

        if ( MUL == token.type )
        {
            mulResult *= ParseFactor();
        }
        else
        {
            UnGetNextToken();
            break;
        }
        //todo add "/"
    }

    return mulResult;
}

int ParseFactor()
{
    Token token = GetNextToken();

    if ( ID == token.type )
    {
        return token.value;
        //cout << "statement should begin with identifier" << endl;
    }
    else if( DIGIT == token.type )
    {
        return atoi(token.str);
    }
    else
    {
        UnGetNextToken();
        //cout << "bug found" << endl;
    }

    return 1;
}

void TestPlus()
{
    g_tokenIndex = 0;
    g_tokens.clear();
    PushBackToken(ID,    "a");
    PushBackToken(EQUAL, "=");
    PushBackToken(DIGIT, "20");
    PushBackToken(PLUS, "+");
    PushBackToken(DIGIT, "30");
    PushBackToken(PLUS, "+");
    PushBackToken(DIGIT, "30");
    PushBackToken(SUB, "-");
    PushBackToken(DIGIT, "1");
    assert(79 == ParseStatement());
}

void TestPlusVar()
{
    PushBackToken(ID,    "b");
    PushBackToken(EQUAL, "=");
    PushBackToken(DIGIT, "20");
    PushBackToken(PLUS, "+");
    PushBackToken(DIGIT, "30");
    PushBackToken(PLUS, "+");
    PushBackToken(DIGIT, "30");
    PushBackToken(SUB, "-");
    PushBackToken(DIGIT, "1");
    PushBackToken(PLUS, "+");
    PushBackToken(ID, "a");

    assert(158 == ParseStatement());
}

void TestPlusVar1()
{
    PushBackToken(ID,    "c");
    PushBackToken(EQUAL, "=");
    PushBackToken(DIGIT, "20");
    PushBackToken(PLUS, "+");
    PushBackToken(ID, "a");
    ShowAllTokens();
    assert(99 == ParseStatement());
}

void TestPlusVar2()
{
    PushBackToken(ID,    "d");
    PushBackToken(EQUAL, "=");
    PushBackToken(ID, "a");
    PushBackToken(PLUS, "+");
    PushBackToken(ID, "b");
    ShowAllTokens();
    assert(158+79 == ParseStatement());
}

void TestMulVar2()
{
    PushBackToken(ID,    "e");
    PushBackToken(EQUAL, "=");
    PushBackToken(ID, "a");
    PushBackToken(MUL, "*");
    PushBackToken(ID, "b");
    ShowAllTokens();
    assert(158*79 == ParseStatement());
}

void TestSub()
{
    g_tokenIndex = 0;
    g_tokens.clear();
    PushBackToken(ID,    "var");
    PushBackToken(EQUAL, "=");
    PushBackToken(DIGIT, "20");
    PushBackToken(SUB, "-");
    PushBackToken(DIGIT, "30");
    assert(-10 == ParseStatement());
}


void TestMul()
{
    g_tokenIndex = 0;
    g_tokens.clear();
    PushBackToken(ID,    "var");
    PushBackToken(EQUAL, "=");
    PushBackToken(DIGIT, "20");
    PushBackToken(PLUS, "+");
    PushBackToken(DIGIT, "30");
    PushBackToken(MUL, "*");
    PushBackToken(DIGIT, "30");
    assert(920 == ParseStatement());
}

int main()
{
    TestPlus();    //测试加法
    TestPlusVar();
    TestPlusVar1();
    TestPlusVar2();
    TestMulVar2();
    //TestSub();    //测试减法
    //TestMul();    //测试乘法

    return 0;
}