public static List <ScriptToken> GetStatementTokens(List <ScriptToken> tokens, bool consumeSemicolon = true)
        {
            List <ScriptToken> statementTokens = new List <ScriptToken>();

            for (int i = 0; i < tokens.Count; i++)
            {
                ScriptToken scriptToken = tokens[i];

                if (scriptToken.Type == EScriptTokenType.SEMI_COLON)
                {
                    if (consumeSemicolon)
                    {
                        tokens.RemoveAt(0); // Consume semicolon
                    }
                    break;
                }

                statementTokens.Add(scriptToken);
                tokens.RemoveAt(0); // Consume part of statement
                i--;
            }
            return(statementTokens);
        }
        public static List <List <ScriptToken> > GetBlockTokens(List <ScriptToken> tokens, EBlockType blockType = EBlockType.PAREN, bool groupByComma = true)
        {
            EScriptTokenType open        = EScriptTokenType.L_PAREN;
            EScriptTokenType close       = EScriptTokenType.R_PAREN;
            string           closeSymbol = ")";

            switch (blockType)
            {
            case EBlockType.BRACE:
                open        = EScriptTokenType.L_BRACE;
                close       = EScriptTokenType.R_BRACE;
                closeSymbol = "}";
                break;

            case EBlockType.BRACKET:
                open        = EScriptTokenType.L_BRACKET;
                close       = EScriptTokenType.R_BRACKET;
                closeSymbol = "]";
                break;
            }

            int openBlocks = 0;
            List <List <ScriptToken> > args = new List <List <ScriptToken> >();
            List <ScriptToken>         arg  = new List <ScriptToken>();

            for (int i = 0; i < tokens.Count; i++)
            {
                ScriptToken scriptToken = tokens[i];
                if (scriptToken.Type == open)
                {
                    openBlocks += 1;
                    if (openBlocks > 1)
                    {
                        arg.Add(scriptToken);
                    }
                }
                else if (scriptToken.Type == close)
                {
                    openBlocks -= 1;
                    if (openBlocks > 0)
                    {
                        arg.Add(scriptToken);
                    }
                }
                else if (groupByComma && scriptToken.Type == EScriptTokenType.COMMA && openBlocks == 1)
                {
                    args.Add(arg);
                    arg = new List <ScriptToken>();
                }
                else if (scriptToken.Type != EScriptTokenType.WHITESPACE)
                {
                    arg.Add(scriptToken);
                }

                // Consume token
                tokens.RemoveAt(0);
                i--;
                if (openBlocks == 0)
                {
                    if (arg.Count > 0)
                    {
                        args.Add(arg);
                    }

                    return(args);
                }
            }
            Console.WriteLine($"Invalid Syntax, missing ${closeSymbol}");
            return(null);
        }
        public static BlockNode ProcessTokens(List <ScriptToken> tokens)
        {
            List <AstTreeNode> blockNodes = new List <AstTreeNode>();
            AstTreeNode        node       = new BlockNode(null);
            int count = 0;

            StripWhiteSpace(tokens);

            while (tokens.Count > 0)
            {
                count++;
                if (count > 1000)
                {
                    break;               // Limit to 1000 iterations while in development
                }
                if (tokens[0].Type == EScriptTokenType.RETURN)
                {
                    tokens.RemoveAt(0); // Last value in block is returned by default
                }
                ScriptToken scriptToken = tokens[0];

                if (scriptToken.Type == EScriptTokenType.NAME)
                {
                    node = new RootScopeMemberNode(
                        new LiteralNode <string>(scriptToken.Value)
                        );
                    tokens.RemoveAt(0);
                }
                else if (scriptToken.Type == EScriptTokenType.ASSIGN)
                {
                    node = AssignmentNode.Parse(node, scriptToken, tokens);
                }
                else if (scriptToken.Type == EScriptTokenType.IF)
                {
                    node = IfStatementNode.Parse(node, scriptToken, tokens);
                    blockNodes.Add(node);
                    node = null;
                }
                else if (scriptToken.Type == EScriptTokenType.FOR)
                {
                    node = ForStatementNode.Parse(node, scriptToken, tokens);
                    blockNodes.Add(node);
                    node = null;
                }
                else if (scriptToken.Type == EScriptTokenType.STRING_LITERAL)
                {
                    node = new LiteralNode <string>(scriptToken.Value);
                    tokens.RemoveAt(0);
                }
                else if (scriptToken.Type == EScriptTokenType.NUMBER_LITERAL)
                {
                    AstTreeNode _node;
                    if (scriptToken.Value.Contains("."))
                    {
                        _node = new FloatLiteralNode(scriptToken.Value);
                    }
                    else
                    {
                        _node = new IntegerLiteralNode(scriptToken.Value);
                    }

                    node = _node;
                    tokens.RemoveAt(0);
                }
                else if (scriptToken.Type == EScriptTokenType.PERIOD)
                {
                    if (tokens[1].Type == EScriptTokenType.NAME)
                    {
                        node = new ScopeMemberNode(
                            node,
                            new LiteralNode <string>(tokens[1].Value)
                            );
                        tokens.RemoveAt(0);
                        tokens.RemoveAt(0);
                    }
                }
                else if (scriptToken.Type == EScriptTokenType.L_BRACKET)
                {
                    if (node.GetType() == typeof(RootScopeMemberNode))
                    {
                        // indice
                    }
                    else
                    {
                        node = ArrayNode.Parse(node, scriptToken, tokens);
                    }
                }
                else if (scriptToken.Type == EScriptTokenType.L_PAREN)
                {
                    List <List <ScriptToken> > funcArgs = GetBlockTokens(tokens);
                    List <AstTreeNode>         nodes    = new List <AstTreeNode>();
                    ;
                    foreach (List <ScriptToken> arg in funcArgs)
                    {
                        nodes.Add(ProcessTokens(arg));
                    }
                    node = new FunctionCallNode(
                        node, // Previous node should be a NAME
                        new FunctionArgumentNode(nodes)
                        );
                }
                else if (scriptToken.Type == EScriptTokenType.SEMI_COLON)
                {
                    if (node != null)
                    {
                        blockNodes.Add(node);
                    }

                    node = null;
                    tokens.RemoveAt(0);
                }
                else if (ComparisonNode.Matches(tokens))
                {
                    node = ComparisonNode.Parse(node, scriptToken, tokens);
                }
                else if (ArithmeticNode.Matches(tokens))
                {
                    AstTreeNode _node = ArithmeticNode.Parse(node, scriptToken, tokens);
                    node = _node;
                }
                else if (ArithmeticAssignmentNode.Matches(tokens))
                {
                    node = ArithmeticAssignmentNode.Parse(node, scriptToken, tokens);
                }
                else if (scriptToken.Type == EScriptTokenType.WHITESPACE)
                {
                    tokens.RemoveAt(0);
                }
                else if (scriptToken.Type == EScriptTokenType.BOOLEAN_LITERAL)
                {
                    node = new BooleanLiteralNode(tokens[0].Value);
                    tokens.RemoveAt(0);
                }
                else if (scriptToken.Type == EScriptTokenType.NULL_LITERAL)
                {
                    node = new LiteralNode <object>(null);
                    tokens.RemoveAt(0);
                }
                else
                {
                    string code = ScriptTree.ToCode(tokens, 10);
                    Console.WriteLine($"Syntax Error.Near {code}");
                }
            }

            if (node != null)
            {
                blockNodes.Add(node);
            }

            return(new BlockNode(blockNodes));
        }
        public static List <ScriptToken> GetEnclosedTokens(List <ScriptToken> tokens, bool consumeEnclosingTokens = true)
        {
            List <ScriptToken>      statementTokens = new List <ScriptToken>();
            List <EScriptTokenType> openingBlocks   = new List <EScriptTokenType>();

            openingBlocks.Add(EScriptTokenType.L_BRACKET);
            openingBlocks.Add(EScriptTokenType.L_BRACE);
            openingBlocks.Add(EScriptTokenType.L_PAREN);

            List <EScriptTokenType> closingBlocks = new List <EScriptTokenType>();

            closingBlocks.Add(EScriptTokenType.SEMI_COLON);
            closingBlocks.Add(EScriptTokenType.R_BRACKET);
            closingBlocks.Add(EScriptTokenType.R_BRACE);
            closingBlocks.Add(EScriptTokenType.R_PAREN);

            // Consume opening block
            bool             hadOpeningBlock  = false;
            EScriptTokenType openingBlockType = tokens[0].Type;
            EScriptTokenType closingBlockType = EScriptTokenType.R_PAREN;

            if (consumeEnclosingTokens && openingBlocks.Contains(tokens[0].Type))
            {
                tokens.RemoveAt(0);
                hadOpeningBlock = true;

                switch (openingBlockType)
                {
                case EScriptTokenType.L_BRACE:
                    closingBlockType = EScriptTokenType.R_BRACE;
                    break;

                case EScriptTokenType.L_BRACKET:
                    closingBlockType = EScriptTokenType.R_BRACKET;
                    break;
                }
            }

            int openParens = 0;

            for (int i = 0; i < tokens.Count; i++)
            {
                ScriptToken scriptToken = tokens[i];
                if (openingBlocks.Contains(scriptToken.Type))
                {
                    openParens++;
                }

                if (closingBlocks.Contains(scriptToken.Type))
                {
                    if (openParens > 0 && closingBlocks.Contains(scriptToken.Type))
                    {
                        openParens--;
                    }
                    else
                    {
                        break;
                    }
                }

                statementTokens.Add(scriptToken);
                tokens.RemoveAt(0); // Consume part of statement
                i--;
            }

            // Consume closing block
            if (hadOpeningBlock && consumeEnclosingTokens && closingBlocks.Contains(tokens[0].Type))
            {
                if (tokens[0].Type != closingBlockType)
                {
                    Console.WriteLine("Syntax error: Opening and closing symbols do not match.");
                }
                tokens.RemoveAt(0);
            }

            return(statementTokens);
        }