Пример #1
0
        private bool IsFlowOperator(string code)
        {
            const int offset     = 3;
            var       delimiters = new char[Spaces.Length + offset];

            delimiters[0] = Multicast;
            delimiters[1] = BlockOpen;
            delimiters[2] = ParenOpen;
            Spaces.CopyTo(delimiters, offset);
            string word = code.Split(delimiters, 2)[0].ToLowerInvariant();

            switch (word)
            {
            case FlowBreak:
            case FlowContinue:
            case FlowElse:
            case FlowGosub:
            case FlowGoto:
            case FlowIf:
            case FlowLoop:
            case FlowReturn:
            case FlowWhile:
            case FunctionLocal:
            case FunctionGlobal:
            case FunctionStatic:
                return(true);
            }

            return(false);
        }
Пример #2
0
#pragma warning restore 414

        #endregion Variables

        private void ParseDirective(string code)
        {
            if (code.Length < 2)
            {
                throw new ParseException(ExUnknownDirv);
            }

            var delim = new char[Spaces.Length + 1];

            delim[0] = Multicast;
            Spaces.CopyTo(delim, 1);
            string[] parts = code.Split(delim, 2);

            if (parts.Length != 2)
            {
                parts = new[] { parts[0], string.Empty }
            }
            ;

            parts[1] = StripComment(parts[1]).Trim(Spaces);

            int  value = default(int);
            bool numeric;

            string[] sub;

            if (parts[1].Length == 0)
            {
                numeric = false;
                sub     = new[] { string.Empty, string.Empty };
            }
            else
            {
                numeric = int.TryParse(parts[1], out value);
                string[] split = parts[1].Split(new[] { Multicast }, 2);
                sub = new[] { split[0].Trim(Spaces), split.Length > 1 ? split[1].Trim(Spaces) : string.Empty };
            }

            string       cmd = parts[0].Substring(1);
            const string res = "__IFWIN\0";

            switch (cmd.ToUpperInvariant())
            {
                #region Assembly manifest

            case "ASSEMBLYTITLE":
                if (!string.IsNullOrEmpty(parts[1]))
                {
                    AddAssemblyAttribute(typeof(AssemblyTitleAttribute), parts[1]);
                }
                break;

            case "ASSEMBLYDESCRIPTION":
                if (!string.IsNullOrEmpty(parts[1]))
                {
                    AddAssemblyAttribute(typeof(AssemblyDescriptionAttribute), parts[1]);
                }
                break;

            case "ASSEMBLYCONFIGURATION":
                if (!string.IsNullOrEmpty(parts[1]))
                {
                    AddAssemblyAttribute(typeof(AssemblyConfigurationAttribute), parts[1]);
                }
                break;

            case "ASSEMBLYCOMPANY":
                if (!string.IsNullOrEmpty(parts[1]))
                {
                    AddAssemblyAttribute(typeof(AssemblyCompanyAttribute), parts[1]);
                }
                break;

            case "ASSEMBLYPRODUCT":
                if (!string.IsNullOrEmpty(parts[1]))
                {
                    AddAssemblyAttribute(typeof(AssemblyProductAttribute), parts[1]);
                }
                break;

            case "ASSEMBLYCOPYRIGHT":
                if (!string.IsNullOrEmpty(parts[1]))
                {
                    AddAssemblyAttribute(typeof(AssemblyCopyrightAttribute), parts[1]);
                }
                break;

            case "ASSEMBLYTRADEMARK":
                if (!string.IsNullOrEmpty(parts[1]))
                {
                    AddAssemblyAttribute(typeof(AssemblyTrademarkAttribute), parts[1]);
                }
                break;

            case "ASSEMBLYCULTURE":
                if (!string.IsNullOrEmpty(parts[1]))
                {
                    AddAssemblyAttribute(typeof(AssemblyCultureAttribute), parts[1]);
                }
                break;

            case "ASSEMBLYVERSION":
                if (!string.IsNullOrEmpty(parts[1]))
                {
                    AddAssemblyAttribute(typeof(AssemblyVersionAttribute), parts[1]);
                }
                break;

            case "ASSEMBLYMERGE":
                if (CompilerParameters is IACompilerParameters)
                {
                    var options = (IACompilerParameters)CompilerParameters;
                    options.Merge = true;

                    switch (parts[1].ToUpperInvariant())
                    {
                    case "FORCE":
                        options.MergeFallbackToLink = false;
                        break;

                    case "OFF":
                        options.Merge = false;
                        break;
                    }
                }
                break;

                #endregion Assembly manifest

            case "CLIPBOARDTIMEOUT":
                ClipboardTimeout = numeric ? value : ClipboardTimeoutDefault;
                break;

            case "COMMENTFLAG":
                if (parts[1].Length == 2 && parts[1][0] == MultiComA && parts[1][1] == MultiComB)
                {
                    throw new ParseException(ExIllegalCommentFlag);
                }
                Comment = parts[1];
                break;

            case "DEREFCHAR":
                Resolve = parts[1][0];
                break;

            case "ESCAPECHAR":
                Escape = parts[1][0];
                break;

            case "DELIMITER":
                Multicast = parts[1][0];
                break;

            case "HOTSTRING":
                HotstringNewOptions = parts[1];
                break;

            case "IFWINACTIVE":
                IfWinActive_WinTitle = sub[0];
                IfWinActive_WinText  = sub[1];
                goto case res;

            case "IFWINEXIST":
                IfWinExist_WinTitle = sub[0];
                IfWinExist_WinText  = sub[1];
                goto case res;

            case "IFWINNOTACTIVE":
                IfWinNotExist_WinTitle = sub[0];
                IfWinNotActive_WinText = sub[1];
                goto case res;

            case "IFWINNOTEXIST":
                IfWinNotExist_WinTitle = sub[0];
                IfWinNotExist_WinText  = sub[1];
                goto case res;

            case res:
                var cond = (CodeMethodInvokeExpression)InternalMethods.Hotkey;
                cond.Parameters.Add(new CodePrimitiveExpression(cmd));
                cond.Parameters.Add(new CodePrimitiveExpression(sub[0]));
                cond.Parameters.Add(new CodePrimitiveExpression(sub[1]));
                prepend.Add(cond);
                break;

            case "LTRIM":
                switch (sub[0].ToUpperInvariant())
                {
                case "":
                case "ON":
                    LTrimForced = true;
                    break;

                case "OFF":
                    LTrimForced = false;
                    break;

                default:
                    throw new ParseException("Directive parameter must be either \"on\" or \"off\"");
                }
                break;

            default:
                throw new ParseException(ExUnknownDirv);
            }
        }
