using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.InteropServices.Marshalling;
using System.Text;
using System.Threading.Tasks;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace CompilerDesignIFlr1
{
    internal class SemanticAnalysis
    {
        List<Quadruple> Quadruples = [];
        internal List<IdentifierTable> IdentifierTables = [new IdentifierTable("Global")];
        internal int QuadrupleIndex = 0;
        internal int TempNameCounter = 0;
        internal Stack<int> ElseIndex = [];

        public void ShiftAnalysis(LR1Unit unit)
        {
            switch (unit.Name)
            {
                case "LBrace":
                    IdentifierTables.Add(new IdentifierTable("LBrace"));
                    break;
                case "RBrace":
                    IdentifierTables.RemoveAt(IdentifierTables.Count - 1);
                    break;
                case "If":
                    IdentifierTables.Add(new IdentifierTable("If"));
                    break;
                case "Else":
                    ElseIndex.Push(QuadrupleIndex);
                    IdentifierTables.RemoveAt(IdentifierTables.Count - 1);
                    IdentifierTables.Add(new IdentifierTable("Else"));
                    break;
                case "Type":
                    IdentifierTables.Add(new IdentifierTable("Type"));
                    break;
                case "Const":
                    IdentifierTables.Add(new IdentifierTable("Const"));
                    break;
            }
        }

        public LR1Unit Analysis(List<LR1Unit> units, LR1Unit unit)
        {
            switch (unit.Id)
            {
                case 0:
                case 1:
                    break;
                case 2:
                {
                    Assert(
                        units.Count == 5
                            && Quadruples[units[1].QuadrupleIndex].To.Type == IdentifierType.Address
                            && ElseIndex.Count > 0
                    );
                    IdentifierTables.RemoveAt(IdentifierTables.Count - 1);
                    var identifier = Quadruples[units[1].QuadrupleIndex].To;
                    identifier.Value = ElseIndex.Pop() + "";
                    break;
                }
                case 3:
                {
                    Assert(
                        units.Count == 3
                            && Quadruples[units[1].QuadrupleIndex].To.Type == IdentifierType.Address
                    );
                    IdentifierTables.RemoveAt(IdentifierTables.Count - 1);
                    var identifier = Quadruples[units[1].QuadrupleIndex].To;
                    identifier.Value = QuadrupleIndex + "";
                    break;
                }
                case 4:
                {
                    Assert(
                        units.Count == 5
                            && Quadruples[units[1].QuadrupleIndex].To.Type == IdentifierType.Address
                            && ElseIndex.Count > 0
                    );
                    IdentifierTables.RemoveAt(IdentifierTables.Count - 1);
                    var identifier = Quadruples[units[1].QuadrupleIndex].To;
                    identifier.Value = ElseIndex.Pop() + "";
                    break;
                }
                case 5:
                    Assert(units.Count == 1);
                    //IdentifierTables.RemoveAt(IdentifierTables.Count - 1);
                    break;
                case 6:
                {
                    Assert(units.Count == 3 && units[1].Value.StartsWith('@'));
                    string tempName = GetTempName();
                    Identifier oldIdentifier = GetIdentifier(units[1].Value);
                    var valueIdentifier = GetFinalIdentifier(units[1].Value);
                    Assert(valueIdentifier.Type == IdentifierType.Int);
                    var value =
                        int.Parse(GetFinalValue(valueIdentifier.Value)) == 0 ? "True" : "False";
                    Identifier identifier = Identifier.Address(tempName, value);
                    AddToIdentifierTable(identifier);
                    Assert(unit.QuadrupleIndex == 0);
                    unit.QuadrupleIndex = QuadrupleIndex;
                    unit.Value = tempName;
                    Quadruples.Add(
                        new Quadruple(
                            "j==",
                            QuadrupleIndex++,
                            oldIdentifier,
                            Identifier.Literal("0"),
                            identifier
                        )
                    );
                    break;
                }
                case 7:
                {
                    string tempName = GetTempName();
                    Assert(units.Count == 2);
                    if (units[1].Value == "None")
                    {
                        unit.Value = units[0].Value;
                        break;
                    }
                    Assert(units[0].Value.StartsWith("@"));
                    Identifier identifier1 = GetFinalIdentifier(units[0].Value);
                    string @operator = units[1].Value.Split()[0].Trim();
                    Identifier identifier2 = GetFinalIdentifier(units[1].Value.Split()[1].Trim());
                    Assert(
                        identifier1.Type is IdentifierType.Int
                            && identifier2.Type is IdentifierType.Int
                    );
                    int value1 = int.Parse(GetFinalValue(identifier1.Value));
                    int value2 = int.Parse(GetFinalValue(identifier2.Value));
                    var value = @operator switch
                    {
                        "||" => value1 | value2,
                        "&&" => value1 & value2,
                        _ => throw new Exception("Type is not supported")
                    };
                    Identifier tempIdentifier = new(IdentifierType.Int, tempName, value + "");
                    AddToIdentifierTable(tempIdentifier);
                    Assert(unit.QuadrupleIndex == 0);
                    unit.Value = tempName;
                    unit.QuadrupleIndex = QuadrupleIndex;
                    Quadruples.Add(
                        new Quadruple(
                            @operator,
                            QuadrupleIndex++,
                            identifier1,
                            identifier2,
                            tempIdentifier
                        )
                    );
                    break;
                }
                case 8:
                {
                    string tempName = GetTempName();
                    Assert(units.Count == 3);
                    Assert(units[0].Value.StartsWith('@') && units[2].Value.StartsWith('@'));
                    var value = OperatorCalculator(units[0].Value, units[1].Value, units[2].Value);
                    Identifier identifier = new Identifier(
                        IdentifierType.Int,
                        tempName,
                        value + ""
                    );
                    unit.Value = tempName;
                    unit.QuadrupleIndex = QuadrupleIndex;
                    Quadruples.Add(
                        new Quadruple(
                            units[1].Value,
                            QuadrupleIndex++,
                            GetIdentifier(units[0].Value),
                            GetIdentifier(units[2].Value),
                            identifier
                        )
                    );
                    AddToIdentifierTable(identifier);
                    break;
                }
                case 9:
                {
                    string tempName = GetTempName();
                    Assert(units.Count == 1 && units[0].Value.StartsWith('@'));
                    var oldIdentifier = GetFinalIdentifier(units[0].Value);
                    Assert(oldIdentifier.Type == IdentifierType.Int);
                    var value = int.Parse(GetFinalValue(oldIdentifier.Value)) != 0 ? 1 : 0;
                    Identifier identifier = new Identifier(
                        IdentifierType.Int,
                        tempName,
                        value + ""
                    );
                    unit.Value = tempName;
                    unit.QuadrupleIndex = QuadrupleIndex;
                    Quadruples.Add(
                        new Quadruple(
                            "!=",
                            QuadrupleIndex++,
                            GetIdentifier(units[0].Value),
                            Identifier.Literal("0"),
                            identifier
                        )
                    );
                    AddToIdentifierTable(identifier);
                    break;
                }
                case 10:
                {
                    string tempName = GetTempName();
                    Assert(units.Count == 2);
                    if (units[1].Value == "None")
                    {
                        unit.Value = units[0].Value;
                        break;
                    }
                    Assert(units[0].Value.StartsWith("@"));
                    Identifier identifier1 = GetIdentifier(units[0].Value);
                    string @operator = units[1].Value.Split()[0].Trim();
                    Identifier identifier2 = GetIdentifier(units[1].Value.Split()[1].Trim());
                    Assert(
                        identifier1.Type is IdentifierType.Int
                            && identifier2.Type is IdentifierType.Int
                    );
                    int value1 = int.Parse(GetFinalValue(identifier1.Value));
                    int value2 = int.Parse(GetFinalValue(identifier2.Value));
                    var value = @operator switch
                    {
                        "+" => value1 + value2,
                        "-" => value1 - value2,
                        _ => throw new Exception("Type is not supported")
                    };
                    Identifier tempIdentifier = new(IdentifierType.Int, tempName, value + "");
                    AddToIdentifierTable(tempIdentifier);
                    Assert(unit.QuadrupleIndex == 0);
                    unit.Value = tempName;
                    unit.QuadrupleIndex = QuadrupleIndex;
                    Quadruples.Add(
                        new Quadruple(
                            @operator,
                            QuadrupleIndex++,
                            identifier1,
                            identifier2,
                            tempIdentifier
                        )
                    );
                    break;
                }
                case 11:
                case 12:
                    break;
                case 13:
                case 14:
                case 15:
                case 16:
                case 17:
                    break;
                case 18:
                {
                    Assert(units.Count == 3 && units[2].Value.StartsWith('@'));
                    Identifier oldIdentifier = GetIdentifier(units[2].Value);
                    var identifierValue = GetFinalIdentifier(units[2].Value).Value;
                    Identifier identifier = new Identifier(
                        oldIdentifier.Type,
                        units[0].Value,
                        identifierValue
                    );
                    unit.Value = units[0].Value;
                    unit.QuadrupleIndex = QuadrupleIndex;
                    if (IdentifierTables[^1].Value == "Type")
                        AddToIdentifierTable(identifier);
                    else
                        UpdateIdentifier(identifier);
                    Quadruples.Add(
                        new Quadruple("=", QuadrupleIndex++, oldIdentifier, null, identifier)
                    );
                    break;
                }
                case 19:
                {
                    string tempName = GetTempName();
                    Assert(units.Count == 2);
                    if (units[1].Value == "None")
                    {
                        unit.Value = units[0].Value;
                        break;
                    }
                    Assert(units[0].Value.StartsWith("@"));
                    Identifier identifier1 = GetIdentifier(units[0].Value);
                    string @operator = units[1].Value.Split()[0].Trim();
                    Identifier identifier2 = GetIdentifier(units[1].Value.Split()[1].Trim());
                    Assert(
                        identifier1.Type is IdentifierType.Int
                            && identifier2.Type is IdentifierType.Int
                    );
                    double value1 = int.Parse(GetFinalValue(identifier1.Value));
                    double value2 = int.Parse(GetFinalValue(identifier2.Value));
                    int value = (int)(
                        @operator switch
                        {
                            "*" => value1 * value2,
                            "/" => value1 / value2,
                            "%" => value1 % value2,
                            _ => throw new Exception("Type is not supported")
                        }
                    );
                    Identifier tempIdentifier = new(IdentifierType.Int, tempName, value + "");
                    AddToIdentifierTable(tempIdentifier);
                    Assert(unit.QuadrupleIndex == 0);
                    unit.Value = tempName;
                    unit.QuadrupleIndex = QuadrupleIndex;
                    Quadruples.Add(
                        new Quadruple(
                            @operator,
                            QuadrupleIndex++,
                            identifier1,
                            identifier2,
                            tempIdentifier
                        )
                    );
                    break;
                }
                case 20:
                {
                    Assert(units.Count == 2);
                    foreach (var (name, identifier) in IdentifierTables[^1].Table)
                    {
                        if (name.StartsWith("@"))
                            continue;
                        if (
                            identifier.Type == IdentifierType.Int
                            || identifier.Type == IdentifierType.Char
                        )
                        {
                            identifier.Type = identifier.Type switch
                            {
                                IdentifierType.Int => IdentifierType.ConstInt,
                                IdentifierType.Char => IdentifierType.ConstChar,
                                _ => throw new Exception("Type is not supported.")
                            };
                            IdentifierTables[^2].Table.Add(name, identifier);
                        }
                        else
                            throw new InvalidOperationException("Wrong type");
                    }
                    IdentifierTables.RemoveAt(IdentifierTables.Count - 1);
                    break;
                }
                case 21:
                {
                    Assert(units.Count == 3);
                    IdentifierType type = units[0].Value switch
                    {
                        "int" => IdentifierType.Int,
                        "char" => IdentifierType.Char,
                        _ => throw new Exception("Type is not supported.")
                    };
                    foreach (var (name, identifier) in IdentifierTables[^1].Table)
                    {
                        if (name.StartsWith("@"))
                            continue;
                        if (identifier.Type == IdentifierType.Unknown || identifier.Type == type)
                        {
                            identifier.Type = type;
                            IdentifierTables[^2].Table.Add(name, identifier);
                        }
                        else
                            throw new InvalidOperationException("Wrong type");
                    }
                    IdentifierTables.RemoveAt(IdentifierTables.Count - 1);
                    break;
                }
                case 22:
                case 23:
                    Assert(units.Count == 1);
                    unit.Value = units[0].Value;
                    break;
                case 24:
                {
                    Assert(units.Count == 1);
                    string tempName = GetTempName();
                    Identifier identifier = GetIdentifier(units[0].Value);
                    Assert(identifier.Type != IdentifierType.Literal);
                    Identifier tempIdentifier =
                        identifier.Type == IdentifierType.Int
                            ? Identifier.Int(tempName, identifier.Value)
                            : Identifier.Char(tempName, identifier.Value);
                    Assert(unit.QuadrupleIndex == 0);
                    unit.Value = tempName;
                    unit.QuadrupleIndex = QuadrupleIndex;
                    Quadruples.Add(
                        new Quadruple("=", QuadrupleIndex++, identifier, null, tempIdentifier)
                    );
                    AddToIdentifierTable(tempIdentifier);

                    break;
                }
                case 25:
                {
                    Assert(units.Count == 1);
                    string tempName = GetTempName();
                    Identifier tempIdentifier = Identifier.Int(tempName, units[0].Value);
                    Quadruples.Add(
                        new Quadruple(
                            "=",
                            QuadrupleIndex++,
                            Identifier.Literal(units[0].Value),
                            null,
                            tempIdentifier
                        )
                    );
                    Assert(unit.QuadrupleIndex == 0);
                    unit.Value = tempName;
                    unit.QuadrupleIndex = QuadrupleIndex;
                    AddToIdentifierTable(tempIdentifier);
                    break;
                }
                case 26:
                {
                    Assert(units.Count == 1);
                    string tempName = GetTempName();
                    Identifier tempIdentifier = Identifier.Char(tempName, units[0].Value);
                    Quadruples.Add(
                        new Quadruple(
                            "=",
                            QuadrupleIndex++,
                            Identifier.Literal(units[0].Value),
                            null,
                            tempIdentifier
                        )
                    );
                    Assert(unit.QuadrupleIndex == 0);
                    unit.Value = tempName;
                    unit.QuadrupleIndex = QuadrupleIndex;
                    AddToIdentifierTable(tempIdentifier);

                    break;
                }
                case 27:
                {
                    Assert(units.Count == 3);
                    Identifier identifier = GetIdentifier(units[1].Value);
                    string tempName = GetTempName();
                    Identifier tempIdentifier = identifier.Type switch
                    {
                        IdentifierType.Int => Identifier.Int(tempName, identifier.Value),
                        IdentifierType.Char => Identifier.Char(tempName, identifier.Value),
                        _ => throw new Exception("Type is not supported.")
                    };
                    Assert(unit.QuadrupleIndex == 0);
                    unit.Value = tempName;
                    unit.QuadrupleIndex = QuadrupleIndex;
                    Quadruples.Add(
                        new Quadruple("=", QuadrupleIndex++, identifier, null, tempIdentifier)
                    );
                    AddToIdentifierTable(tempIdentifier);
                    break;
                }
                case 28:
                case 29:
                    Assert(units.Count == 1);
                    unit.Value = units[0].Value;
                    break;
                case 30:
                case 31:
                case 32:
                    Assert(units.Count == 1);
                    unit.Value = units[0].Value;
                    break;
                case 33:
                {
                    Assert(units.Count == 1);
                    unit.Value = units[0].Value;
                    string tempName = GetTempName();
                    Identifier tempIdentifier = Identifier.Int(tempName, unit.Value);
                    Quadruples.Add(
                        new Quadruple(
                            "=",
                            QuadrupleIndex++,
                            Identifier.Literal(unit.Value),
                            null,
                            tempIdentifier
                        )
                    );
                    Assert(unit.QuadrupleIndex == 0);
                    unit.Value = tempName;
                    unit.QuadrupleIndex = QuadrupleIndex;
                    AddToIdentifierTable(tempIdentifier);
                    break;
                }
                case 34:
                {
                    Assert(units.Count == 2);
                    unit.Value = "-" + units[1].Value;
                    string tempName = GetTempName();
                    Identifier tempIdentifier = Identifier.Int(tempName, unit.Value);
                    Quadruples.Add(
                        new Quadruple(
                            "=",
                            QuadrupleIndex++,
                            Identifier.Literal(unit.Value),
                            null,
                            tempIdentifier
                        )
                    );
                    Assert(unit.QuadrupleIndex == 0);
                    unit.Value = tempName;
                    unit.QuadrupleIndex = QuadrupleIndex;
                    AddToIdentifierTable(tempIdentifier);
                    break;
                }
                case 35:
                {
                    Assert(units.Count == 2);
                    unit.Value = units[1].Value;
                    string tempName = GetTempName();
                    Identifier tempIdentifier = Identifier.Int(tempName, unit.Value);
                    Quadruples.Add(
                        new Quadruple(
                            "=",
                            QuadrupleIndex++,
                            Identifier.Literal(unit.Value),
                            null,
                            tempIdentifier
                        )
                    );
                    Assert(unit.QuadrupleIndex == 0);
                    unit.Value = tempName;
                    unit.QuadrupleIndex = QuadrupleIndex;
                    AddToIdentifierTable(tempIdentifier);
                    break;
                }
                case 36:
                case 37:
                case 38:
                case 39:
                case 40:
                case 41:
                    Assert(units.Count == 1);
                    unit.Value = units[0].Value;
                    break;
                case 42:
                case 43:
                    Assert(units.Count == 1);
                    unit.Value = units[0].Value;
                    break;
                case 44:
                case 45:
                    break;
                case 46:
                    Assert(units.Count == 0);
                    unit.Value = "None";
                    break;
                case 47:
                {
                    Assert(units.Count == 3);
                    if (units[2].Value == "None")
                    {
                        unit.Value = units[0].Value + " " + units[1].Value;
                        break;
                    }
                    string tempName = GetTempName();
                    string @operator = units[2].Value.Split()[0].Trim();
                    Assert(@operator is "||" or "&&");
                    string unit2Name = units[2].Value.Split()[1].Trim();
                    Identifier unit1Identifier = GetIdentifier(units[1].Value);
                    Identifier unit2Identifier = GetIdentifier(unit2Name);
                    string value1 = GetFinalValue(units[1].Value);
                    string value2 = GetFinalValue(unit2Name);
                    Assert(
                        unit1Identifier.Type == IdentifierType.Int
                            && unit2Identifier.Type == IdentifierType.Int
                    );
                    var value = @operator switch
                    {
                        "||" => int.Parse(value1) | int.Parse(value2),
                        "&&" => int.Parse(value1) & int.Parse(value2),
                        _ => throw new Exception("Type is not supported")
                    };
                    Identifier tempIdentifier = new Identifier(
                        IdentifierType.Int,
                        tempName,
                        value + ""
                    );
                    AddToIdentifierTable(tempIdentifier);
                    unit.Value = units[0].Value + " " + tempName;
                    Quadruples.Add(
                        new Quadruple(
                            @operator,
                            QuadrupleIndex++,
                            unit1Identifier,
                            unit2Identifier,
                            tempIdentifier
                        )
                    );
                    break;
                }
                case 48:
                    Assert(units.Count == 0);
                    unit.Value = "None";
                    break;
                case 49:
                {
                    Assert(units.Count == 3);
                    if (units[2].Value == "None")
                    {
                        unit.Value = units[0].Value + " " + units[1].Value;
                        break;
                    }
                    string tempName = GetTempName();
                    string @operator = units[2].Value.Split()[0].Trim();
                    Assert(@operator is "+" or "-");
                    string unit2Name = units[2].Value.Split()[1].Trim();
                    Identifier unit1Identifier = GetIdentifier(units[1].Value);
                    Identifier unit2Identifier = GetIdentifier(unit2Name);
                    string value1 = GetFinalValue(units[1].Value);
                    string value2 = GetFinalValue(unit2Name);
                    Assert(
                        unit1Identifier.Type == IdentifierType.Int
                            && unit2Identifier.Type == IdentifierType.Int
                    );
                    var value = @operator switch
                    {
                        "+" => int.Parse(value1) + int.Parse(value2),
                        "-" => int.Parse(value1) - int.Parse(value2),
                        _ => throw new Exception("Type is not supported")
                    };
                    Identifier tempIdentifier = new Identifier(
                        IdentifierType.Int,
                        tempName,
                        value + ""
                    );
                    AddToIdentifierTable(tempIdentifier);
                    unit.Value = units[0].Value + " " + tempName;
                    Quadruples.Add(
                        new Quadruple(
                            @operator,
                            QuadrupleIndex++,
                            unit1Identifier,
                            unit2Identifier,
                            tempIdentifier
                        )
                    );
                    break;
                }
                case 50:
                case 51:
                    break;
                case 52:
                    Assert(units.Count == 0);
                    unit.Value = "None";
                    break;
                case 53:
                {
                    Assert(units.Count == 3);
                    if (units[2].Value == "None")
                    {
                        unit.Value = units[0].Value + " " + units[1].Value;
                        break;
                    }
                    string tempName = GetTempName();
                    string @operator = units[2].Value.Split()[0].Trim();
                    Assert(@operator is "*" or "/" or "%");
                    string unit2Name = units[2].Value.Split()[1].Trim();
                    Identifier unit1Identifier = GetIdentifier(units[1].Value);
                    Identifier unit2Identifier = GetIdentifier(unit2Name);
                    string value1 = GetFinalValue(units[1].Value);
                    string value2 = GetFinalValue(unit2Name);
                    Assert(
                        unit1Identifier.Type == IdentifierType.Int
                            && unit2Identifier.Type == IdentifierType.Int
                    );
                    var value = (int)(
                        @operator switch
                        {
                            "*" => int.Parse(value1) * int.Parse(value2),
                            "/" => int.Parse(value1) / int.Parse(value2),
                            "%" => int.Parse(value1) % int.Parse(value2),
                            _ => throw new Exception("Type is not supported")
                        }
                    );
                    Identifier tempIdentifier = new Identifier(
                        IdentifierType.Int,
                        tempName,
                        value + ""
                    );
                    AddToIdentifierTable(tempIdentifier);
                    unit.Value = units[0].Value + " " + tempName;
                    Quadruples.Add(
                        new Quadruple(
                            @operator,
                            QuadrupleIndex++,
                            unit1Identifier,
                            unit2Identifier,
                            tempIdentifier
                        )
                    );
                    break;
                }
                case 54:
                    Assert(units.Count == 1);
                    unit.Value = units[0].Value;
                    unit.QuadrupleIndex = units[0].QuadrupleIndex;
                    AddToIdentifierTable(Identifier.Unknown(unit.Value));
                    break;
                case 55:
                    Assert(units.Count == 1);
                    unit.Value = units[0].Value;
                    unit.QuadrupleIndex = units[0].QuadrupleIndex;
                    break;
                case 56:
                    Assert(units.Count == 0);
                    unit.Value = "None";
                    break;
                case 57:
                    Assert(units.Count == 3);
                    unit.Value = units[1].Value;
                    unit.QuadrupleIndex = units[1].QuadrupleIndex;
                    AddToIdentifierTable(Identifier.Unknown(unit.Value));
                    break;
                case 58:
                    Assert(units.Count == 3);
                    unit.Value = units[1].Value;
                    unit.QuadrupleIndex = units[1].QuadrupleIndex;
                    break;
                default:
                    break;
            }
            return unit;
        }

        internal void PrintQuadruples()
        {
            for (int i = 0; i < Quadruples.Count; i++)
            {
                Console.Write($"{i, 3}");
                Console.WriteLine(Quadruples[i]);
            }
        }

        internal static bool Assert(bool val) =>
            !val ? throw new Exception("Assertion failed.") : false;

        internal void AddToIdentifierTable(Identifier identifier)
        {
            if (IdentifierTables[^1].Table.ContainsKey(identifier.Name))
                throw new InvalidOperationException($"{identifier.Name} redefined.");
            IdentifierTables[^1].Table.Add(identifier.Name, identifier);
        }

        internal string GetTempName() => "@T" + TempNameCounter++;

        internal Identifier GetIdentifier(string identifier)
        {
            for (int i = 1; i <= IdentifierTables.Count; i++)
            {
                if (IdentifierTables[^i].Table.TryGetValue(identifier, out var val))
                    return val;
            }
            throw new InvalidOperationException($"No identifier called: {identifier}");
        }

        internal void UpdateIdentifier(Identifier identifier)
        {
            for (int i = 1; i <= IdentifierTables.Count; i++)
            {
                if (IdentifierTables[^i].Table.TryGetValue(identifier.Name, out var oldIdentifier))
                {
                    if (oldIdentifier.Type is IdentifierType.ConstInt or IdentifierType.ConstChar)
                        throw new InvalidOperationException(
                            "Change the value of const is not allowed."
                        );
                    IdentifierTables[^i].Table[identifier.Name] = identifier;
                    return;
                }
            }
            throw new InvalidOperationException($"No identifier called: {identifier.Name}");
        }

        internal Identifier GetFinalIdentifier(string identifier)
        {
            var nextIdentifier = GetIdentifier(identifier);
            if (nextIdentifier.Value.StartsWith("@"))
                return GetFinalIdentifier(nextIdentifier.Value);
            else
                return nextIdentifier;
        }

        internal string GetFinalValue(string any)
        {
            string ans = any;
            if (any.StartsWith("@"))
            {
                ans = GetFinalIdentifier(any).Value;
            }
            if (ans.Length == 0)
                throw new InvalidOperationException("Identifier used before assignment");
            return ans;
        }

        internal int OperatorCalculator(string a, string op, string b)
        {
            if (a.StartsWith('@'))
            {
                var identifier = GetFinalIdentifier(a);
                if (identifier.Type != IdentifierType.Int)
                    throw new InvalidOperationException("This type is not allowed to compare.");
                a = GetFinalValue(identifier.Value);
            }
            if (b.StartsWith('@'))
            {
                var identifier = GetFinalIdentifier(b);
                if (identifier.Type != IdentifierType.Int)
                    throw new InvalidOperationException("This type is not allowed to compare.");
                b = GetFinalValue(identifier.Value);
            }
            int av = int.Parse(a),
                bv = int.Parse(b);
            return op.Trim() switch
            {
                "==" => av == bv,
                ">" => av > bv,
                ">=" => av >= bv,
                "<" => av < bv,
                "<=" => av <= bv,
                "!=" => av != bv,
                _ => throw new Exception("Type is not uspported.")
            }
                ? 1
                : 0;
        }
    }

    enum IdentifierType
    {
        Int,
        Char,
        ConstInt,
        ConstChar,
        Literal,
        Address,
        Unknown
    }

    internal class Identifier
    {
        internal IdentifierType Type { get; set; } = IdentifierType.Literal;
        internal string Name { get; set; } = "";
        internal string Value { get; set; } = "";

        internal Identifier(IdentifierType type, string name, string value)
        {
            Type = type;
            Name = name;
            Value = value;
        }

        internal static Identifier Literal(string value) =>
            new Identifier(IdentifierType.Literal, "", value);

        internal static Identifier Literal(string name, string value) =>
            new Identifier(IdentifierType.Literal, name, value);

        internal static Identifier Int(string name, string value) =>
            new Identifier(IdentifierType.Int, name, value);

        internal static Identifier Char(string name, string value) =>
            new Identifier(IdentifierType.Char, name, value);

        internal static Identifier Address(string name, string value) =>
            new Identifier(IdentifierType.Address, name, value);

        internal static Identifier Unknown(string name) =>
            new Identifier(IdentifierType.Unknown, name, "");

        internal Identifier Clone()
        {
            return new Identifier(Type, Name, Value);
        }
    }

    internal class Quadruple
    {
        internal string Operator;
        internal int Index = 0;
        internal Identifier A;
        internal Identifier? B;
        internal Identifier To;

        internal Quadruple(string @operator, int index, Identifier a, Identifier? b, Identifier to)
        {
            Operator = @operator;
            this.Index = index;
            A = a;
            B = b;
            To = to;
        }

        internal Quadruple Clone()
        {
            return new Quadruple(Operator, Index, A.Clone(), B?.Clone(), To.Clone());
        }

        public override string ToString()
        {
            var sb = new StringBuilder();
            string GetValue(Identifier? identifier)
            {
                if (identifier is null)
                    return "";
                if (identifier.Type == IdentifierType.Address)
                    return identifier.Value;
                if (identifier.Name.Length != 0)
                    return identifier.Name;
                return identifier.Value;
            }
            sb.Append('(')
                .Append(Operator.PadLeft(4, ' '))
                .Append(',')
                .Append(GetValue(A).PadLeft(4, ' '))
                .Append(',')
                .Append(GetValue(B).PadLeft(4, ' '))
                .Append(',')
                .Append(GetValue(To).PadLeft(4, ' '))
                .Append(")");
            return sb.ToString();
        }
    }

    internal class IdentifierTable
    {
        internal string Value = "";
        internal Dictionary<string, Identifier> Table { get; set; } = [];

        internal IdentifierTable(string value)
        {
            Value = value;
        }
    }
}
/*
文法:
key: Program
     0 Program ::= . StatementList
key: StatementList
     1 StatementList ::= . LBrace Statement_0 RBrace
key: IfStatement
     2 IfStatement ::= . If ConditionPart PartIfStatement Else Statement
     3 IfStatement ::= . If ConditionPart Statement
key: PartIfStatement
     4 PartIfStatement ::= . If ConditionPart PartIfStatement Else PartIfStatement
     5 PartIfStatement ::= . NoIfStatement
key: ConditionPart
     6 ConditionPart ::= . LParen Condition RParen
key: Condition
     7 Condition ::= . ConditionalExpression LogicalOperator_ConditionalExpression_0
key: ConditionalExpression
     8 ConditionalExpression ::= . Expression Operator Expression
     9 ConditionalExpression ::= . Expression
key: Expression
    10 Expression ::= . Term AddLike_Term_0
key: Statement
    11 Statement ::= . IfStatement
    12 Statement ::= . NoIfStatement
key: NoIfStatement
    13 NoIfStatement ::= . AssignmentStatement Semicolon
    14 NoIfStatement ::= . VariableDefinition Semicolon
    15 NoIfStatement ::= . LBrace Statement_1 RBrace
    16 NoIfStatement ::= . ConstantDefinition Semicolon
    17 NoIfStatement ::= . Semicolon
key: AssignmentStatement
    18 AssignmentStatement ::= . Identifier Equal Expression
key: Term
    19 Term ::= . Factor MultiplyLike_Factor_0
key: ConstantDefinition
    20 ConstantDefinition ::= . Const VariableDefinition
key: VariableDefinition
    21 VariableDefinition ::= . Type Identifier__AssignmentStatement_0 Comma_Identifier__Comma_AssignmentStatement_0
key: Type
    22 Type ::= . Int
    23 Type ::= . Char
key: Factor
    24 Factor ::= . Identifier
    25 Factor ::= . Number
    26 Factor ::= . Character
    27 Factor ::= . LParen Expression RParen
key: AddLike
    28 AddLike ::= . Plus
    29 AddLike ::= . Minus
key: MultiplyLike
    30 MultiplyLike ::= . Multiply
    31 MultiplyLike ::= . Divide
    32 MultiplyLike ::= . Modulo
key: Number
    33 Number ::= . UnsignedNumber
    34 Number ::= . Minus UnsignedNumber
    35 Number ::= . Plus UnsignedNumber
key: Operator
    36 Operator ::= . EqualTo
    37 Operator ::= . NotEqualTo
    38 Operator ::= . LessThan
    39 Operator ::= . GreaterThan
    40 Operator ::= . LessThanOrEqual
    41 Operator ::= . GreaterThanOrEqual
key: LogicalOperator
    42 LogicalOperator ::= . And
    43 LogicalOperator ::= . Or
key: Statement_0
    44 Statement_0 ::= .
    45 Statement_0 ::= . Statement Statement_0
key: LogicalOperator_ConditionalExpression_0
    46 LogicalOperator_ConditionalExpression_0 ::= .
    47 LogicalOperator_ConditionalExpression_0 ::= . LogicalOperator ConditionalExpression LogicalOperator_ConditionalExpression_0
key: AddLike_Term_0
    48 AddLike_Term_0 ::= .
    49 AddLike_Term_0 ::= . AddLike Term AddLike_Term_0
key: Statement_1
    50 Statement_1 ::= .
    51 Statement_1 ::= . Statement Statement_1
key: MultiplyLike_Factor_0
    52 MultiplyLike_Factor_0 ::= .
    53 MultiplyLike_Factor_0 ::= . MultiplyLike Factor MultiplyLike_Factor_0
key: Identifier__AssignmentStatement_0
    54 Identifier__AssignmentStatement_0 ::= . Identifier
    55 Identifier__AssignmentStatement_0 ::= . AssignmentStatement
key: Comma_Identifier__Comma_AssignmentStatement_0
    56 Comma_Identifier__Comma_AssignmentStatement_0 ::= .
    57 Comma_Identifier__Comma_AssignmentStatement_0 ::= . Comma Identifier Comma_Identifier__Comma_AssignmentStatement_0
    58 Comma_Identifier__Comma_AssignmentStatement_0 ::= . Comma AssignmentStatement Comma_Identifier__Comma_AssignmentStatement_0
*/