private static int readVarArgOnlyCmd(CmdType cmd, string scmd, ref List <Token> ts, ref List <byte> bytes, ref List <string> vars, int i, ref int argCount)
        {
            argCount = 0;
            while (!isKeyWordOrProcName(ts[i]) && !Syntax.IsTokenHasOperator(ts[i], Syntax.OP_END))
            {
                if (Syntax.IsEnumOperator(ts[i]) || ts[i].Type == TknType.NewLine)
                {
                    i++;
                    continue;
                }

                if (ts[i].Type != TknType.Word)
                {
                    new CompileException(inFile(ref ts, i), inLine(ref ts, i), ErrCode.InvalidArgument, $"'{ts[i].Text}' for {scmd}");
                }

                if (!vars.Contains(ts[i].Text))
                {
                    new CompileException(inFile(ref ts, i), inLine(ref ts, i), ErrCode.UndefinedVariable, ts[i].Text);
                }

                bytes.AddCmd(cmd, (uint)getVarID(ref vars, ts[i].Text));
                argCount++;
                i++;
            }
            return(i);
        }
 private static int seekOperator(ref List <Token> ts, int i, string op)
 {
     while (!Syntax.IsTokenHasOperator(ts[i], op))
     {
         i++;
     }
     return(i);
 }
 /// <summary> Proc, if, while... bodies! </summary>
 private static int readNextBody(ref List <Token> ts, ref List <byte> bytes, ref List <string> vars, int i)
 {
     i++;
     while (!Syntax.IsTokenHasOperator(ts[i], "}"))
     {
         i = readNextExpr(ref ts, ref bytes, ref vars, i);
     }
     return(i);
 }
        private static int readOutput(CmdType cmd_v, CmdType cmd_i, string scmd, ref List <Token> ts, ref List <byte> bytes, ref List <string> vars, int i)
        {
            List <byte> content = new List <byte>();

            while (!isKeyWordOrProcName(ts[i]) && !Syntax.IsTokenHasOperator(ts[i], Syntax.OP_END))
            {
                if (Syntax.IsEnumOperator(ts[i]))
                {
                    i++;
                    continue;
                }

                switch (ts[i].Type)
                {
                case TknType.Char: content.Add((byte)ts[i].Text[0]); break;

                case TknType.String:
                    for (int j = 0; j < ts[i].Text.Length; j++)
                    {
                        content.Add((byte)ts[i].Text[j]);
                    }
                    break;

                case TknType.Number: content.Add(toByte(ts[i].Text, inFile(ref ts, i), inLine(ref ts, i))); break;

                case TknType.Word:
                    for (int q = 0; q < content.Count; q += 3)
                    {
                        bytes.AddCmd(CmdType.OUT_3C, content.AtOrNull(q),
                                     content.AtOrNull(q + 1),
                                     content.AtOrNull(q + 2));
                    }
                    content.Clear();
                    //
                    if (!vars.Contains(ts[i].Text))
                    {
                        new CompileException(inFile(ref ts, i), inLine(ref ts, i), ErrCode.UndefinedVariable, ts[i].Text);
                    }
                    bytes.AddCmd(CmdType.OUT_B, (uint)getVarID(ref vars, ts[i].Text));
                    break;

                default: break;
                }
                i++;
            }

            for (int q = 0; q < content.Count; q += 3)
            {
                bytes.AddCmd(CmdType.OUT_3C, content.AtOrNull(q),
                             content.AtOrNull(q + 1),
                             content.AtOrNull(q + 2));
            }
            content.Clear();

            return(i);
        }
        private static int readWhile(ref List <Token> ts, ref List <byte> bytes, ref List <string> vars, int i)
        {
            //while [var | has | pop | const] { ... }
            //ARGUMENT:
            Token arg = ts[i];

            switch (arg.Type)
            {
            case TknType.Number: bytes.AddCmd(CmdType.WHILE_C, toByte(arg.Text, inFile(ref ts, i), inLine(ref ts, i))); break;

            case TknType.Char: bytes.AddCmd(CmdType.WHILE_C, (byte)arg.Text[0]); break;

            case TknType.Word:
                switch (arg.Text)
                {
                case Syntax.OP_HAS: bytes.AddCmd(CmdType.WHL_HAS); break;

                case Syntax.KW_POP: bytes.AddCmd(CmdType.WHL_POP); break;

                default:
                    if (!vars.Contains(arg.Text))
                    {
                        new CompileException(inFile(ref ts, i), inLine(ref ts, i), ErrCode.UndefinedVariable, arg.Text);
                    }

                    bytes.AddCmd(CmdType.WHILE_B, (uint)getVarID(ref vars, arg.Text));
                    break;
                }
                break;

            default: new CompileException(inFile(ref ts, i), inLine(ref ts, i), ErrCode.InvalidArgument, $"'{arg.Text}' for while"); break;
            }
            //SEEK BODY ('{'):
            i++;
            while (!Syntax.IsTokenHasOperator(ts[i], Syntax.OP_BEGIN))
            {
                if (ts[i].Type != TknType.NewLine)
                {
                    new CompileException(inFile(ref ts, i), inLine(ref ts, i), ErrCode.UndefinedBody, "while");
                }
                i++;
            }
            //READ BODY:
            i = readNextBody(ref ts, ref bytes, ref vars, i) + 1;
            //END:
            bytes.AddCmd(CmdType.WHL_END);

            return(i);
        }
        private static int readCmd(CmdType cmd_v, CmdType cmd_i, string scmd, ref List <Token> ts, ref List <byte> bytes, ref List <string> vars, int i)
        {
            while (!isKeyWordOrProcName(ts[i]) && !Syntax.IsTokenHasOperator(ts[i], Syntax.OP_END))
            {
                if (Syntax.IsEnumOperator(ts[i]))
                {
                    i++;
                    continue;
                }

                switch (ts[i].Type)
                {
                case TknType.Char: bytes.AddCmd(cmd_v, (byte)ts[i].Text[0]); break;

                case TknType.String:
                    for (int j = 0; j < ts[i].Text.Length; j++)
                    {
                        bytes.AddCmd(cmd_v, (byte)ts[i].Text[j]);
                    }
                    break;

                case TknType.Number: bytes.AddCmd(cmd_v, toByte(ts[i].Text, inFile(ref ts, i), inLine(ref ts, i))); break;

                case TknType.Word:
                    if (!vars.Contains(ts[i].Text))
                    {
                        new CompileException(inFile(ref ts, i), inLine(ref ts, i), ErrCode.UndefinedVariable, ts[i].Text);
                    }
                    bytes.AddCmd(cmd_i, (uint)getVarID(ref vars, ts[i].Text));
                    break;

                default: break;
                }
                i++;
            }
            return(i);
        }
        /// <summary> Proc calls or keywords </summary>
        private static int readNextExpr(ref List <Token> ts, ref List <byte> bytes, ref List <string> vars, int i)
        {
            int argCount = 0;

            //[cmd] [ [ops | null] args | null]
            //Need command or newline:
            if (ts[i].Type == TknType.NewLine || Syntax.IsEnumOperator(ts[i]))
            {
                return(i + 1);
            }
            if (ts[i].Type != TknType.Word)
            {
                new CompileException(inFile(ref ts, i), inLine(ref ts, i), ErrCode.UndefinedCommand, ts[i].Text);
            }
            //
            string cmd = ts[i].Text;

            i++; //to args
            if (IsProcessName(cmd))
            {
                //push args:
                while (!isKeyWordOrProcName(ts[i]) && !Syntax.IsTokenHasOperator(ts[i], Syntax.OP_END))
                {
                    bytes.AddRange(pushArg(ts[i], ref vars, inFile(ref ts, i), inLine(ref ts, i)));
                    i++;
                }

                //call proc:
                bytes.AddCmd(CmdType.RUN_ID, (uint)getProcID(cmd));
            }
            else
            {
                switch (cmd)
                {
                //new var
                case Syntax.KW_BYTE:
                    while (!isKeyWordOrProcName(ts[i]))
                    {
                        if (Syntax.IsEnumOperator(ts[i]) || ts[i].Type == TknType.NewLine)
                        {
                            i++;
                            continue;
                        }

                        if (ts[i].Type != TknType.Word)
                        {
                            new CompileException(inFile(ref ts, i), inLine(ref ts, i), ErrCode.InvalidArgument, $"'{ts[i].Text}' for {cmd}");
                        }

                        if (!vars.Contains(ts[i].Text))
                        {
                            vars.Add(ts[i].Text);
                        }

                        bytes.AddCmd(CmdType.BYTE_I, (uint)getVarID(ref vars, ts[i].Text));
                        i++;
                    }
                    break;

                //var or value
                case Syntax.KW_ADD: i = readCmd(CmdType.ADD_V, CmdType.ADD_I, cmd, ref ts, ref bytes, ref vars, i); break;

                case Syntax.KW_DIVIDE: i = readCmd(CmdType.DIV_V, CmdType.DIV_I, cmd, ref ts, ref bytes, ref vars, i); break;

                case Syntax.KW_IS: i = readCmd(CmdType.IS_V, CmdType.IS_I, cmd, ref ts, ref bytes, ref vars, i); break;

                case Syntax.KW_MODULE: i = readCmd(CmdType.MOD_C, CmdType.MOD_B, cmd, ref ts, ref bytes, ref vars, i); break;

                case Syntax.KW_MULTIPLY: i = readCmd(CmdType.MUL_V, CmdType.MUL_I, cmd, ref ts, ref bytes, ref vars, i); break;

                case Syntax.KW_PUSH: i = readCmd(CmdType.PUSH_V, CmdType.PUSH_I, cmd, ref ts, ref bytes, ref vars, i); break;

                case Syntax.KW_SUBTRACT: i = readCmd(CmdType.SUB_V, CmdType.SUB_I, cmd, ref ts, ref bytes, ref vars, i); break;

                //roots
                case Syntax.KW_IF: i = readIf(ref ts, ref bytes, ref vars, i); break;

                case Syntax.KW_WHILE: i = readWhile(ref ts, ref bytes, ref vars, i); break;

                //var only
                case Syntax.KW_THIS: i = readVarArgOnlyCmd(CmdType.THIS_I, cmd, ref ts, ref bytes, ref vars, i, ref argCount); break;

                case Syntax.KW_POP:
                    i = readVarArgOnlyCmd(CmdType.POP_I, cmd, ref ts, ref bytes, ref vars, i, ref argCount);
                    if (argCount == 0)
                    {
                        bytes.AddCmd(CmdType.POP);
                    }
                    break;

                case Syntax.KW_PEEK: i = readVarArgOnlyCmd(CmdType.PEEK_B, cmd, ref ts, ref bytes, ref vars, i, ref argCount); break;

                case Syntax.KW_DECREMENT: i = readVarArgOnlyCmd(CmdType.DEC_B, cmd, ref ts, ref bytes, ref vars, i, ref argCount); break;

                case Syntax.KW_INCREMENT: i = readVarArgOnlyCmd(CmdType.INC_B, cmd, ref ts, ref bytes, ref vars, i, ref argCount); break;

                case Syntax.KW_NOT: i = readVarArgOnlyCmd(CmdType.NOT_B, cmd, ref ts, ref bytes, ref vars, i, ref argCount); break;

                case Syntax.KW_INPUT:
                    i = readVarArgOnlyCmd(CmdType.INPUT_B, cmd, ref ts, ref bytes, ref vars, i, ref argCount);
                    if (argCount == 0)
                    {
                        bytes.AddCmd(CmdType.INPUT);
                    }
                    break;

                //var or 3 value
                case Syntax.KW_OUTPUT: i = readOutput(CmdType.SUB_V, CmdType.SUB_I, cmd, ref ts, ref bytes, ref vars, i); break;

                //null
                case Syntax.KW_FLIP: bytes.AddCmd(CmdType.FLIP); break;

                case Syntax.KW_STOP: bytes.AddCmd(CmdType.PRC_STP); break;

                case Syntax.KW_OUTCLEAR: bytes.AddCmd(CmdType.CLR_OUT); break;

                case Syntax.KW_STACLEAR: bytes.AddCmd(CmdType.CLR_STK); break;

                case Syntax.KW_PUSH_THIS: bytes.AddCmd(CmdType.PUSH_T); break;

                case Syntax.KW_POP_THIS: bytes.AddCmd(CmdType.POP_T); break;

                //
                default: new CompileException(inFile(ref ts, i), inLine(ref ts, i), ErrCode.UndefinedCommand, cmd); break;
                }
            }

            Console.Write($"\r{(int)((float)i * 100.0 / ts.Count)}%");
            return(i);
        }