Пример #3
0
        CodeStatement[] ParseFlow(List <CodeLine> lines, int index)
        {
            #region Variables

            var      line  = lines[index];
            string   code  = line.Code.TrimStart(Spaces);
            string[] parts = { string.Empty, string.Empty };

            var delimiters = new char[Spaces.Length + 1];
            delimiters[0] = Multicast;
            Spaces.CopyTo(delimiters, 1);
            int[] d = { code.IndexOfAny(delimiters), code.IndexOfAny(new[] { BlockOpen, ParenOpen }) };

            if (d[0] == -1 && d[1] == -1)
            {
                parts[0] = code;
            }
            else if (d[1] != -1 && (d[1] < d[0] || d[0] == -1))
            {
                parts[0] = code.Substring(0, d[1]);
                parts[1] = code.Substring(d[1], code.Length - d[1]).TrimStart(Spaces);
            }
            else
            {
                parts[0] = code.Substring(0, d[0]);
                parts[1] = code.Substring(d[0] + 1, code.Length - d[0] - 1).TrimStart(Spaces);
            }

            if (parts.Length > 1 && IsEmptyStatement(parts[1]))
            {
                parts = new[] { parts[0] }
            }
            ;

            #endregion

            switch (parts[0].ToLowerInvariant())
            {
                #region If/Else

            case FlowIf:
            {
                if (parts.Length < 1)
                {
                    throw new ParseException("If requires a parameter");
                }

                bool           blockOpen = false;
                CodeExpression condition = ParseFlowParameter(parts[1], true, out blockOpen, false);
                var            ifelse    = new CodeConditionStatement {
                    Condition = condition
                };

                var block = new CodeBlock(line, Scope, ifelse.TrueStatements, CodeBlock.BlockKind.IfElse, blocks.Count == 0 ? null : blocks.Peek());
                block.Type = blockOpen ? CodeBlock.BlockType.Within : CodeBlock.BlockType.Expect;
                CloseTopSingleBlock();
                blocks.Push(block);

                elses.Push(ifelse.FalseStatements);
                return(new CodeStatement[] { ifelse });
            }

            case FlowElse:
            {
                if (elses.Count == 0)
                {
                    throw new ParseException("Else with no preceeding if block");
                }

                string next = line.Code.TrimStart(Spaces).Substring(FlowElse.Length).TrimStart(Spaces);

                if (!IsEmptyStatement(next))
                {
                    lines.Insert(index + 1, new CodeLine(lines[index].FileName, lines[index].LineNumber, next));
                }

                var type  = parts.Length > 1 && parts[1][0] == BlockOpen ? CodeBlock.BlockType.Within : CodeBlock.BlockType.Expect;
                var block = new CodeBlock(lines[index], Scope, elses.Pop(), CodeBlock.BlockKind.IfElse, blocks.Count == 0 ? null : blocks.Peek())
                {
                    Type = type
                };
                CloseTopSingleBlock();
                blocks.Push(block);
            }
            break;

                #endregion

                #region Goto

            case FlowGosub:
            {
                if (parts.Length < 1)
                {
                    throw new ParseException("No label specified");
                }
                return(new CodeStatement[] { new CodeExpressionStatement(LocalLabelInvoke(parts[1])) });
            }

            case FlowGoto:
            {
                if (parts.Length < 1)
                {
                    throw new ParseException("No label specified");
                }
                return(new CodeStatement[] { new CodeExpressionStatement(LocalLabelInvoke(parts[1])), new CodeMethodReturnStatement() });
            }

                #endregion

                #region Loops

            case FlowLoop:
            {
                bool blockOpen = false;
                CodeMethodInvokeExpression iterator;
                bool skip       = true;
                bool checkBrace = true;
                bool byref      = false;

                #region Loop types
                if (parts.Length > 1)
                {
                    string[] sub = parts[1].Split(new[] { Multicast }, 2);
                    sub = new[] { sub[0].Trim(), sub.Length > 1 ? sub[1].Trim() : string.Empty };

                    switch (sub[0].ToUpperInvariant())
                    {
                    case "READ":
                        byref    = true;
                        iterator = (CodeMethodInvokeExpression)InternalMethods.LoopRead;
                        break;

                    case "PARSE":
                        checkBrace = false;
                        byref      = true;
                        iterator   = (CodeMethodInvokeExpression)InternalMethods.LoopParse;
                        break;

                    case "HKEY_LOCAL_MACHINE":
                    case "HKLM":
                    case "HKEY_USERS":
                    case "HKU":
                    case "HKEY_CURRENT_USER":
                    case "HKCU":
                    case "HKEY_CLASSES_ROOT":
                    case "HKCR":
                    case "HKEY_CURRENT_CONFIG":
                    case "HKCC":
                        iterator = (CodeMethodInvokeExpression)InternalMethods.LoopRegistry;
                        break;

                    case "EACH":
                        byref    = true;
                        iterator = (CodeMethodInvokeExpression)InternalMethods.LoopEach;
                        break;

                    default:
                    {
                        var file = false;

                        if (parts[1].IndexOf(Multicast) != -1)
                        {
                            file = true;
                        }

                        // TODO: check file/iteration loop types

                        skip     = false;
                        iterator = (CodeMethodInvokeExpression)(file ? InternalMethods.LoopFile : InternalMethods.Loop);
                    }
                    break;
                    }

                    if (skip)
                    {
                        parts[1] = sub[1];
                    }

                    if (checkBrace)
                    {
                        // TODO: check expression parameters before stripping comments
                        int    x    = parts.Length == 1 ? 0 : 1;
                        string part = StripComment(parts[x]).TrimEnd(Spaces);
                        int    l    = part.Length - 1;
                        if (part.Length > 0 && part[l] == BlockOpen)
                        {
                            blockOpen = true;
                            parts[x]  = part.Substring(0, l);
                        }
                    }

                    if (skip && parts[1].Length == 0)
                    {
                        throw new ParseException("Loop type must have an argument");
                    }

                    foreach (var arg in SplitCommandParameters(parts[1]))
                    {
                        iterator.Parameters.Add(ParseCommandParameter(arg));
                    }

                    if (LegacyLoop && byref)
                    {
                        iterator.Parameters[0] = VarId(iterator.Parameters[0]);
                    }
                }
                else
                {
                    iterator = (CodeMethodInvokeExpression)InternalMethods.Loop;
                    iterator.Parameters.Add(new CodePrimitiveExpression(int.MaxValue));
                }
                #endregion

                string id = InternalID;

                var init = new CodeVariableDeclarationStatement();
                init.Name           = id;
                init.Type           = new CodeTypeReference(typeof(IEnumerable));
                init.InitExpression = new CodeMethodInvokeExpression(iterator, "GetEnumerator", new CodeExpression[] { });

                var condition = new CodeMethodInvokeExpression();
                condition.Method.TargetObject = new CodeVariableReferenceExpression(id);
                condition.Method.MethodName   = "MoveNext";

                var loop = new CodeIterationStatement();
                loop.InitStatement      = init;
                loop.IncrementStatement = new CodeCommentStatement(string.Empty);         // for C# display
                loop.TestExpression     = condition;

                var block = new CodeBlock(line, Scope, loop.Statements, CodeBlock.BlockKind.Loop, blocks.Count == 0 ? null : blocks.Peek(), InternalID, InternalID);
                block.Type = blockOpen ? CodeBlock.BlockType.Within : CodeBlock.BlockType.Expect;
                CloseTopSingleBlock();
                blocks.Push(block);

                return(new CodeStatement[] { loop, new CodeLabeledStatement(block.ExitLabel) });
            }

            case FlowWhile:
            {
                bool           blockOpen = false;
                CodeExpression condition = parts.Length > 1 ? ParseFlowParameter(parts[1], true, out blockOpen, true) : new CodePrimitiveExpression(true);
                var            loop      = new CodeIterationStatement();
                loop.TestExpression = condition;
                loop.InitStatement  = new CodeCommentStatement(string.Empty);

                var block = new CodeBlock(line, Scope, loop.Statements, CodeBlock.BlockKind.Loop, blocks.Count == 0 ? null : blocks.Peek(), InternalID, InternalID);
                block.Type = blockOpen ? CodeBlock.BlockType.Within : CodeBlock.BlockType.Expect;
                CloseTopSingleBlock();
                blocks.Push(block);

                return(new CodeStatement[] { loop, new CodeLabeledStatement(block.ExitLabel) });
            }

            case FlowBreak:
                int b = 1;
                if (parts.Length > 1)
                {
                    parts[1] = StripCommentSingle(parts[1]);
                    if (!int.TryParse(parts[1], out b) || b < 1)
                    {
                        throw new ParseException("Break parameter must be a static integer greater than zero.");
                    }
                }
                string exit = PeekLoopLabel(true, b);
                if (exit == null)
                {
                    throw new ParseException("Cannot break outside a loop");
                }
                return(new CodeStatement[] { new CodeGotoStatement(exit) });

            case FlowContinue:
                int c = 1;
                if (parts.Length > 1)
                {
                    parts[1] = StripCommentSingle(parts[1]);
                    if (!int.TryParse(parts[1], out c) || c < 1)
                    {
                        throw new ParseException("Continue parameter must be a static integer greater than zero.");
                    }
                }
                string cont = PeekLoopLabel(false, c);
                if (cont == null)
                {
                    throw new ParseException("Cannot continue outside a loop");
                }
                return(new CodeStatement[] { new CodeGotoStatement(cont) });

                #endregion

                #region Return

            case FlowReturn:
                if (Scope == mainScope)
                {
                    if (parts.Length > 1)
                    {
                        throw new ParseException("Cannot have return parameter for entry point method");
                    }
                    return(new CodeStatement[] { new CodeMethodReturnStatement() });
                }
                else
                {
                    var result = parts.Length > 1 ? ParseSingleExpression(parts[1]) : new CodePrimitiveExpression(null);
                    return(new CodeStatement[] { new CodeMethodReturnStatement(result) });
                }

                #endregion

                #region Function

            case FunctionLocal:
            case FunctionGlobal:
            case FunctionStatic:
                // TODO: function local/global/static scoping modifiers
                break;

                #endregion

            default:
                throw new ParseException(ExUnexpected);
            }

            return(null);
        }

        #region Parameters

        CodeExpression ParseFlowParameter(string code, bool inequality, out bool blockOpen, bool expr)
        {
            blockOpen = false;
            code      = code.Trim(Spaces);
            if (code.Length == 0)
            {
                return(new CodePrimitiveExpression(false));
            }
            if (LaxExpressions && IsLegacyIf(code))
            {
                return(ParseLegacyIf(code));
            }
            else if (expr || IsExpressionIf(code))
            {
                code = StripComment(code).TrimEnd(Spaces);
                int l = code.Length - 1;
                if (code.Length > 0 && code[l] == BlockOpen)
                {
                    blockOpen = true;
                    code      = code.Substring(0, l);
                }

                this.blockOpen = false;
                var result = ParseSingleExpression(code);
                blockOpen = blockOpen || this.blockOpen;

                var iftest = (CodeMethodInvokeExpression)InternalMethods.IfElse;
                iftest.Parameters.Add(result);
                return(iftest);
            }
            else if (LegacyIf)
            {
                code = StripCommentSingle(code);

                if (inequality)
                {
                    return(ParseInequality(code));
                }

                object result;
                if (IsPrimativeObject(code, out result))
                {
                    return(new CodePrimitiveExpression(result));
                }
                else
                {
                    throw new ParseException(ExUnexpected);
                }
            }
            else
            {
                throw new ParseException("Invalid arguments for if statement");
            }
        }

        CodeExpression ParseInequality(string code)
        {
            var buf = new StringBuilder(code.Length);
            int i   = 0;

            while (i < code.Length && IsSpace(code[i]))
            {
                i++;
            }

            while (i < code.Length && (IsIdentifier(code[i]) || code[i] == Resolve))
            {
                buf.Append(code[i++]);
            }

            while (i < code.Length && IsSpace(code[i]))
            {
                i++;
            }

            if (i != code.Length) // if test argument is not a lone identifier then it is an expression
            {
                var op = new[] { Equal, Not, Greater, Less };

                if (Array.IndexOf(op, code[i]) == -1)
                {
                    throw new ParseException(ExUnexpected);
                }

                buf.Append(code[i++]);

                if (i < code.Length && Array.IndexOf(op, code[i]) != -1)
                {
                    buf.Append(code[i++]);
                }

                buf.Append(StringBound);

                while (i < code.Length && IsSpace(code[i]))
                {
                    i++;
                }

                if (i < code.Length)
                {
                    string str = code.Substring(i);
                    str = str.Replace(StringBound.ToString(), new string(StringBound, 2));
                    buf.Append(str);
                }

                while (i < code.Length && IsSpace(code[i]))
                {
                    i++;
                }

                buf.Append(StringBound);
            }

            var iftest = (CodeMethodInvokeExpression)InternalMethods.IfElse;
            var expr   = ParseSingleExpression(buf.ToString());

            iftest.Parameters.Add(expr);
            return(iftest);
        }

        CodeExpression ParseLegacyIf(string code)
        {
            string[] parts = code.TrimStart(Spaces).Split(Spaces, 3);

            if (parts.Length != 3)
            {
                throw new ArgumentOutOfRangeException();
            }

            if (!IsIdentifier(parts[0]))
            {
                throw new ArgumentException();
            }

            bool not = false;

            if (parts[1].Equals(NotTxt, StringComparison.OrdinalIgnoreCase))
            {
                not = false;
                string[] sub = parts[2].Split(Spaces, 2);
                parts[1] = sub[0];
                parts[2] = sub[1];
            }

            var invoke = (CodeMethodInvokeExpression)InternalMethods.IfLegacy;

            invoke.Parameters.Add(VarId(parts[0]));
            parts[1] = parts[1].ToLowerInvariant();

            switch (parts[1])
            {
            case BetweenTxt:
            case InTxt:
            case ContainsTxt:
            case IsTxt:
                invoke.Parameters.Add(new CodePrimitiveExpression(parts[1]));
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            invoke.Parameters.Add(ParseCommandParameter(parts[2]));

            if (not)
            {
                var flip = (CodeMethodInvokeExpression)InternalMethods.OperateUnary;
                flip.Parameters.Add(OperatorAsFieldReference(Script.Operator.BitwiseNot));
                flip.Parameters.Add(invoke);
                invoke = flip;
            }

            return(invoke);
        }

        #endregion
    }
