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

#define MAX_SOURCE 5000
#define MAX_TOKENS 500
#define MAX_TEXT 50
#define MAX_SYMBOLS 100
#define MAX_ERRORS 100
#define MAX_TAC 500

#define TOKEN_INT 1
#define TOKEN_IDENTIFIER 2
#define TOKEN_NUMBER 3
#define TOKEN_IF 4
#define TOKEN_ELSE 5
#define TOKEN_WHILE 6
#define TOKEN_OPERATOR 7
#define TOKEN_SYMBOL 8
#define TOKEN_UNKNOWN 9

char source[MAX_SOURCE];
int sourceLength = 0;

char tokenText[MAX_TOKENS][MAX_TEXT];
int tokenType[MAX_TOKENS];
int tokenLine[MAX_TOKENS];
int tokenCount = 0;

char symbolName[MAX_SYMBOLS][MAX_TEXT];
char symbolType[MAX_SYMBOLS][20];
int symbolValue[MAX_SYMBOLS];
int symbolCount = 0;

char semanticErrors[MAX_ERRORS][100];
int semanticErrorCount = 0;

char tac[MAX_TAC][100];
int tacCount = 0;

int currentToken = 0;
int syntaxError = 0;
char syntaxErrorMessage[120];

int tempCount = 1;
int labelCount = 1;
int assignmentAffectsSymbol = 1;

char lastIdentifier[MAX_TEXT];
char lastExpressionText[100];
char lastExprLeft[MAX_TEXT];
char lastExprOp[MAX_TEXT];
char lastExprRight[MAX_TEXT];
int lastExprHasOperator = 0;
int lastExprValue = 0;
int lastExprValueKnown = 0;
int lastExprSemanticOk = 1;

char conditionLeft[MAX_TEXT];
char conditionOp[MAX_TEXT];
char conditionRight[MAX_TEXT];

char tempLine[150];

void readSource();
void tokenize();
void printSource();
void printTokens();
void parseProgram();
void parseDeclaration();
void parseAssignment();
void parseIfStatement();
void parseWhileStatement();
void parseCondition();
void parseExpression();
void readOperand(char outputText[]);
int findSymbolIndex(char name[]);
void addSymbol(char name[]);
void updateSymbolValue(char name[], int value);
void addSemanticError(char message[]);
void addTAC(char line[]);
void printSyntaxResult();
void printSymbolTable();
void printSemanticErrors();
void printTAC();
void addToken(char text[], int type, int line);
int isOperatorToken(char text[]);
int isSymbolToken(char text[]);
int tokenIs(char text[]);
int tokenKindIs(int type);
void setSyntaxError(char message[]);
void expectText(char text[]);
void copyCurrentToken(char outputText[]);
int operandValue(char text[], int ok);
int newLabel();

int main()
{
    readSource();
    tokenize();

    printf("========== Source Program ==========\n");
    printSource();

    printf("\n========== 1. Tokens ==========\n");
    printTokens();

    parseProgram();

    printf("\n========== 2. Syntax Analysis Output ==========\n");
    printSyntaxResult();

    printf("\n========== 3. Symbol Table ==========\n");
    printSymbolTable();

    printf("\n========== 4. Semantic Analysis Output ==========\n");
    printSemanticErrors();

    printf("\n========== 5. Three Address Code ==========\n");
    printTAC();

    return 0;
}

void readSource()
{
    int ch;
    int i;

    i = 0;

    if (freopen("input.txt", "r", stdin) == NULL)
    {
        printf("Error: input.txt file not found.\n");
        source[0] = '\0';
        sourceLength = 0;
        return;
    }

    while ((ch = getchar()) != EOF && i < MAX_SOURCE - 1)
    {
        source[i] = ch;
        i++;
    }

    source[i] = '\0';
    sourceLength = i;
}

void printSource()
{
    if (sourceLength == 0)
    {
        printf("No source code loaded.\n");
    }
    else
    {
        printf("%s", source);
        if (source[sourceLength - 1] != '\n')
        {
            printf("\n");
        }
    }
}

void addToken(char text[], int type, int line)
{
    if (tokenCount >= MAX_TOKENS)
    {
        return;
    }

    strcpy(tokenText[tokenCount], text);
    tokenType[tokenCount] = type;
    tokenLine[tokenCount] = line;
    tokenCount++;
}

