private static int ParseConditionalExpr(SyntaxNode parent, Token[] tkns, int idx)
        {
            var nNode = new OperatorSyntaxNode(SyntaxNodeType.ConditionalNode, tkns[idx]);

            parent.ChildNodes.Add(nNode);
            idx = ParseNullCoalesceExpr(nNode, tkns, idx);

            if (tkns[idx].TokenType == TokenType.Operator && tkns[idx].TokenValue == "?")
            {
                nNode.Operator.Add(tkns[idx]);
                idx = ParseNullCoalesceExpr(nNode, tkns, idx + 1);
                if (tkns[idx].TokenType == TokenType.Operator && tkns[idx].TokenValue == ":")
                {
                    return(ParseNullCoalesceExpr(nNode, tkns, idx + 1));
                }
                else
                {
                    throw new SyntaxException("Incomplete conditional operator.", tkns[idx]);
                }
            }
            else
            {
                return(idx);
            }
        }
        private static int ParseType(SyntaxNode parent, Token[] tkns, int idx)
        {
            var nNode = new OperatorSyntaxNode(SyntaxNodeType.TypeNode, tkns[idx]);

            parent.ChildNodes.Add(nNode);

            string[] ops   = new string[] { "++", "--" };
            var      terms = new TokenType[] { TokenType.Semicolon, TokenType.Comma, TokenType.ClosingBracket, TokenType.ClosingAngle, TokenType.OpeningBrace };

            idx = ParseTypeReference(nNode, tkns, idx, false);
            if (tkns[idx].TokenType == TokenType.Operator && ops.Contains(tkns[idx].TokenValue))
            {
                nNode.Operator.Add(tkns[idx]);
                return(idx + 1);
            }
            else if (tkns[idx].TokenType == TokenType.Identifier)
            {
                nNode.ChildNodes.Add(new SyntaxNode(SyntaxNodeType.VariableDeclNode, tkns[idx]));
                return(idx);
            }
            else if (tkns[idx].TokenType == TokenType.ClosingParen && !terms.Contains(tkns[idx + 1].TokenType))
            {
                //Type Cast
                var nNode2 = new SyntaxNode(SyntaxNodeType.TypeCastNode, tkns[idx]);
                nNode.ChildNodes.Add(nNode2);
                idx = ParsePrimaryExpr(nNode2, tkns, idx + 1);
            }
            return(idx);
        }
        private static int ParseUnaryExpr(SyntaxNode parent, Token[] tkns, int idx)
        {
            var nNode = new OperatorSyntaxNode(SyntaxNodeType.UnaryNode, tkns[idx]);

            parent.ChildNodes.Add(nNode);

            string[] ops = new string[] { "+", "-", "!", "~", "++", "--", "&", "*" };

            //TODO: Parse type casting

            if (tkns[idx].TokenType == TokenType.Operator && ops.Contains(tkns[idx].TokenValue))
            {
                nNode.Operator.Add(tkns[idx]);
                return(ParseUnaryExpr(nNode, tkns, idx + 1));
            }/*else if(tkns[idx].TokenType == TokenType.OpeningParen){
              * nNode.Operator.Add(tkns[idx]);
              * idx = ParseUnaryExpr(nNode, tkns, idx + 1);
              * if(tkns[idx].TokenType != TokenType.ClosingParen)
              *     throw new SyntaxException("Expected closing parenthesis.", tkns[idx]);
              * return ParseUnaryExpr(nNode, tkns, idx + 1);
              * }*/
            else
            {
                return(ParsePrimaryExpr(nNode, tkns, idx));
            }
        }
        private static int ParseRelationTypeTestingExpr(SyntaxNode parent, Token[] tkns, int idx)
        {
            var nNode = new OperatorSyntaxNode(SyntaxNodeType.RelationalTypeTestingNode, tkns[idx]);

            parent.ChildNodes.Add(nNode);
            idx = ParseShiftExpr(nNode, tkns, idx);

            string[] ops_1 = new string[] { "<=", ">=" };
            string[] ops_2 = new string[] { "is", "as" };

            if (tkns[idx].TokenType == TokenType.OpeningAngle | tkns[idx].TokenType == TokenType.ClosingAngle)
            {
                nNode.Operator.Add(tkns[idx]);
                return(ParseShiftExpr(nNode, tkns, idx + 1));
            }
            else if (tkns[idx].TokenType == TokenType.ConditionalOperator && ops_1.Contains(tkns[idx].TokenValue))
            {
                nNode.Operator.Add(tkns[idx]);
                return(ParseShiftExpr(nNode, tkns, idx + 1));
            }
            else if (tkns[idx].TokenType == TokenType.Keyword && ops_2.Contains(tkns[idx].TokenValue))
            {
                throw new SyntaxException("is/as operators not implemented yet.", tkns[idx]);
            }
            else
            {
                return(idx);
            }
        }
        private static int ParseEqualityExpr(SyntaxNode parent, Token[] tkns, int idx)
        {
            var nNode = new OperatorSyntaxNode(SyntaxNodeType.EqualityNode, tkns[idx]);

            parent.ChildNodes.Add(nNode);
            idx = ParseRelationTypeTestingExpr(nNode, tkns, idx);

            string[] ops = new string[] { "==", "!=" };

            if (tkns[idx].TokenType == TokenType.ConditionalOperator && ops.Contains(tkns[idx].TokenValue))
            {
                nNode.Operator.Add(tkns[idx]);
                return(ParseRelationTypeTestingExpr(nNode, tkns, idx + 1));
            }
            else
            {
                return(idx);
            }
        }
        private static int ParseCondOrExpr(SyntaxNode parent, Token[] tkns, int idx)
        {
            var nNode = new OperatorSyntaxNode(SyntaxNodeType.CondOrNode, tkns[idx]);

            parent.ChildNodes.Add(nNode);
            idx = ParseCondAndExpr(nNode, tkns, idx);

            if (tkns[idx].TokenType == TokenType.Operator && tkns[idx].TokenValue == "||")
            {
                while (tkns[idx].TokenType == TokenType.Operator && tkns[idx].TokenValue == "||")
                {
                    nNode.Operator.Add(tkns[idx]);
                    idx = ParseCondAndExpr(nNode, tkns, idx + 1);
                }
                return(idx);
            }
            else
            {
                return(idx);
            }
        }
        //NOTE: Sorted by lowest to highest precedence
        private static int ParseAssignAndLambdaExpr(SyntaxNode parent, Token[] tkns, int idx)
        {
            var nNode = new OperatorSyntaxNode(SyntaxNodeType.AssignAndLambdaNode, tkns[idx]);

            parent.ChildNodes.Add(nNode);
            idx = ParseConditionalExpr(nNode, tkns, idx);

            if (tkns[idx].TokenType == TokenType.Operator && tkns[idx].TokenValue == "=>")
            {
                nNode.Operator.Add(tkns[idx]);
                return(ParseCodeBlock(nNode, tkns, idx + 1));
            }
            else if (tkns[idx].TokenType == TokenType.AssignmentOperator)
            {
                nNode.Operator.Add(tkns[idx]);
                return(ParseConditionalExpr(nNode, tkns, idx + 1));
            }
            else
            {
                return(idx);
            }
        }
        private static int ParseMultiplicativeExpr(SyntaxNode parent, Token[] tkns, int idx)
        {
            var nNode = new OperatorSyntaxNode(SyntaxNodeType.MultiplicativeNode, tkns[idx]);

            parent.ChildNodes.Add(nNode);
            idx = ParseUnaryExpr(nNode, tkns, idx);

            string[] ops = new string[] { "*", "/", "%" };

            if (tkns[idx].TokenType == TokenType.Operator && ops.Contains(tkns[idx].TokenValue))
            {
                while (tkns[idx].TokenType == TokenType.Operator && ops.Contains(tkns[idx].TokenValue))
                {
                    nNode.Operator.Add(tkns[idx]);
                    idx = ParseUnaryExpr(nNode, tkns, idx + 1);
                }
                return(idx);
            }
            else
            {
                return(idx);
            }
        }
        private static int ParseShiftExpr(SyntaxNode parent, Token[] tkns, int idx)
        {
            var nNode = new OperatorSyntaxNode(SyntaxNodeType.ShiftNode, tkns[idx]);

            parent.ChildNodes.Add(nNode);
            idx = ParseAdditiveExpr(nNode, tkns, idx);

            string[] ops = new string[] { "<<", ">>" };

            if (tkns[idx].TokenType == TokenType.Operator && ops.Contains(tkns[idx].TokenValue))
            {
                while (tkns[idx].TokenType == TokenType.Operator && ops.Contains(tkns[idx].TokenValue))
                {
                    nNode.Operator.Add(tkns[idx]);
                    idx = ParseAdditiveExpr(nNode, tkns, idx + 1);
                }
                return(idx);
            }
            else
            {
                return(idx);
            }
        }
        private static int ParsePrimaryExpr(SyntaxNode parent, Token[] tkns, int idx)
        {
            var nNode = new OperatorSyntaxNode(SyntaxNodeType.PrimaryNode, tkns[idx]);

            parent.ChildNodes.Add(nNode);

            //SUB_EXPRESSION (ASSIGNMENT ASSIGNMENT_EXPRESSION)*;
            //ASSIGNMENT_EXPRESSION
            //CONDITIONAL
            string[] primary_ops = new string[] { "true", "false", "null", "typeof", "sizeof", "nameof" };

            if (tkns[idx].TokenType == TokenType.StringLiteral | tkns[idx].TokenType == TokenType.CharLiteral | tkns[idx].TokenType == TokenType.IntegerLiteral | tkns[idx].TokenType == TokenType.HexLiteral | tkns[idx].TokenType == TokenType.BinaryLiteral | tkns[idx].TokenType == TokenType.FloatLiteral)
            {
                //STRING | INTEGER | CHAR | HEX | BINARY
                nNode.ChildNodes.Add(new SyntaxNode(SyntaxNodeType.ConstantNode, tkns[idx]));
                return(idx + 1);
            }
            else if (tkns[idx].TokenType == TokenType.OpeningParen)
            {
                //(EXPRESSION)
                var nNode2 = new SyntaxNode(SyntaxNodeType.NestedExpression, tkns[idx]);
                nNode.ChildNodes.Add(nNode2);
                idx = ParseExpression(nNode2, tkns, idx + 1);
                if (tkns[idx].TokenType != TokenType.ClosingParen && tkns[idx].TokenType != TokenType.Semicolon)
                {
                    throw new SyntaxException("Expected closing parenthesis.", tkns[idx]);
                }

                if (tkns[idx].TokenType == TokenType.Semicolon)
                {
                    return(idx);
                }
                return(idx + 1);
            }
            else if (tkns[idx].TokenType == TokenType.Keyword)
            {
                switch (tkns[idx].TokenValue)
                {
                //true | false | null
                case "true":
                case "false":
                case "null":
                    nNode.ChildNodes.Add(new SyntaxNode(SyntaxNodeType.ConstantNode, tkns[idx]));
                    break;

                case "checked":
                case "unchecked":
                case "typeof":
                case "default":
                case "sizeof":
                case "nameof":
                {
                    var nNode2 = new SyntaxNode(SyntaxNodeType.SpecialStatement, tkns[idx]);
                    nNode.ChildNodes.Add(nNode2);

                    if (tkns[idx + 1].TokenType != TokenType.OpeningParen)
                    {
                        throw new SyntaxException("Expected opening parenthesis.", tkns[idx + 1]);
                    }

                    idx = ParseExpression(nNode2, tkns, idx + 2);

                    if (tkns[idx].TokenType != TokenType.ClosingParen)
                    {
                        throw new SyntaxException("Expected closing parenthesis.", tkns[idx]);
                    }
                    return(idx + 1);
                }

                case "new":
                {
                    var nNode2 = new SyntaxNode(SyntaxNodeType.SpecialStatement, tkns[idx]);
                    nNode.ChildNodes.Add(nNode2);

                    idx = ParseType(nNode2, tkns, idx + 1);
                    return(idx);
                }
                break;

                case "delegate":
                    break;

                case "bool":
                case "byte":
                case "char":
                case "decimal":
                case "double":
                case "float":
                case "int":
                case "long":
                case "sbyte":
                case "uint":
                case "ulong":
                case "ushort":
                    return(ParseType(nNode, tkns, idx));

                    break;

                default:
                    throw new SyntaxException("Unexpected keyword.", tkns[idx]);
                }
                return(idx + 1);
            }
            else if (tkns[idx].TokenType == TokenType.Identifier)
            {
                return(ParseType(nNode, tkns, idx));
            }
            else if (tkns[idx].TokenType == TokenType.Semicolon)
            {
                throw new Exception("Unknown.");
            }
            else if (tkns[idx].TokenType == TokenType.OpeningBracket)   //Array access
            {
                SyntaxNode nNode2 = null;
                while (tkns[idx].TokenType == TokenType.OpeningBracket)
                {
                    nNode2 = new SyntaxNode(SyntaxNodeType.IndexerAccess, tkns[idx]);
                    nNode.ChildNodes.Add(nNode2);

                    if (tkns[idx + 1].TokenType != TokenType.ClosingBracket)
                    {
                        idx = ParseExpression(nNode2, tkns, idx + 1);
                    }
                    else
                    {
                        idx++;
                    }

                    if (tkns[idx].TokenType != TokenType.ClosingBracket)
                    {
                        throw new SyntaxException("Expected closing bracket.", tkns[idx]);
                    }
                    idx++;
                }
                return(idx);
            }
            else
            {
                return(ParseExpression(nNode, tkns, idx));
            }
        }