Пример #4
0
        List <CodeLine> Read(TextReader source, string name)
        {
            #region Properties

            var    list = new List <CodeLine>();
            string code;
            int    line = 0;

            var    includes    = new List <string>();
            string includePath = string.Empty;

            name = Path.GetFullPath(name);

            #endregion

            while ((code = source.ReadLine()) != null)
            {
                #region Line

                line++;

                if (line == 1 && code.Length > 2 && code[0] == '#' && code[1] == '!')
                {
                    continue;
                }

                string codeTrim = code.TrimStart(Spaces);

                #endregion

                #region Multiline comments

                if (codeTrim.Length > 1 && codeTrim[0] == MultiComA && codeTrim[1] == MultiComB)
                {
                    while ((code = source.ReadLine()) != null)
                    {
                        line++;
                        codeTrim = code.TrimStart(Spaces);
                        if (codeTrim.Length > 1 && codeTrim[0] == MultiComB && codeTrim[1] == MultiComA)
                        {
                            code = codeTrim = codeTrim.Substring(2);
                            break;
                        }
                    }
                    if (code == null)
                    {
                        continue;
                    }
                }

                #endregion

                #region Directives

                if (codeTrim.Length > 1 && codeTrim[0] == Directive)
                {
                    if (codeTrim.Length < 2)
                    {
                        throw new ParseException(ExUnknownDirv, line);
                    }

                    var delim = new char[Spaces.Length + 1];
                    delim[0] = Multicast;
                    Spaces.CopyTo(delim, 1);
                    string[] sub   = codeTrim.Split(delim, 2);
                    var      parts = new[] { sub[0], sub.Length > 1 ? sub[1] : string.Empty };

                    parts[1] = StripComment(parts[1]).Trim(Spaces);

                    int value;
                    int.TryParse(parts[1], out value);

                    bool next        = true;
                    bool includeOnce = false;

                    switch (parts[0].Substring(1).ToUpperInvariant())
                    {
                    case "INCLUDE":
                        includeOnce = true;
                        goto case "INCLUDEAGAIN";

                    case "INCLUDEAGAIN":
                    {
                        var replace = new[, ]
                        {
                            { "A_ScriptDir", Path.GetDirectoryName(name) },
                            { "A_AppData", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) },
                            { "A_AppDataCommon", Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) },
                            { "ProgramFiles", Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) },
                        };

                        for (int i = 0; i < replace.Length / 2; i++)
                        {
                            parts[1] = Replace(parts[1], string.Format("{0}{1}{0}", Resolve, replace[i, 0]), replace[i, 1]);
                        }

                        bool silent = false;

                        if (parts[1].Length > 3 && parts[1][0] == '*' && (parts[1][1] == 'i' || parts[1][1] == 'I') && IsSpace(parts[1][2]))
                        {
                            parts[1] = parts[1].Substring(3);
                            silent   = true;
                        }

                        string path = parts[1];

                        if (!Path.IsPathRooted(path) && Directory.Exists(includePath))
                        {
                            path = Path.Combine(includePath, path);
                        }
                        else if (!Path.IsPathRooted(path))
                        {
                            path = Path.Combine(Path.GetDirectoryName(name), path);
                        }

                        path = Path.GetFullPath(path);

                        if (!File.Exists(path))
                        {
                            if (!silent)
                            {
                                throw new ParseException(ExIncludeNotFound, line);
                            }
                            break;
                        }

                        if (includeOnce && includes.Contains(path))
                        {
                            break;
                        }

                        var newlist = Read(new StreamReader(path), path);
                        list.AddRange(newlist);

                        if (!includes.Contains(path))
                        {
                            includes.Add(path);
                        }
                    }
                    break;

                    case "NODYNAMICVARS":
                        DynamicVars = false;
                        break;

                    case "NOENV":
                        NoEnv = true;
                        break;

                    case "NOTRAYICON":
                        NoTrayIcon = true;
                        break;

                    case "PERSISTENT":
                        Persistent = true;
                        break;

                    case "SINGLEINSTANCE":
                        switch (parts[1].ToUpperInvariant())
                        {
                        case "FORCE":
                            SingleInstance = true;
                            break;

                        case "IGNORE":
                            SingleInstance = null;
                            break;

                        case "OFF":
                            SingleInstance = false;
                            break;

                        default:
                            break;
                        }
                        break;

                    case "WINACTIVATEFORCE":
                        WinActivateForce = true;
                        break;

                    case "HOTSTRING":
                        switch (parts[1].ToUpperInvariant())
                        {
                        case "NOMOUSE":
                            HotstringNoMouse = true;
                            break;

                        case "ENDCHARS":
                            HotstringEndChars = parts[1];
                            break;

                        default:
                            next = false;
                            break;
                        }
                        break;

                    case "ALLOWSAMELINECOMMENTS":
                    case "ERRORSTDOUT":
                    case "HOTKEYINTERVAL":
                    case "HOTKEYMODIFIERTIMEOUT":
                    case "INSTALLKEYBDHOOK":
                    case "INSTALLMOUSEHOOK":
                    case "KEYHISTORY":
                    case "MAXHOTKEYSPERINTERVAL":
                    case "MAXMEM":
                    case "MAXTHREADS":
                    case "MAXTHREADSBUFFER":
                    case "MAXTHREADSPERHOTKEY":
                    case "USEHOOK":
                        // deprecated directives
                        break;

                    default:
                        next = false;
                        break;
                    }

                    if (next)
                    {
                        continue;
                    }
                }

                #endregion

                #region Mulitline strings

                if (codeTrim.Length > 0 && codeTrim[0] == ParenOpen)
                {
                    if (list.Count == 0)
                    {
                        throw new ParseException(ExUnexpected, line);
                    }

                    var buf = new StringBuilder(code.Length);
                    buf.Append(code);
                    buf.Append(Environment.NewLine);

                    while ((code = source.ReadLine()) != null)
                    {
                        codeTrim = code.TrimStart(Spaces);

                        if (codeTrim.Length > 0 && codeTrim[0] == ParenClose)
                        {
                            code = codeTrim = codeTrim.Substring(1);
                            buf.Append(ParenClose);
                            break;
                        }
                        else
                        {
                            buf.Append(code);
                            buf.Append(Environment.NewLine);
                        }
                    }

                    string str    = buf.ToString();
                    string result = MultilineString(str);
                    list[list.Count - 1].Code += result + code;
                    continue;
                }

                #endregion

                #region Statement

                code = code.Trim(Spaces);

                if (code.StartsWith(new string(new[] { MultiComB, MultiComA })))
                {
                    code = code.Substring(2);
                }

                if (code.Length == 0 || IsCommentLine(code))
                {
                    continue;
                }

                if (IsContinuationLine(code))
                {
                    if (list.Count == 0)
                    {
                        throw new ParseException(ExUnexpected, line);
                    }

                    int i   = list.Count - 1;
                    var buf = new StringBuilder(list[i].Code, list[i].Code.Length + Environment.NewLine.Length + code.Length);
                    buf.Append(Environment.NewLine);
                    buf.Append(code);
                    list[i].Code = buf.ToString();
                }
                else
                {
                    Translate(ref code);

                    if (code.Length != 0)
                    {
                        list.Add(new CodeLine(name, line, code));
                    }
                }

                #endregion
            }

            return(list);
        }
