/// <summary>
        /// Быстрое проведение тестирования <see cref="Parser.ParserLang"/>.
        /// Удаляет жетоны с CH_.
        /// </summary>
        /// <param name="resource">Текст тестирования.</param>
        /// <param name="isSuccess">True, если ожидается успех парсирования.</param>
        /// <param name="tokens">Количество ожидаемых жетонов. Установите -1 для игнорирования.</param>
        /// <param name="parser">Особые правила парсера. Оставьте null, если нужен язык <see cref="parserLang"/>.</param>
        /// <param name="lexer">Особые правила лексера. Оставьте null, если нужен язык <see cref="lexerLang"/>.</param>
        public ReportParser CheckTest(string resource, bool isSuccess = true, int tokens = -1, ParserLang parser = null, LexerLang lexer = null)
        {
            if (parser == null)
            {
                parser = parserLang;
            }
            if (lexer == null)
            {
                lexer = lexerLang;
            }
            List <Token> listT = lexer.SearchTokens(StringToStream(resource));

            Assert.IsNotNull(listT);
            Console.WriteLine("Count tokens: " + listT.Count);
            foreach (Token token in listT)
            {
                Console.WriteLine(token);
            }
            Console.WriteLine("\n ---- Without CH_:");
            listT.RemoveAll((Token t) => t.Type.Name.Contains("CH_"));
            Console.WriteLine("Count tokens: " + listT.Count);
            foreach (Token token in listT)
            {
                Console.WriteLine(token);
            }
            ReportParser report = parser.Check(listT);

            Console.WriteLine(report);
            if (tokens != -1)
            {
                Assert.AreEqual(tokens, listT.Count);
            }
            Assert.AreEqual(isSuccess, report.IsSuccess);
            return(report);
        }
        public StackMachineTest()
        {
            EasyLexerLang = new LexerLang(new Terminal[] {
                new Terminal("ASSIGN_OP", "^=$"),
                new Terminal("VAR", "^[a-zA-Z]+$", uint.MaxValue),
                new Terminal("DIGIT", "^0|([1-9][0-9]*)$"),
                new Terminal("OP", "^\\+|-|\\*|/$"),
                new Terminal("WHILE_KW", "^while$", 0),
                new Terminal("PRINT_KW", "^print$", 0),
                new Terminal("L_QB", "^{$"),
                new Terminal("R_QB", "^}$"),
                new Terminal("L_B", "^\\($"),
                new Terminal("R_B", "^\\)$"),

                new Terminal("CH_SPACE", "^ $"),
                new Terminal("CH_LEFTLINE", "^\r$"),
                new Terminal("CH_NEWLINE", "^\n$"),
                new Terminal("CH_TAB", "^\t$")
            });

            /*
             * Правила стековой машины:
             * Все команды выполняются в постфиксной записи.
             *
             * {value logical, value addr, "if", com1}
             * Где:
             * logical - если 0, то переход к com1. Ложь - переход к адресу addr.
             * addr - адрес перехода.
             *
             * {value addr, "goto"}
             * Переход к адресу addr.
             *
             */

            Nonterminal lang = new Nonterminal("lang",
                                               (List <string> commands, ActionInsert insert, int id) =>
            {
                for (int i = 0; i < id; i++)
                {
                    insert(i);
                }
            }, ZERO_AND_MORE);
            Nonterminal value = new Nonterminal("value",
                                                (List <string> commands, ActionInsert insert, int id) =>
            {
                insert();
            }, OR, "VAR", "DIGIT");
            Nonterminal stmt = new Nonterminal("stmt",
                                               (List <string> commands, ActionInsert insert, int id) =>
            {
                insert(1);
                insert(0);
            }, AND,
                                               value, new Nonterminal("(OP value)*",
                                                                      (List <string> commands, ActionInsert insert, int id) =>
            {
                for (int i = 0; i < id; i++)
                {
                    insert(i);
                }
            }, ZERO_AND_MORE, new Nonterminal("OP & value",
                                              (List <string> commands, ActionInsert insert, int id) =>
            {
                insert(1);
                insert(0);
            }, AND, "OP", value)));
            Nonterminal assign_expr = new Nonterminal("assign_expr",
                                                      (List <string> commands, ActionInsert insert, int id) =>
            {
                insert(0);
                insert(2);
                insert(1);
            }, AND,
                                                      "VAR", "ASSIGN_OP", stmt);
            Nonterminal while_expr = new Nonterminal("while_expr",
                                                     // Нужно преобразовать в стековый код.
                                                     (List <string> commands, ActionInsert insert, int id) =>
            {
                insert(2);
                commands.Add("?");     // Адрес с истиной.
                int indexAddrTrue = commands.Count - 1;
                commands.Add("if");
                commands.Add("?");     // Адрес с ложью.
                int indexAddrFalse = commands.Count - 1;
                commands.Add("goto");
                // Сюда надо попасть, если true.
                commands[indexAddrTrue] = commands.Count.ToString();
                insert(5);            // Тело while.
                commands.Add(indexAddrTrue.ToString());
                commands.Add("goto"); // Команда перехода в if к while.
                // Надо выйти из цикла, если false:
                commands[indexAddrFalse] = commands.Count.ToString();
            }, AND,
                                                     "WHILE_KW", "L_B", stmt, "R_B", "L_QB", lang, "R_QB");
            Nonterminal expr = new Nonterminal("expr",
                                               // Нужно преобразовать в стековый код.
                                               (List <string> commands, ActionInsert insert, int id) =>
            {
                switch (id)
                {
                case 0:
                    {
                        insert();
                    }
                    break;

                case 1:
                    {
                        insert();
                    }
                    break;

                case 2:
                    {
                        commands.Add("print");
                    }
                    break;
                }
            }, OR,
                                               assign_expr, while_expr, "PRINT_KW");

            lang.Add(expr);
            EasyParserLang = new ParserLang(lang);

            SuperEasyParserLang = new ParserLang(new Nonterminal("easy lang",
                                                                 (List <string> commands, ActionInsert insert, int id) =>
            {
                insert(0);
                insert(2);
                insert(1);
            }, AND, "VAR", "ASSIGN_OP", "DIGIT"));

            EasyStackLang = new MyEasyStackLang();
        }