public bool CompareTypes(AST.Type t1, AST.Type t2)
        {
            if (t1.GetType() != t2.GetType())
            {
                return(false);
            }

            if (t1 is ObjectType to1 && t2 is ObjectType to2)
            {
                if (to1.Name != to2.Name)
                {
                    return(false);
                }
            }

            return(true);
        }
        public bool TypeChecking(Node tree, string currentClass, string currentMethod)
        {
            switch (tree)
            {
            case MethodBody methodBody:
            {
                foreach (var stm in methodBody.Statements)
                {
                    TypeChecking(stm, currentClass, currentMethod);
                }
                if (symbolTable.GetMethodReturnType(currentClass, currentMethod).GetType() != TypeOf(methodBody.ReturnExpression, currentClass, currentMethod).GetType())
                {
                    throw new Exception("Wrong return type");
                }
                else
                {
                    return(true);
                }
            }

            case BlockStatement blockStm:
            {
                bool result = false;
                foreach (var stm in blockStm.Statements)
                {
                    result = TypeChecking(stm, currentClass, currentMethod);
                }
                return(result);
            }

            case IfElseBlock ifElse:
            {
                if (!(TypeOf(ifElse.Expression, currentClass, currentMethod) is AST.Boolean))
                {
                    throw new Exception("Sorry but an if statement awaits a boolean :(");
                }
                else
                {
                    bool a = TypeChecking(ifElse.FalseBranch, currentClass, currentMethod);
                    bool b = TypeChecking(ifElse.TrueBranch, currentClass, currentMethod);
                    return(a && b);
                }
            }

            case WhileBlock whileBlock:
            {
                if (!(TypeOf(whileBlock.Expression, currentClass, currentMethod) is AST.Boolean))
                {
                    throw new Exception("While type stupid");
                }
                else
                {
                    return(TypeChecking(whileBlock.Statement, currentClass, currentMethod));
                }
            }

            case VarAssignment varAss:
            {
                AST.Type varType = symbolTable.GetVarType(currentClass, currentMethod, varAss.Id);
                if (varType.GetType() != TypeOf(varAss.Expression, currentClass, currentMethod).GetType())
                {
                    throw new Exception("Variable Assignment type is shit");
                }
                else
                {
                    return(true);
                }
            }

            case ArrayAssignment arrAss:
            {
                if (!(TypeOf(arrAss.Index, currentClass, currentMethod) is Int) || !(TypeOf(arrAss.Value, currentClass, currentMethod) is Int))
                {
                    throw new Exception("???");
                }
                else
                {
                    return(true);
                }
            }

            case Print print:
            {
                if (TypeOf(print.Expression, currentClass, currentMethod) is AST.Int)
                {
                    return(true);
                }
                else
                {
                    return(false);
                }
            }

            case Write write:
            {
                if (TypeOf(write.Expression, currentClass, currentMethod) is AST.IntegerLit)
                {
                    return(true);
                }
                else
                {
                    //throw new Exception("Write is shit");
                    return(true);
                }
            }

            default: throw new Exception("No thingy matched");
            }
        }
        public AST.Type TypeOf(Expression exp, string currentClass, string currentMethod)
        {
            switch (exp)
            {
            case Identifier id:
            {
                return(symbolTable.GetVarType(currentClass, currentMethod, id.Name));
            }

            case And and:
            {
                AST.Type left  = TypeOf(and.Left, currentClass, currentMethod);
                AST.Type right = TypeOf(and.Right, currentClass, currentMethod);

                if (left.GetType() != right.GetType())
                {
                    throw new Exception("Binary operator type fail and shit");
                }
                else
                {
                    return(new AST.Boolean());
                }
            }

            case Plus plus:
            {
                AST.Type left  = TypeOf(plus.Left, currentClass, currentMethod);
                AST.Type right = TypeOf(plus.Right, currentClass, currentMethod);

                if (left.GetType() != right.GetType())
                {
                    throw new Exception("Binary operator type fail and shit");
                }
                else
                {
                    return(new Int());
                }
            }

            case Minus minus:
            {
                AST.Type left  = TypeOf(minus.Left, currentClass, currentMethod);
                AST.Type right = TypeOf(minus.Right, currentClass, currentMethod);

                if (left.GetType() != right.GetType())
                {
                    throw new Exception("Binary operator type fail and shit");
                }
                else
                {
                    return(new Int());
                }
            }

            case Times times:
            {
                AST.Type left  = TypeOf(times.Left, currentClass, currentMethod);
                AST.Type right = TypeOf(times.Right, currentClass, currentMethod);

                if (left.GetType() != right.GetType())
                {
                    throw new Exception("Binary operator type fail and shit");
                }
                else
                {
                    return(new Int());
                }
            }

            case Division division:
            {
                AST.Type left  = TypeOf(division.Left, currentClass, currentMethod);
                AST.Type right = TypeOf(division.Right, currentClass, currentMethod);

                if (left.GetType() != right.GetType())
                {
                    throw new Exception("Binary operator type fail and shit");
                }
                else
                {
                    return(new Int());
                }
            }

            case LessThan lt:
            {
                AST.Type left  = TypeOf(lt.Left, currentClass, currentMethod);
                AST.Type right = TypeOf(lt.Right, currentClass, currentMethod);

                if (left.GetType() != right.GetType())
                {
                    throw new Exception("Binary operator type fail and shit");
                }
                else
                {
                    return(new AST.Boolean());
                }
            }

            case GreaterThan gt:
            {
                AST.Type left  = TypeOf(gt.Left, currentClass, currentMethod);
                AST.Type right = TypeOf(gt.Right, currentClass, currentMethod);

                if (left.GetType() != right.GetType())
                {
                    throw new Exception("Binary operator type fail and shit");
                }
                else
                {
                    return(new AST.Boolean());
                }
            }

            case ArrayAccess arrAcc:
            {
                return(TypeOf(arrAcc.Val, currentClass, currentMethod));
            }

            case ArrayLength arrlength:
            {
                if (!(TypeOf(arrlength.Exp, currentClass, currentMethod) is IntArray))
                {
                    throw new Exception(arrlength.Exp + " is not an array");
                }
                else
                {
                    return(new Int());
                }
            }

            case MethodCall methodCall:
            {
                ObjectType expType;
                if (TypeOf(methodCall.Exp, currentClass, currentMethod) is ObjectType ot)
                {
                    expType = ot;
                }
                else
                {
                    throw new Exception("This method does not exist... Sorry bro");
                }

                if (!symbolTable.ExistsMethod(expType.Name, methodCall.MethodName))
                {
                    throw new Exception("Method does not exist");
                }
                AST.Type[] paramTypes = symbolTable.GetMethodParam(expType.Name, methodCall.MethodName);
                int        i          = 0;

                if (methodCall.Parameters == null)
                {
                    return(symbolTable.GetMethodReturnType(expType.Name, methodCall.MethodName));
                }

                foreach (var par in methodCall.Parameters)
                {
                    if (!CompareTypes(TypeOf(par, currentClass, currentMethod), paramTypes[i]))
                    {
                        throw new Exception("Parameters type error");
                    }
                    i++;
                }

                methodCall.EnhancedName = expType.Name + "$" + methodCall.MethodName;

                return(symbolTable.GetMethodReturnType(expType.Name, methodCall.MethodName));
            }

            case Read read:
            {
                return(new AST.Boolean());
            }

            case BooleanLit booleanLit:
            {
                return(new AST.Boolean());
            }

            case IntegerLit intLit:
            {
                return(new Int());
            }

            case This th:
            {
                return(new ObjectType(currentClass));        // TODO
            }

            case ArrayInstantiation arrInst:
            {
                TypeOf(arrInst.Length, currentClass, currentMethod);
                return(new AST.IntArray());
            }

            case ObjectInstantiation objInst:
            {
                return(new ObjectType(objInst.ObjectId));
            }

            case Not not:
            {
                TypeOf(not.Exp, currentClass, currentMethod);
                return(new AST.Boolean());
            }

            case Parent par:
            {
                return(TypeOf(par.Exp, currentClass, currentMethod));
            }
            }

            return(null);
        }