public void TakeArgumentStateLogInitTest() { var sut = new RemoveArgumentStateLog(); var initHisto = sut.ToString(true); const string expected = @" Start: 0 LeadingWhitespace: 0 ScanningBackslashesOutsideQuotes: 0 QuoteAfterBackslashesOutsideQuotes: 0 EmittingBackslashesOutsideQuotes: 0 CheckForArgumentEndOutsideQuotes: 0 EmittingBackslashesOutsideQuotesGoingInsideQuotes: 0 ScanningBackslashesInsideQuotes: 0 QuoteAfterBackslashesInsideQuotes: 0 EmittingBackslashesInsideQuotes: 0 CheckForTwoQuotesAfterBackslashesInsideQuotes: 0 EmittingBackslashesInsideQuotesGoingOutsideQuotes: 0 CheckForArgumentEndInsideQuotesGoingOutsideQuotes: 0 EmittingBackslashesInsideQuotesStayingInsideQuotes: 0 EmitFinalBackslashesInsideQuotes: 0 CopyToEnd: 0 End: 0 "; Assert.Equal(expected, initHisto); }
public void TakeArgumentTests(string input, string expectedArgument, string expectedRemainingLine) { var argumentStream = input.ToCharArray().AsEnumerable(); var argumentValue = new StringBuilder(); var log = new RemoveArgumentStateLog(); var remainingLine = input.RemoveArgument(argumentValue, log).Collect(); output_.WriteLine(log.ToString(true)); argumentValue.ToString().Should().Be(expectedArgument, "arg failed, input was \"{0}\"", input.ToLiteralFormat()); remainingLine.Should().Be(expectedRemainingLine, "remaining failed, input was \"{0}\"", input.ToLiteralFormat()); }
public void TakeArgumentStateLogWithLoggingTest() { var sut = new RemoveArgumentStateLog(); var sb = new StringBuilder(); sb.Append('x'); sut.Add("Start-a-0-x", CppArgumentLexing.ArgStates.Start, 'a', 0, sb); sb.Append('y'); sut.Add("LeadingWhitespace-b-1-xy", CppArgumentLexing.ArgStates.LeadingWhitespace, 'b', 1, sb); sb.Append('z'); sut.Add("EmitFinalBackslashesInsideQuotes-c-2-xyz", CppArgumentLexing.ArgStates.EmitFinalBackslashesInsideQuotes, 'c', 2, sb); sb.Append('w'); sut.Add("LeadingWhitespace-d-3-xyzw", CppArgumentLexing.ArgStates.LeadingWhitespace, 'd', 3, sb); var log = sut.ToString(); output_.WriteLine(log); const string expectedLog = @"(Start-a-0-x, Start, 'a', 0, ""x"") (LeadingWhitespace-b-1-xy, LeadingWhitespace, 'b', 1, ""xy"") (EmitFinalBackslashesInsideQuotes-c-2-xyz, EmitFinalBackslashesInsideQuotes, 'c', 2, ""xyz"") (LeadingWhitespace-d-3-xyzw, LeadingWhitespace, 'd', 3, ""xyzw"") "; Assert.Equal(expectedLog, log); var histo = sut.ToString(true); const string expectedHisto = expectedLog + @" Start: 1 LeadingWhitespace: 2 ScanningBackslashesOutsideQuotes: 0 QuoteAfterBackslashesOutsideQuotes: 0 EmittingBackslashesOutsideQuotes: 0 CheckForArgumentEndOutsideQuotes: 0 EmittingBackslashesOutsideQuotesGoingInsideQuotes: 0 ScanningBackslashesInsideQuotes: 0 QuoteAfterBackslashesInsideQuotes: 0 EmittingBackslashesInsideQuotes: 0 CheckForTwoQuotesAfterBackslashesInsideQuotes: 0 EmittingBackslashesInsideQuotesGoingOutsideQuotes: 0 CheckForArgumentEndInsideQuotesGoingOutsideQuotes: 0 EmittingBackslashesInsideQuotesStayingInsideQuotes: 0 EmitFinalBackslashesInsideQuotes: 1 CopyToEnd: 0 End: 0 "; Assert.Equal(expectedHisto, histo); }
/// <summary> /// Grab a parameter (_not_ the first) off of incoming stream, then pass /// all the rest of the characters unchanged. Quoting rules are /// complicated, see http://www.daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULES /// and https://msdn.microsoft.com/en-us/library/17w5ykft(v=vs.120).aspx /// for details. /// </summary> /// <remarks> /// Engineered from the D.Deley article above into a state machine. /// </remarks> public static IEnumerable<char> RemoveArgument( this IEnumerable<char> source, StringBuilder argument, /*out*/ RemoveArgumentStateLog log = null) { ArgStates state = ArgStates.Start; int nBackslashes = 0; foreach (char c in source) { if (null != log) log.Add("fetchChar", state, c, nBackslashes, argument); goto stateAction; reinspectChar: if (null != log) log.Add("reinspectChar", state, c, nBackslashes, argument); goto stateAction; stateAction: switch (state) { case ArgStates.Start: state = ArgStates.LeadingWhitespace; goto reinspectChar; case ArgStates.LeadingWhitespace: if (' ' == c || '\t' == c) { continue; } else { state = ArgStates.ScanningBackslashesOutsideQuotes; nBackslashes = 0; goto reinspectChar; } case ArgStates.ScanningBackslashesOutsideQuotes: if ('\\' == c) { nBackslashes++; continue; } else if ('\"' == c) { state = ArgStates.QuoteAfterBackslashesOutsideQuotes; goto reinspectChar; } else { state = ArgStates.EmittingBackslashesOutsideQuotes; goto reinspectChar; } case ArgStates.QuoteAfterBackslashesOutsideQuotes: { bool oddBackslashes = 1 == nBackslashes % 2; nBackslashes /= 2; if (oddBackslashes) { state = ArgStates.EmittingBackslashesOutsideQuotes; goto reinspectChar; } else { state = ArgStates.EmittingBackslashesOutsideQuotesGoingInsideQuotes; continue; } } case ArgStates.EmittingBackslashesOutsideQuotes: argument.Append('\\', nBackslashes); nBackslashes = 0; state = ArgStates.CheckForArgumentEndOutsideQuotes; goto reinspectChar; case ArgStates.CheckForArgumentEndOutsideQuotes: if (' ' == c || '\t' == c) { state = ArgStates.CopyToEnd; goto reinspectChar; } else { argument.Append(c); nBackslashes = 0; state = ArgStates.ScanningBackslashesOutsideQuotes; continue; } case ArgStates.EmittingBackslashesOutsideQuotesGoingInsideQuotes: argument.Append('\\', nBackslashes); nBackslashes = 0; state = ArgStates.ScanningBackslashesInsideQuotes; goto reinspectChar; case ArgStates.ScanningBackslashesInsideQuotes: if ('\\' == c) { nBackslashes++; continue; } else if ('\"' == c) { state = ArgStates.QuoteAfterBackslashesInsideQuotes; goto reinspectChar; } else { state = ArgStates.EmittingBackslashesInsideQuotes; goto reinspectChar; } case ArgStates.QuoteAfterBackslashesInsideQuotes: { bool oddBackslashes = 1 == nBackslashes % 2; nBackslashes /= 2; if (oddBackslashes) { state = ArgStates.EmittingBackslashesInsideQuotes; goto reinspectChar; } else { state = ArgStates.CheckForTwoQuotesAfterBackslashesInsideQuotes; continue; } } case ArgStates.EmittingBackslashesInsideQuotes: argument.Append('\\', nBackslashes).Append(c); nBackslashes = 0; state = ArgStates.ScanningBackslashesInsideQuotes; continue; case ArgStates.CheckForTwoQuotesAfterBackslashesInsideQuotes: if ('\"' == c) { state = ArgStates.EmittingBackslashesInsideQuotesStayingInsideQuotes; goto reinspectChar; } else { state = ArgStates.EmittingBackslashesInsideQuotesGoingOutsideQuotes; goto reinspectChar; } case ArgStates.EmittingBackslashesInsideQuotesGoingOutsideQuotes: argument.Append('\\', nBackslashes); nBackslashes = 0; state = ArgStates.CheckForArgumentEndInsideQuotesGoingOutsideQuotes; goto reinspectChar; case ArgStates.EmittingBackslashesInsideQuotesStayingInsideQuotes: argument.Append('\\', nBackslashes).Append('\"'); nBackslashes = 0; state = ArgStates.ScanningBackslashesInsideQuotes; continue; case ArgStates.CheckForArgumentEndInsideQuotesGoingOutsideQuotes: if (' ' == c || '\t' == c) { state = ArgStates.CopyToEnd; goto reinspectChar; } else { argument.Append(c); nBackslashes = 0; state = ArgStates.ScanningBackslashesOutsideQuotes; continue; } case ArgStates.CopyToEnd: yield return c; state = ArgStates.CopyToEnd; continue; } } // Flush final backslashes if any (there's only one state where this is necessary) if (ArgStates.CheckForTwoQuotesAfterBackslashesInsideQuotes == state) { if (null != log) log.Add("flushing final backslashes", state, '¶', nBackslashes, argument); argument.Append('\\', nBackslashes); } if (null != log) log.Add("final result", ArgStates.End, '¶', 0, argument); }