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); }
#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); } }
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 }
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); }
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(); } }
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); }