int isOperatorToken(char text[])
{
    if (strcmp(text, "=") == 0)
    {
        return 1;
    }
    if (strcmp(text, "+") == 0)
    {
        return 1;
    }
    if (strcmp(text, "-") == 0)
    {
        return 1;
    }
    if (strcmp(text, "<") == 0)
    {
        return 1;
    }
    if (strcmp(text, ">") == 0)
    {
        return 1;
    }
    return 0;
}

int isSymbolToken(char text[])
{
    if (strcmp(text, ";") == 0)
    {
        return 1;
    }
    if (strcmp(text, "(") == 0)
    {
        return 1;
    }
    if (strcmp(text, ")") == 0)
    {
        return 1;
    }
    return 0;
}

void tokenize()
{
    int i;
    int j;
    int line;
    char word[MAX_TEXT];
    char single[2];

    i = 0;
    line = 1;

    while (source[i] != '\0')
    {
        if (source[i] == '\n')
        {
            line++;
            i++;
        }
        else if (isspace(source[i]))
        {
            i++;
        }
        else if (isalpha(source[i]) || source[i] == '_')
        {
            j = 0;
            while ((isalnum(source[i]) || source[i] == '_') && j < MAX_TEXT - 1)
            {
                word[j] = source[i];
                j++;
                i++;
            }
            word[j] = '\0';

            if (strcmp(word, "int") == 0)
            {
                addToken(word, TOKEN_INT, line);
            }
            else if (strcmp(word, "if") == 0)
            {
                addToken(word, TOKEN_IF, line);
            }
            else if (strcmp(word, "else") == 0)
            {
                addToken(word, TOKEN_ELSE, line);
            }
            else if (strcmp(word, "while") == 0)
            {
                addToken(word, TOKEN_WHILE, line);
            }
            else
            {
                addToken(word, TOKEN_IDENTIFIER, line);
            }
        }
        else if (isdigit(source[i]))
        {
            j = 0;
            while (isdigit(source[i]) && j < MAX_TEXT - 1)
            {
                word[j] = source[i];
                j++;
                i++;
            }
            word[j] = '\0';
            addToken(word, TOKEN_NUMBER, line);
        }
        else
        {
            single[0] = source[i];
            single[1] = '\0';

            if (isOperatorToken(single))
            {
                addToken(single, TOKEN_OPERATOR, line);
            }
            else if (isSymbolToken(single))
            {
                addToken(single, TOKEN_SYMBOL, line);
            }
            else
            {
                addToken(single, TOKEN_UNKNOWN, line);
            }

            i++;
        }
    }
}

void printTokens()
{
    int i;
    int previousLine;

    previousLine = -1;

    for (i = 0; i < tokenCount; i++)
    {
        if (previousLine != -1 && tokenLine[i] != previousLine)
        {
            printf("\n");
        }

        if (tokenType[i] == TOKEN_INT)
        {
            printf("INT");
        }
        else if (tokenType[i] == TOKEN_IDENTIFIER)
        {
            printf("IDENTIFIER(%s)", tokenText[i]);
        }
        else if (tokenType[i] == TOKEN_NUMBER)
        {
            printf("NUMBER(%s)", tokenText[i]);
        }
        else if (tokenType[i] == TOKEN_IF)
        {
            printf("IF");
        }
        else if (tokenType[i] == TOKEN_ELSE)
        {
            printf("ELSE");
        }
        else if (tokenType[i] == TOKEN_WHILE)
        {
            printf("WHILE");
        }
        else
        {
            printf("%s", tokenText[i]);
        }

        if (i + 1 < tokenCount && tokenLine[i + 1] == tokenLine[i])
        {
            printf(" ");
        }

        previousLine = tokenLine[i];
    }

    printf("\n");
}

int tokenIs(char text[])
{
    if (currentToken < tokenCount && strcmp(tokenText[currentToken], text) == 0)
    {
        return 1;
    }
    return 0;
}

int tokenKindIs(int type)
{
    if (currentToken < tokenCount && tokenType[currentToken] == type)
    {
        return 1;
    }
    return 0;
}