Пример #5
0
        void Translate(ref string code)
        {
            #region Variables

            var delim = new char[Spaces.Length + 1];
            delim[0] = Multicast;
            Spaces.CopyTo(delim, 1);
            int    z = code.IndexOfAny(delim);
            string cmd, param;

            if (z == -1)
            {
                cmd   = code;
                param = string.Empty;
            }
            else
            {
                cmd   = code.Substring(0, z);
                param = code.Substring(z).TrimStart(delim);
            }

            var replaced = new StringBuilder(code.Length);

            #endregion

            #region Parameters

            string[] parts = SplitCommandParameters(param);

            if (parts.Length > 0)
            {
                parts[parts.Length - 1] = StripCommentSingle(parts[parts.Length - 1]);
            }

            for (int i = 0; i < parts.Length; i++)
            {
                if (IsExpressionParameter(parts[i]))
                {
                    int e = parts[i].IndexOf(Resolve) + 1;
                    if (e < parts[i].Length)
                    {
                        parts[i] = parts[i].Substring(e);
                    }
                    else
                    {
                        parts[i] = new string(StringBound, 2);
                    }
                }
                else
                {
                    parts[i] = parts[i].TrimStart(Spaces);
                    int l = parts[i].Length;
                    if (l > 1 && parts[i][0] == Resolve && parts[i][l - 1] == Resolve)
                    {
                        parts[i] = parts[i].Substring(1, l - 2);
                    }
                    else
                    {
                        string str = StringBound.ToString();
                        parts[i] = string.Concat(str, parts[i], str);
                    }
                }
            }

            #endregion

            switch (cmd.ToLowerInvariant())
            {
                #region Repeat

            case "repeat":
                param = StripCommentSingle(param);
                if (param.Length > 0 && !IsPrimativeObject(param))
                {
                    param = string.Empty;
                }
                replaced.Append("Loop ");
                replaced.Append(param);
                replaced.Append(SingleSpace);
                replaced.Append(BlockOpen);
                break;

            case "endrepeat":
                replaced.Append(BlockClose);
                replaced.Append(param);
                break;

                #endregion

                #region Setters

            case "setbatchlines":
            case "setcontroldelay":
            case "setdefaultmousespeed":
            case "setkeydelay":
            case "setmousedelay":
            case "setstorecapslockmode":
            case "settitlematchmode":
            case "setwindelay":
            case "setworkingdir":
                replaced.Append("A_");
                replaced.Append(cmd, 3, cmd.Length - 3);
                replaced.Append(Equal);
                replaced.Append(param);
                break;

            case "setenv":
                replaced.Append(parts[0].Substring(1, parts[0].Length - 2));
                replaced.Append(AssignPre);
                replaced.Append(Equal);
                if (parts.Length > 1)
                {
                    replaced.Append(parts[1]);
                }
                else
                {
                    replaced.Append(NullTxt);
                }
                break;

            case "setformat":
                if (parts.Length != 2)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append("A_Format");
                const string fast = "fast";
                parts[0] = parts[0].Substring(1, parts[0].Length - 2);
                if (parts[0].EndsWith(fast, System.StringComparison.OrdinalIgnoreCase))
                {
                    parts[0] = parts[0].Substring(0, parts[0].Length - fast.Length);
                }
                replaced.Append(parts[0]);
                replaced.Append(AssignPre);
                replaced.Append(Equal);
                replaced.Append(parts[1]);
                break;

            case "autotrim":
            case "detecthiddentext":
            case "detecthiddenwindows":
            case "stringcasesense":
                replaced.Append("A_");
                replaced.Append(cmd);
                replaced.Append(Equal);
                replaced.Append(param);
                break;

                #endregion

                #region If

                #region Equality

            // TODO: push single conditional command on same line as legacy converted equality-if statements

            case "ifequal":
                if (parts.Length < 1)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(parts[0]);
                replaced.Append(Equal);
                if (parts.Length > 1)
                {
                    replaced.Append(parts[1]);
                }
                break;

            case "ifnotequal":
                if (parts.Length < 1)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(parts[0]);
                replaced.Append(Not);
                replaced.Append(Equal);
                if (parts.Length > 1)
                {
                    replaced.Append(parts[1]);
                }
                break;

            case "ifgreater":
                if (parts.Length < 1)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(parts[0]);
                replaced.Append(Greater);
                if (parts.Length > 1)
                {
                    replaced.Append(parts[1]);
                }
                break;

            case "ifgreaterorequal":
                if (parts.Length < 1)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(parts[0]);
                replaced.Append(Greater);
                replaced.Append(Equal);
                if (parts.Length > 1)
                {
                    replaced.Append(parts[1]);
                }
                break;

            case "ifless":
                if (parts.Length < 1)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(parts[0]);
                replaced.Append(Less);
                if (parts.Length > 1)
                {
                    replaced.Append(parts[1]);
                }
                break;

            case "iflessorequal":
                if (parts.Length < 1)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(parts[0]);
                replaced.Append(Less);
                replaced.Append(Equal);
                if (parts.Length > 1)
                {
                    replaced.Append(parts[1]);
                }
                break;

                #endregion

            case "ifexist":
                if (parts.Length < 1)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(ParenOpen);
                replaced.Append("FileExist");
                replaced.Append(ParenOpen);
                replaced.Append(parts[0]);
                replaced.Append(ParenClose, 2);
                break;

            case "ifnotexist":
                if (parts.Length < 1)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(ParenOpen);
                replaced.Append(Not);
                replaced.Append("FileExist");
                replaced.Append(ParenOpen);
                replaced.Append(parts[0]);
                replaced.Append(ParenClose, 2);
                break;

            case "ifinstring":
                if (parts.Length < 2)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(ParenOpen);
                replaced.Append("InStr");
                replaced.Append(ParenOpen);
                replaced.Append(parts[0]);
                replaced.Append(Multicast);
                replaced.Append(parts[1]);
                replaced.Append(ParenClose, 2);
                break;

            case "ifnotinstring":
                if (parts.Length < 2)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(ParenOpen);
                replaced.Append(Not);
                replaced.Append("InStr");
                replaced.Append(ParenOpen);
                replaced.Append(parts[0]);
                replaced.Append(Multicast);
                replaced.Append(parts[1]);
                replaced.Append(ParenClose, 2);
                break;

            case "ifmsgbox":
                if (parts.Length < 1)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(ParenOpen);
                replaced.Append("A_MsgBox");
                replaced.Append(Equal);
                replaced.Append(parts[0]);
                replaced.Append(ParenClose);
                break;

            case "ifwinactive":
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(ParenOpen);
                replaced.Append("WinActive");
                replaced.Append(ParenOpen);
                foreach (var part in parts)
                {
                    replaced.Append(part);
                    replaced.Append(Multicast);
                }
                if (parts.Length > 1)
                {
                    replaced.Remove(replaced.Length - 1, 1);
                }
                replaced.Append(ParenClose, 2);
                break;

            case "ifwinexist":
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(ParenOpen);
                replaced.Append("WinExist");
                replaced.Append(ParenOpen);
                foreach (var part in parts)
                {
                    replaced.Append(part);
                    replaced.Append(Multicast);
                }
                if (parts.Length > 1)
                {
                    replaced.Remove(replaced.Length - 1, 1);
                }
                replaced.Append(ParenClose, 2);
                break;

            case "ifwinnotactive":
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(ParenOpen);
                replaced.Append(Not);
                replaced.Append("WinActive");
                replaced.Append(ParenOpen);
                foreach (var part in parts)
                {
                    replaced.Append(part);
                    replaced.Append(Multicast);
                }
                if (parts.Length > 1)
                {
                    replaced.Remove(replaced.Length - 1, 1);
                }
                replaced.Append(ParenClose, 2);
                break;

            case "ifwinnotexist":
                replaced.Append(FlowIf);
                replaced.Append(SingleSpace);
                replaced.Append(ParenOpen);
                replaced.Append(Not);
                replaced.Append("WinExist");
                replaced.Append(ParenOpen);
                foreach (var part in parts)
                {
                    replaced.Append(part);
                    replaced.Append(Multicast);
                }
                if (parts.Length > 1)
                {
                    replaced.Remove(replaced.Length - 1, 1);
                }
                replaced.Append(ParenClose, 2);
                break;

                #endregion

                #region Strings

            // HACK: convert L/R paramter for legacy StringGetPos command
            case "stringgetpos":
                if (parts.Length < 3)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(parts[0].Trim(StringBound));
                replaced.Append(AssignPre);
                replaced.Append(Equal);
                replaced.Append("InStr");
                replaced.Append(ParenOpen);
                replaced.Append(parts[1]);
                replaced.Append(Multicast);
                replaced.Append(parts[2]);
                replaced.Append(Multicast);
                replaced.Append(FalseTxt);
                replaced.Append(Multicast);
                replaced.Append(parts.Length > 4 ? parts[4] : "0");
                replaced.Append(ParenClose);
                break;

            case "stringleft":
                if (parts.Length < 3)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(parts[0]);
                replaced.Append(AssignPre);
                replaced.Append(Equal);
                replaced.Append("SubStr");
                replaced.Append(ParenOpen);
                replaced.Append(parts[1]);
                replaced.Append(Multicast);
                replaced.Append("1");
                replaced.Append(Multicast);
                replaced.Append(parts[2]);
                replaced.Append(ParenClose);
                break;

            case "stringlen":
                if (parts.Length < 2)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(parts[0]);
                replaced.Append(AssignPre);
                replaced.Append(Equal);
                replaced.Append("StrLen");
                replaced.Append(ParenOpen);
                replaced.Append(parts[1]);
                replaced.Append(ParenClose);
                break;

            case "stringmid":
                if (parts.Length < 3)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(parts[0]);
                replaced.Append(AssignPre);
                replaced.Append(Equal);
                replaced.Append("SubStr");
                replaced.Append(ParenOpen);
                replaced.Append(parts[1]);
                replaced.Append(Multicast);
                replaced.Append(parts[2]);
                if (parts.Length > 3)
                {
                    replaced.Append(Multicast);
                    replaced.Append(parts[3]);
                }
                if (parts.Length > 4)
                {
                    replaced.Append(Multicast);
                    replaced.Append(parts[4]);
                }
                replaced.Append(ParenClose);
                break;

            case "stringright":
                if (parts.Length < 3)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(parts[0]);
                replaced.Append(AssignPre);
                replaced.Append(Equal);
                replaced.Append("SubStr");
                replaced.Append(ParenOpen);
                replaced.Append(parts[1]);
                replaced.Append(Multicast);
                replaced.Append("1");
                replaced.Append(Add);
                replaced.Append(Minus);
                replaced.Append(ParenOpen);
                replaced.Append(parts[2]);
                replaced.Append(ParenClose);
                replaced.Append(ParenClose);
                break;

            case "stringtrimleft":
                if (parts.Length < 3)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(parts[0]);
                replaced.Append(AssignPre);
                replaced.Append(Equal);
                replaced.Append("SubStr");
                replaced.Append(ParenOpen);
                replaced.Append(parts[1]);
                replaced.Append(Multicast);
                replaced.Append("1");
                replaced.Append(Add);
                replaced.Append(parts[2]);
                replaced.Append(ParenClose);
                break;

            case "stringtrimright":
                if (parts.Length < 3)
                {
                    throw new ParseException(ExTooFewParams);
                }
                replaced.Append(parts[0]);
                replaced.Append(AssignPre);
                replaced.Append(Equal);
                replaced.Append("SubStr");
                replaced.Append(ParenOpen);
                replaced.Append(parts[1]);
                replaced.Append(Multicast);
                replaced.Append("1");
                replaced.Append(Multicast);
                replaced.Append(Minus);
                replaced.Append(ParenOpen);
                replaced.Append(parts[2]);
                replaced.Append(ParenClose);
                replaced.Append(ParenClose);
                break;

                #endregion

                #region Arithmetic

                // TODO: translate legacy EnvMult, EnvDiv etc

                #endregion

                #region Send

            case "sendevent":
            case "sendinput":
            case "sendplay":
                replaced.Append("Send");
                replaced.Append(Multicast);
                replaced.Append(SingleSpace);
                replaced.Append(param);
                break;

            case "sendraw":
                replaced.Append("Send");
                replaced.Append(Multicast);
                replaced.Append(SingleSpace);
                ParameterPrepend(ref param, "{Raw}");
                replaced.Append(param);
                break;

            case "controlsendraw":
                replaced.Append("ControlSend");
                replaced.Append(Multicast);
                replaced.Append(SingleSpace);
                ParameterPrepend(ref param, "{Raw}");
                replaced.Append(param);
                break;

            case "sendmode":
                code = string.Empty;
                break;

            case "setcapslockstate":
            case "setnumlockstate":
            case "setscrolllockstate":
                replaced.Append("SetLockState");
                replaced.Append(Multicast);
                replaced.Append(SingleSpace);
                replaced.Append(cmd, 3, cmd.Length - 3 - 5);
                replaced.Append(Multicast);
                replaced.Append(SingleSpace);
                replaced.Append(param);
                break;

                #endregion

                #region Mouse

            case "leftclick":
            case "mouseclick":
                replaced.Append("Click");
                replaced.Append(Multicast);
                replaced.Append(param);
                break;

            case "leftclickdrag":
                replaced.Append("MouseClickDrag");
                replaced.Append(Multicast);
                replaced.Append("Left");
                replaced.Append(Multicast);
                replaced.Append(param);
                break;

            case "mousemove":
                replaced.Append("Click");
                replaced.Append(Multicast);
                replaced.Append(param);
                replaced.Append(Multicast);
                replaced.Append("0");
                break;

                #endregion

                #region Debug

            case "edit":
            case "listlines":
            case "listvars":
                replaced = null;
                break;

                #endregion

                #region Other

            case "filegetattrib":
                if (parts.Length != 2)
                {
                    replaced = null;
                }
                else
                {
                    replaced.Append(parts[0].Substring(1, parts[0].Length - 2));
                    replaced.Append(AssignPre);
                    replaced.Append(Equal);
                    replaced.Append("FileExist");
                    replaced.Append(ParenOpen);
                    replaced.Append(parts[1]);
                    replaced.Append(ParenClose);
                }
                break;

                #endregion
            }

            if (replaced == null)
            {
                code = string.Empty;
            }
            else if (replaced.Length > 0)
            {
                code = replaced.ToString();
            }
        }