void setSyntaxError(char message[])
{
    if (syntaxError == 0)
    {
        syntaxError = 1;
        strcpy(syntaxErrorMessage, message);
    }
}

void expectText(char text[])
{
    if (syntaxError)
    {
        return;
    }

    if (tokenIs(text))
    {
        currentToken++;
    }
    else
    {
        sprintf(tempLine, "Expected '%s'", text);
        setSyntaxError(tempLine);
    }
}

void copyCurrentToken(char outputText[])
{
    if (currentToken < tokenCount)
    {
        strcpy(outputText, tokenText[currentToken]);
    }
    else
    {
        outputText[0] = '\0';
    }
}

void parseProgram()
{
    currentToken = 0;
    assignmentAffectsSymbol = 1;

    while (currentToken < tokenCount && syntaxError == 0)
    {
        if (tokenKindIs(TOKEN_INT))
        {
            parseDeclaration();
        }
        else if (tokenKindIs(TOKEN_IDENTIFIER))
        {
            assignmentAffectsSymbol = 1;
            parseAssignment();
        }
        else if (tokenKindIs(TOKEN_IF))
        {
            parseIfStatement();
        }
        else if (tokenKindIs(TOKEN_WHILE))
        {
            parseWhileStatement();
        }
        else
        {
            sprintf(tempLine, "Unexpected token '%s'", tokenText[currentToken]);
            setSyntaxError(tempLine);
        }
    }
}

void parseDeclaration()
{
    char name[MAX_TEXT];

    expectText("int");

    if (syntaxError)
    {
        return;
    }

    if (tokenKindIs(TOKEN_IDENTIFIER))
    {
        copyCurrentToken(name);
        currentToken++;
    }
    else
    {
        setSyntaxError("Expected identifier after int");
        return;
    }

    expectText(";");

    if (syntaxError == 0)
    {
        addSymbol(name);
    }
}

void parseAssignment()
{
    char target[MAX_TEXT];
    int targetIndex;
    int canUpdate;

    canUpdate = 1;

    if (tokenKindIs(TOKEN_IDENTIFIER))
    {
        copyCurrentToken(target);
        strcpy(lastIdentifier, target);
        currentToken++;
    }
    else
    {
        setSyntaxError("Expected identifier at start of assignment");
        return;
    }

    targetIndex = findSymbolIndex(target);
    if (targetIndex == -1)
    {
        sprintf(tempLine, "Variable '%s' used without declaration", target);
        addSemanticError(tempLine);
        canUpdate = 0;
    }

    expectText("=");
    parseExpression();
    expectText(";");

    if (syntaxError)
    {
        return;
    }

    if (lastExprSemanticOk == 0)
    {
        canUpdate = 0;
    }

    if (lastExprHasOperator)
    {
        sprintf(tempLine, "t%d = %s %s %s", tempCount, lastExprLeft, lastExprOp, lastExprRight);
        addTAC(tempLine);
        sprintf(tempLine, "%s = t%d", target, tempCount);
        addTAC(tempLine);
        tempCount++;
    }
    else
    {
        sprintf(tempLine, "%s = %s", target, lastExprLeft);
        addTAC(tempLine);
    }

    if (assignmentAffectsSymbol && canUpdate && lastExprValueKnown)
    {
        updateSymbolValue(target, lastExprValue);
    }
}

void parseExpression()
{
    int leftOk;
    int rightOk;
    int leftValue;
    int rightValue;

    lastExprHasOperator = 0;
    lastExprValueKnown = 0;
    lastExprSemanticOk = 1;
    lastExprLeft[0] = '\0';
    lastExprOp[0] = '\0';
    lastExprRight[0] = '\0';
    lastExpressionText[0] = '\0';

    readOperand(lastExprLeft);
    if (syntaxError)
    {
        return;
    }

    leftOk = lastExprSemanticOk;
    leftValue = operandValue(lastExprLeft, leftOk);

    if (tokenIs("+") || tokenIs("-"))
    {
        lastExprHasOperator = 1;
        copyCurrentToken(lastExprOp);
        currentToken++;

        readOperand(lastExprRight);
        if (syntaxError)
        {
            return;
        }

        rightOk = lastExprSemanticOk;
        rightValue = operandValue(lastExprRight, rightOk);

        sprintf(lastExpressionText, "%s %s %s", lastExprLeft, lastExprOp, lastExprRight);

        if (leftOk && rightOk)
        {
            lastExprValueKnown = 1;
            if (strcmp(lastExprOp, "+") == 0)
            {
                lastExprValue = leftValue + rightValue;
            }
            else
            {
                lastExprValue = leftValue - rightValue;
            }
        }
    }
    else
    {
        strcpy(lastExpressionText, lastExprLeft);
        if (leftOk)
        {
            lastExprValueKnown = 1;
            lastExprValue = leftValue;
        }
    }
}

void readOperand(char outputText[])
{
    int symbolIndex;

    if (tokenKindIs(TOKEN_IDENTIFIER))
    {
        copyCurrentToken(outputText);
        symbolIndex = findSymbolIndex(outputText);

        if (symbolIndex == -1)
        {
            sprintf(tempLine, "Variable '%s' used without declaration", outputText);
            addSemanticError(tempLine);
            lastExprSemanticOk = 0;
        }

        currentToken++;
    }
    else if (tokenKindIs(TOKEN_NUMBER))
    {
        copyCurrentToken(outputText);
        currentToken++;
    }
    else
    {
        setSyntaxError("Expected identifier or number in expression");
    }
}

int operandValue(char text[], int ok)
{
    int i;

    if (ok == 0)
    {
        return 0;
    }

    if (isdigit(text[0]))
    {
        return atoi(text);
    }

    i = findSymbolIndex(text);
    if (i != -1)
    {
        return symbolValue[i];
    }

    return 0;
}

void parseIfStatement()
{
    int trueLabel;
    int falseLabel;
    int endLabel;

    expectText("if");
    parseCondition();

    if (syntaxError)
    {
        return;
    }

    trueLabel = newLabel();
    falseLabel = newLabel();
    endLabel = newLabel();

    sprintf(tempLine, "if %s %s %s goto L%d", conditionLeft, conditionOp, conditionRight, trueLabel);
    addTAC(tempLine);
    sprintf(tempLine, "goto L%d", falseLabel);
    addTAC(tempLine);
    sprintf(tempLine, "L%d:", trueLabel);
    addTAC(tempLine);

    assignmentAffectsSymbol = 0;

    if (tokenKindIs(TOKEN_IDENTIFIER))
    {
        parseAssignment();
    }
    else
    {
        setSyntaxError("Expected assignment after if condition");
        return;
    }

    sprintf(tempLine, "goto L%d", endLabel);
    addTAC(tempLine);
    sprintf(tempLine, "L%d:", falseLabel);
    addTAC(tempLine);

    if (tokenKindIs(TOKEN_ELSE))
    {
        expectText("else");
    }
    else
    {
        setSyntaxError("Expected else after if statement");
        return;
    }

    if (tokenKindIs(TOKEN_IDENTIFIER))
    {
        parseAssignment();
    }
    else
    {
        setSyntaxError("Expected assignment after else");
        return;
    }

    sprintf(tempLine, "L%d:", endLabel);
    addTAC(tempLine);
    assignmentAffectsSymbol = 1;
}

void parseWhileStatement()
{
    int startLabel;
    int bodyLabel;
    int endLabel;

    expectText("while");

    startLabel = newLabel();
    bodyLabel = newLabel();
    endLabel = newLabel();

    sprintf(tempLine, "L%d:", startLabel);
    addTAC(tempLine);

    parseCondition();

    if (syntaxError)
    {
        return;
    }

    sprintf(tempLine, "if %s %s %s goto L%d", conditionLeft, conditionOp, conditionRight, bodyLabel);
    addTAC(tempLine);
    sprintf(tempLine, "goto L%d", endLabel);
    addTAC(tempLine);
    sprintf(tempLine, "L%d:", bodyLabel);
    addTAC(tempLine);

    assignmentAffectsSymbol = 0;

    if (tokenKindIs(TOKEN_IDENTIFIER))
    {
        parseAssignment();
    }
    else
    {
        setSyntaxError("Expected assignment after while condition");
        return;
    }

    sprintf(tempLine, "goto L%d", startLabel);
    addTAC(tempLine);
    sprintf(tempLine, "L%d:", endLabel);
    addTAC(tempLine);

    assignmentAffectsSymbol = 1;
}