Пример #6
0
        private CodeStatement[] ParseFlow(List <CodeLine> lines, int index)
        {
            #region Variables

            var      line  = lines[index];
            string   code  = line.Code.TrimStart(Spaces);
            string[] parts = { string.Empty, string.Empty };

            var delimiters = new char[Spaces.Length + 1];
            delimiters[0] = Multicast;
            Spaces.CopyTo(delimiters, 1);
            int[] d = { code.IndexOfAny(delimiters), code.IndexOfAny(new[] { BlockOpen, ParenOpen }) };

            if (d[0] == -1 && d[1] == -1)
            {
                parts[0] = code;
            }
            else if (d[1] != -1 && (d[1] < d[0] || d[0] == -1))
            {
                parts[0] = code.Substring(0, d[1]);
                parts[1] = code.Substring(d[1], code.Length - d[1]).TrimStart(Spaces);
            }
            else
            {
                parts[0] = code.Substring(0, d[0]);
                parts[1] = code.Substring(d[0] + 1, code.Length - d[0] - 1).TrimStart(Spaces);
            }

            if (parts.Length > 1 && IsEmptyStatement(parts[1]))
            {
                parts = new[] { parts[0] }
            }
            ;

            #endregion Variables

            switch (parts[0].ToLowerInvariant())
            {
                #region If/Else

            case FlowIf:
            {
                if (parts.Length < 1)
                {
                    throw new ParseException("If requires a parameter");
                }

                bool           blockOpen = false;
                CodeExpression condition = ParseFlowParameter(parts[1], true, out blockOpen, false);
                var            ifelse    = new CodeConditionStatement
                {
                    Condition = condition
                };

                var block = new CodeBlock(line, Scope, ifelse.TrueStatements, CodeBlock.BlockKind.IfElse, blocks.Count == 0 ? null : blocks.Peek());
                block.Type = blockOpen ? CodeBlock.BlockType.Within : CodeBlock.BlockType.Expect;
                CloseTopSingleBlock();
                blocks.Push(block);

                elses.Push(ifelse.FalseStatements);
                return(new CodeStatement[] { ifelse });
            }

            case FlowElse:
            {
                if (elses.Count == 0)
                {
                    throw new ParseException("Else with no preceeding if block");
                }

                string next = line.Code.TrimStart(Spaces).Substring(FlowElse.Length).TrimStart(Spaces);

                if (!IsEmptyStatement(next))
                {
                    lines.Insert(index + 1, new CodeLine(lines[index].FileName, lines[index].LineNumber, next));
                }

                var type  = parts.Length > 1 && parts[1][0] == BlockOpen ? CodeBlock.BlockType.Within : CodeBlock.BlockType.Expect;
                var block = new CodeBlock(lines[index], Scope, elses.Pop(), CodeBlock.BlockKind.IfElse, blocks.Count == 0 ? null : blocks.Peek())
                {
                    Type = type
                };
                CloseTopSingleBlock();
                blocks.Push(block);
            }
            break;

                #endregion If/Else

                #region Goto

            case FlowGosub:
            {
                if (parts.Length < 1)
                {
                    throw new ParseException("No label specified");
                }
                return(new CodeStatement[] { new CodeExpressionStatement(LocalLabelInvoke(parts[1])) });
            }

            case FlowGoto:
            {
                if (parts.Length < 1)
                {
                    throw new ParseException("No label specified");
                }
                return(new CodeStatement[] { new CodeExpressionStatement(LocalLabelInvoke(parts[1])), new CodeMethodReturnStatement() });
            }

                #endregion Goto

                #region Loops

            case FlowLoop:
            {
                bool blockOpen = false;
                CodeMethodInvokeExpression iterator;
                bool skip       = true;
                bool checkBrace = true;
                bool byref      = false;

                #region Loop types

                if (parts.Length > 1)
                {
                    string[] sub = parts[1].Split(new[] { Multicast }, 2);
                    sub = new[] { sub[0].Trim(), sub.Length > 1 ? sub[1].Trim() : string.Empty };

                    switch (sub[0].ToUpperInvariant())
                    {
                    case "READ":
                        byref    = true;
                        iterator = (CodeMethodInvokeExpression)InternalMethods.LoopRead;
                        break;

                    case "PARSE":
                        checkBrace = false;
                        byref      = true;
                        iterator   = (CodeMethodInvokeExpression)InternalMethods.LoopParse;
                        break;

                    case "HKEY_LOCAL_MACHINE":
                    case "HKLM":
                    case "HKEY_USERS":
                    case "HKU":
                    case "HKEY_CURRENT_USER":
                    case "HKCU":
                    case "HKEY_CLASSES_ROOT":
                    case "HKCR":
                    case "HKEY_CURRENT_CONFIG":
                    case "HKCC":
                        iterator = (CodeMethodInvokeExpression)InternalMethods.LoopRegistry;
                        break;

                    case "EACH":
                        byref    = true;
                        iterator = (CodeMethodInvokeExpression)InternalMethods.LoopEach;
                        break;

                    default:
                    {
                        var file = false;

                        if (parts[1].IndexOf(Multicast) != -1)
                        {
                            file = true;
                        }

                        // TODO: check file/iteration loop types

                        skip     = false;
                        iterator = (CodeMethodInvokeExpression)(file ? InternalMethods.LoopFile : InternalMethods.Loop);
                    }
                    break;
                    }

                    if (skip)
                    {
                        parts[1] = sub[1];
                    }

                    if (checkBrace)
                    {
                        // TODO: check expression parameters before stripping comments
                        int    x    = parts.Length == 1 ? 0 : 1;
                        string part = StripComment(parts[x]).TrimEnd(Spaces);
                        int    l    = part.Length - 1;
                        if (part.Length > 0 && part[l] == BlockOpen)
                        {
                            blockOpen = true;
                            parts[x]  = part.Substring(0, l);
                        }
                    }

                    if (skip && parts[1].Length == 0)
                    {
                        throw new ParseException("Loop type must have an argument");
                    }

                    foreach (var arg in SplitCommandParameters(parts[1]))
                    {
                        iterator.Parameters.Add(ParseCommandParameter(arg));
                    }

                    if (LegacyLoop && byref)
                    {
                        iterator.Parameters[0] = VarId(iterator.Parameters[0]);
                    }
                }
                else
                {
                    iterator = (CodeMethodInvokeExpression)InternalMethods.Loop;
                    iterator.Parameters.Add(new CodePrimitiveExpression(int.MaxValue));
                }

                #endregion Loop types

                string id = InternalID;

                var init = new CodeVariableDeclarationStatement();
                init.Name           = id;
                init.Type           = new CodeTypeReference(typeof(IEnumerable));
                init.InitExpression = new CodeMethodInvokeExpression(iterator, "GetEnumerator", new CodeExpression[] { });

                var condition = new CodeMethodInvokeExpression();
                condition.Method.TargetObject = new CodeVariableReferenceExpression(id);
                condition.Method.MethodName   = "MoveNext";

                var loop = new CodeIterationStatement();
                loop.InitStatement      = init;
                loop.IncrementStatement = new CodeCommentStatement(string.Empty);                                 // for C# display
                loop.TestExpression     = condition;

                var block = new CodeBlock(line, Scope, loop.Statements, CodeBlock.BlockKind.Loop, blocks.Count == 0 ? null : blocks.Peek(), InternalID, InternalID);
                block.Type = blockOpen ? CodeBlock.BlockType.Within : CodeBlock.BlockType.Expect;
                CloseTopSingleBlock();
                blocks.Push(block);

                return(new CodeStatement[] { loop, new CodeLabeledStatement(block.ExitLabel) });
            }

            case FlowWhile:
            {
                bool           blockOpen = false;
                CodeExpression condition = parts.Length > 1 ? ParseFlowParameter(parts[1], true, out blockOpen, true) : new CodePrimitiveExpression(true);
                var            loop      = new CodeIterationStatement();
                loop.TestExpression = condition;
                loop.InitStatement  = new CodeCommentStatement(string.Empty);

                var block = new CodeBlock(line, Scope, loop.Statements, CodeBlock.BlockKind.Loop, blocks.Count == 0 ? null : blocks.Peek(), InternalID, InternalID);
                block.Type = blockOpen ? CodeBlock.BlockType.Within : CodeBlock.BlockType.Expect;
                CloseTopSingleBlock();
                blocks.Push(block);

                return(new CodeStatement[] { loop, new CodeLabeledStatement(block.ExitLabel) });
            }

            case FlowBreak:
                int b = 1;
                if (parts.Length > 1)
                {
                    parts[1] = StripCommentSingle(parts[1]);
                    if (!int.TryParse(parts[1], out b) || b < 1)
                    {
                        throw new ParseException("Break parameter must be a static integer greater than zero.");
                    }
                }
                string exit = PeekLoopLabel(true, b);
                if (exit == null)
                {
                    throw new ParseException("Cannot break outside a loop");
                }
                return(new CodeStatement[] { new CodeGotoStatement(exit) });

            case FlowContinue:
                int c = 1;
                if (parts.Length > 1)
                {
                    parts[1] = StripCommentSingle(parts[1]);
                    if (!int.TryParse(parts[1], out c) || c < 1)
                    {
                        throw new ParseException("Continue parameter must be a static integer greater than zero.");
                    }
                }
                string cont = PeekLoopLabel(false, c);
                if (cont == null)
                {
                    throw new ParseException("Cannot continue outside a loop");
                }
                return(new CodeStatement[] { new CodeGotoStatement(cont) });

                #endregion Loops

                #region Return

            case FlowReturn:
                if (Scope == mainScope)
                {
                    if (parts.Length > 1)
                    {
                        throw new ParseException("Cannot have return parameter for entry point method");
                    }
                    return(new CodeStatement[] { new CodeMethodReturnStatement() });
                }
                else
                {
                    var result = parts.Length > 1 ? ParseSingleExpression(parts[1]) : new CodePrimitiveExpression(null);
                    return(new CodeStatement[] { new CodeMethodReturnStatement(result) });
                }

                #endregion Return

                #region Function

            case FunctionLocal:
            case FunctionGlobal:
            case FunctionStatic:
                // TODO: function local/global/static scoping modifiers
                break;

                #endregion Function

            default:
                throw new ParseException(ExUnexpected);
            }

            return(null);
        }