void parseCondition()
{
    int symbolIndex;

    expectText("(");

    if (tokenKindIs(TOKEN_IDENTIFIER) || tokenKindIs(TOKEN_NUMBER))
    {
        copyCurrentToken(conditionLeft);

        if (tokenKindIs(TOKEN_IDENTIFIER))
        {
            symbolIndex = findSymbolIndex(conditionLeft);
            if (symbolIndex == -1)
            {
                sprintf(tempLine, "Variable '%s' used without declaration", conditionLeft);
                addSemanticError(tempLine);
            }
        }

        currentToken++;
    }
    else
    {
        setSyntaxError("Expected left operand in condition");
        return;
    }

    if (tokenIs("<") || tokenIs(">"))
    {
        copyCurrentToken(conditionOp);
        currentToken++;
    }
    else
    {
        setSyntaxError("Expected relational operator in condition");
        return;
    }

    if (tokenKindIs(TOKEN_IDENTIFIER) || tokenKindIs(TOKEN_NUMBER))
    {
        copyCurrentToken(conditionRight);

        if (tokenKindIs(TOKEN_IDENTIFIER))
        {
            symbolIndex = findSymbolIndex(conditionRight);
            if (symbolIndex == -1)
            {
                sprintf(tempLine, "Variable '%s' used without declaration", conditionRight);
                addSemanticError(tempLine);
            }
        }

        currentToken++;
    }
    else
    {
        setSyntaxError("Expected right operand in condition");
        return;
    }

    expectText(")");
}

int findSymbolIndex(char name[])
{
    int i;

    for (i = 0; i < symbolCount; i++)
    {
        if (strcmp(symbolName[i], name) == 0)
        {
            return i;
        }
    }

    return -1;
}

void addSymbol(char name[])
{
    if (findSymbolIndex(name) != -1)
    {
        sprintf(tempLine, "Duplicate declaration of variable '%s'", name);
        addSemanticError(tempLine);
        return;
    }

    if (symbolCount >= MAX_SYMBOLS)
    {
        addSemanticError("Symbol table is full");
        return;
    }

    strcpy(symbolName[symbolCount], name);
    strcpy(symbolType[symbolCount], "int");
    symbolValue[symbolCount] = 0;
    symbolCount++;
}

void updateSymbolValue(char name[], int value)
{
    int index;

    index = findSymbolIndex(name);
    if (index != -1)
    {
        symbolValue[index] = value;
    }
}

void addSemanticError(char message[])
{
    if (semanticErrorCount >= MAX_ERRORS)
    {
        return;
    }

    strcpy(semanticErrors[semanticErrorCount], message);
    semanticErrorCount++;
}

void addTAC(char line[])
{
    if (tacCount >= MAX_TAC)
    {
        return;
    }

    strcpy(tac[tacCount], line);
    tacCount++;
}

int newLabel()
{
    int label;

    label = labelCount;
    labelCount++;
    return label;
}

void printSyntaxResult()
{
    if (syntaxError)
    {
        printf("Parsing Failed\n");
        printf("Syntax Error: %s\n", syntaxErrorMessage);
    }
    else
    {
        printf("Parsing Successful\n");
        printf("No Syntax Error Found\n");
    }
}

void printSymbolTable()
{
    int i;

    printf("Variable\tType\tValue\n");
    printf("-----------------------------\n");

    if (symbolCount == 0)
    {
        printf("No symbols found\n");
        return;
    }

    for (i = 0; i < symbolCount; i++)
    {
        printf("%s\t\t%s\t%d\n", symbolName[i], symbolType[i], symbolValue[i]);
    }
}

void printSemanticErrors()
{
    int i;

    if (semanticErrorCount == 0)
    {
        printf("No Semantic Error Found\n");
        return;
    }

    for (i = 0; i < semanticErrorCount; i++)
    {
        printf("Semantic Error: %s\n", semanticErrors[i]);
    }
}

void printTAC()
{
    int i;

    if (tacCount == 0)
    {
        printf("No TAC generated\n");
        return;
    }

    for (i = 0; i < tacCount; i++)
    {
        printf("%s\n", tac[i]);
    }
}
