예제 #1
0
        public static byte[] ConvertDataUrlToBinary(string url)
        {
            var parser = new Parser(null);

            Options options = new Options(null);

            options.CaseSensitive = true;

            // The main statement list
            StatementList mainStatements = new StatementList(null);

            mainStatements.Add(AggregateComparisons.WildcardComparison("data:*/*;base64,", null, options));

            // Capture the remaining string
            var capturing = new Dictionary <string, Capture>();
            var capture   = new Capture(null);

            capturing.Add("base64string", capture);
            mainStatements.Add(capture);
            capture.Comparison = new AdvanceToTheEnd(null).AsComparisonWithAdvance();

            string replaceWith = "'base64string'";
            var    stateList   = new List <State>();
            string b64String   = parser.Replace(url, replaceWith, mainStatements, capturing, stateList);

            var binData = Convert.FromBase64String(b64String);

            return(binData);
        }
예제 #2
0
        public static IComparisonWithAdvance CreateVBScriptQuotedString(
            Options options,
            string name = null,
            ILog log    = null)
        {
            // We can use a pattern comparison to do this
            // First character is a quote
            // Subsequent characters can either be anything BUT a single ", or double "" but advance 2.
            // Although it seems un-intuitive this gives us the behaviour we require: skip double quotes.

            var isQuote = new CharComparison(log, options, '\"');

            // This requires more than one statement
            StatementList statementList = new StatementList(log);

            statementList.Name = name;

            // The pattern comparison that will do the work
            PatternComparison patternComp = new PatternComparison(log, options);

            statementList.Add(patternComp);
            patternComp.MinLength = 2;

            // First character is open quote
            var first = new ComparisonRange <IComparisonWithAdvance>();

            first.Range.Min  = 1;
            first.Range.Max  = 1;
            first.Comparison = isQuote;
            patternComp.AddComparisonRange(first);

            // Subsequent characters
            var subsequent = new ComparisonRange <IComparisonWithAdvance>();

            subsequent.Range.Min = 2;
            OrComparison isNotSingleQuoteOrIsDoubleQuotesAndAdvance2 = new OrComparison(log);
            var          notQuote = new NotCharComparison(log, isQuote);

            isNotSingleQuoteOrIsDoubleQuotesAndAdvance2.Add(notQuote);
            var doubleQuote = new StringComparison(log, options, "\"\"");

            isNotSingleQuoteOrIsDoubleQuotesAndAdvance2.Add(doubleQuote);
            subsequent.Comparison = isNotSingleQuoteOrIsDoubleQuotesAndAdvance2;
            patternComp.AddComparisonRange(subsequent);

            // End when find the terminating "
            patternComp.EndComparison = isQuote;

            // Skip the trailing quote by advancing by 1
            statementList.Add(new OffsetAdvance(log, 1));
            return(statementList);
        }
예제 #3
0
        internal static void UnitTest()
        {
            var parser   = new Parser(null);
            var stmtList = new StatementList(null);
            var options  = new Options(null);

            options.CaseSensitive = false;
            stmtList.Add(new StringOffsetComparison(
                             null,
                             options,
                             Operand.CallFunction <int>("ReturnTwo"),
                             Operand.StaticValue(2),
                             true));
            int numMatches;

            Action <RunState> InitRunState = (RunState runState) => {
                runState.SetFunction <int>("ReturnTwo", (int pos, string str, RunState rs) => 2);
            };

            parser.Extract("abbA", "", stmtList, null, null, out numMatches, null, InitRunState);
            if (numMatches == 1)
            {
                Console.WriteLine("matches (success)");
            }
            else
            {
                Console.WriteLine("doesnt match (fail)");
            }
        }
예제 #4
0
        // Convert an operation into a comparison
        public IComparisonWithAdvance AsComparisonWithAdvance()
        {
            var list = new StatementList(Log);

            list.Add(this);
            return(list);
        }
예제 #5
0
        // Get a value between two comparisons
        public static IComparisonWithAdvance GetValuesBetweenComp(
            ILog log,
            IComparisonWithAdvance begin,
            IComparisonWithAdvance after)
        {
            StatementList mainComp = new StatementList(null);

            // Find the beginning
            mainComp.Add(begin);
            // Remember the position
            mainComp.Add(new StorePosAsVariable(log, "ValueBegin"));

            // Find the end and store the preceeding position
            mainComp.Add(new AdvanceUntilComparison(log, new CompareNoAdvance(log, after)));
            mainComp.Add(new StorePosAsVariable(log, "ValueEnd"));

            return(mainComp);
        }
예제 #6
0
        public static void MatchVBScriptFunction_T2(ILog log)
        {
            const string input   = "ignorethis obj.Func(1)";
            const string funcStr = "obj.Func";

            var statementList = new StatementList(log);

            var mainComp = TokenComparison.CreateVBScriptFunctionOrVar(null, null, null, null, log);

            statementList.Add(new StorePosAsVariable(log, "BeginPos"));
            statementList.Add(mainComp);

            Action <RunState> InitRunState = (runState) => {
                {
                    var        funcIndex = input.IndexOf(funcStr);
                    IAssertion sub       = new PositionAssertion(true, funcIndex, funcIndex + funcStr.Length);
                    sub.Name = "sub";
                    runState.AddAssertion("sub", sub);
                }

                {
                    var        funcIndex      = input.IndexOf(funcStr);
                    IAssertion isEndOrArgList = new PositionAssertion(
                        true, funcIndex + funcStr.Length, funcIndex + funcStr.Length + 3
                        );
                    isEndOrArgList.Name = "IsEndOrArgumentList";
                    runState.AddAssertion("IsEndOrArgumentList", isEndOrArgList);
                }
            };

            Func <RunState, string, int, string> ReplaceFunc = (runState, _input, pos) => {
                var beginPos = (int)runState.GetVariable("BeginPos");
                var str      = input.Substring(beginPos, pos - beginPos);
                return(str);
            };

            var parser = new Parser(log);
            int numMatches;

            parser.Extract(input, null, statementList, null, null, out numMatches, ReplaceFunc, InitRunState);
            System.Diagnostics.Debug.Assert(numMatches == 1);
        }
예제 #7
0
        public static IComparisonWithAdvance VBScriptLineWrap(
            ILog log,
            Options options)
        {
            // In VB script you can have the underscore character which signifies wrapping to the next line.
            // E.g:
            // Response.Write _
            //  (test & _
            //  args)
            IOperation    skipWhitespace = SkipWhitespace(log);
            StatementList lineWrap       = new StatementList(log);

            lineWrap.Add(skipWhitespace);
            var isUnderscore = new CharComparison(log, options, '_');

            lineWrap.Add(isUnderscore);
            lineWrap.Add(skipWhitespace);

            return(lineWrap);
        }
예제 #8
0
        // This relies on there being the variables 'ValueBegin' and 'ValueEnd'
        public static string GetSingleValue(
            string input,
            Parser parser,
            IComparisonWithAdvance comp,
            ILog log)
        {
            // Skip to the end
            var statementList = new StatementList(log);

            statementList.Add(comp);
            statementList.Add(new AdvanceToTheEnd(log));

            int    numAmount;
            string singleValue = parser.Extract(input, null, statementList, null, null, out numAmount, GetSingleValue_ReplaceFunc);

            if (numAmount == 1)
            {
                return(singleValue);
            }
            return(null);
        }
예제 #9
0
        public static IComparisonWithAdvance HTMLTag(
            ILog log,
            Options options,
            string tagName,
            OrComparison attributes,
            string name = null)
        {
            ICharComparison isWhitespace   = IsWhitespace(log);
            IOperation      skipWhitespace = SkipWhitespace(log);

            StatementList statementList = new StatementList(log);

            statementList.Name = name;

            statementList.Add(new CharComparison(log, options, '<'));
            statementList.Add(new StringComparison(log, options, tagName));
            statementList.Add(isWhitespace);

            // The reason for the or list is in case the tags appear in a different order on different browsers
            DelimitedListComparison list = new DelimitedListComparison(log, options, attributes, isWhitespace);

            statementList.Add(list);
            list.MinAmount = attributes.Count;
            list.MaxAmount = attributes.Count;

            statementList.Add(skipWhitespace);
            statementList.Add(new CharComparison(log, options, '>'));

            return(statementList);
        }
예제 #10
0
        public static IComparisonWithAdvance VBScriptKeywords(
            ILog log,
            Options options)
        {
            // Need more than one statement to achieve this
            StatementList statements = new StatementList(log);

            // Start of the string

            //// Starts with whitespace or is the start of the input string
            StartOfInputStringComparison isStartOfString = new StartOfInputStringComparison(log);
            var          isWhitespace = IsWhitespace(log);
            OrComparison startsWithWhitespaceOrIsStartOfInputString = new OrComparison(log);

            startsWithWhitespaceOrIsStartOfInputString.Add(isStartOfString);
            startsWithWhitespaceOrIsStartOfInputString.Add(isWhitespace);
            statements.Add(startsWithWhitespaceOrIsStartOfInputString);

            // Trim left - skip whitespace
            var skipWhitespace = SkipWhitespace(log);

            statements.Add(skipWhitespace);

            //// Keywords
            OrComparison orComp = new OrComparison(log);

            statements.Add(orComp);
            for (int i = 0; i < VBKeywords.Length; ++i)
            {
                orComp.Add(new StringComparison(log, options, VBKeywords[i]));
            }

            // Trim right
            statements.Add(skipWhitespace);

            return(statements);
        }
예제 #11
0
        // E.g. Test123 matches
        // 1Test123 doesn't (cann't lead with a number)
        // test"123 doesn't match (can only contain letters and numbers)
        public static IComparisonWithAdvance CreateIdentifier(
            Options options,
            IComparisonWithAdvance end,
            IComparison exclusion,
            string name = null,
            ILog log    = null)
        {
            StatementList stmtList = new StatementList(log);

            stmtList.Name = name;

            PatternComparison patternComp = new PatternComparison(log, options);

            patternComp.EndComparison = end;
            patternComp.MinLength     = 1;

            // First character is a letter
            var firstCharacter = new ComparisonRange <IComparisonWithAdvance>();

            firstCharacter.Range.Min = 1;
            firstCharacter.Range.Max = 1;
            var isLetter = new CharDelegateComparison(log, Char.IsLetter);

            firstCharacter.Comparison = isLetter;
            patternComp.AddComparisonRange(firstCharacter);

            // Subsequent (2+) characters can be a letter or digit (0-9)
            var subsequentCharacters = new ComparisonRange <IComparisonWithAdvance>();

            subsequentCharacters.Range.Min = 2;
            var isLetterOrDigit = new CharDelegateComparison(log, Char.IsLetterOrDigit);

            subsequentCharacters.Comparison = isLetterOrDigit;
            patternComp.AddComparisonRange(subsequentCharacters);

            stmtList.Add(patternComp);

            stmtList.Exclusion = exclusion;

            return(stmtList);
        }
예제 #12
0
        public static IComparisonWithAdvance CreateVBScriptSub(
            Options options,
            string name = null,
            ILog log    = null)
        {
            // This requires more than one statement
            StatementList statementList = new StatementList(log);

            statementList.Name = name;

            // Function end: . , ", or whitespace
            CharComparison         isDot        = new CharComparison(log, options, '.');
            CharComparison         isQuote      = new CharComparison(log, options, '\"');
            CharDelegateComparison isWhitespace = new CharDelegateComparison(log, Char.IsWhiteSpace);
            OrComparison           funcNameEnd  = new OrComparison(log);

            funcNameEnd.Add(isDot);
            funcNameEnd.Add(isQuote);
            funcNameEnd.Add(isWhitespace);

            // Identifier but not including classes/namespaces
            var identifier = TokenComparison.CreateIdentifier(
                options,
                end: funcNameEnd,
                exclusion: null,
                name: null,
                log: log);

            // List of identifiers seperated by . which will include classes/namespaces
            var sub = TokenComparison.CreateListOfTokens(
                options,
                token: identifier,
                segment: isDot,
                name: null,
                log: null);

            statementList.Add(sub);

            return(statementList);
        }
예제 #13
0
        public static IComparisonWithAdvance CreateNumber(
            Options options,
            IComparisonWithAdvance end,
            string name = null,
            ILog log    = null)
        {
            //// List of 1 or 2 sets of numbers seperated by a .
            StatementList stmtList = new StatementList(log);

            stmtList.Name = name;

            OrComparison isDotOrEnd = new OrComparison(log);

            isDotOrEnd.Add(end);
            ICharComparison isDot = new CharComparison(log, options, '.');

            isDotOrEnd.Add(isDot);
            PatternComparison patternComp = new PatternComparison(log, options);

            patternComp.MinLength     = 1;
            patternComp.EndComparison = isDotOrEnd;

            // All characters must be between 0 and 9
            var allCharacters = new ComparisonRange <IComparisonWithAdvance>();

            allCharacters.Range.Min = 1;
            var isNumber = new CharDelegateComparison(log, Char.IsNumber);

            allCharacters.Comparison = isNumber;
            patternComp.AddComparisonRange(allCharacters);

            // List consisting of 1 or 2 items seperated by .
            DelimitedListComparison functionName = new DelimitedListComparison(log, options, patternComp, isDot);

            functionName.MinAmount = 1;
            functionName.MaxAmount = 2;
            stmtList.Add(functionName);
            return(stmtList);
        }
예제 #14
0
        // E.g.
        // class.Function _ \r\n (p1, p2, class.Function2( _\r\n p1,p2 ) )
        // or
        // class.FunctionWithNoArgs
        // or
        // variableName
        public static IComparisonWithAdvance CreateVBScriptFunctionOrVar(
            Options options,
            IComparisonWithAdvance vbScriptConcatCommaOrWhitespaceOrEnd,
            IComparison vbScriptKeywords,
            string name = null,
            ILog log    = null)
        {
            if (options == null)
            {
                options = CreateVBScriptParserOptions(log);
            }
            if (vbScriptConcatCommaOrWhitespaceOrEnd == null)
            {
                vbScriptConcatCommaOrWhitespaceOrEnd = CreateVBScriptConcatCommaOrWhitespaceOrEnd(log);
            }
            if (vbScriptKeywords == null)
            {
                vbScriptKeywords = TokenComparison.VBScriptKeywords(log, options);
            }

            if (options.SkipLineWrapOperation == null)
            {
                Parser.ThrowParseError("The line wrap comparison has not been specified. This is mandatory.");
            }

            // This requires more than one statement
            StatementList statementList = new StatementList(log);

            statementList.Name = name;

            // Function
            CharComparison isDot             = new CharComparison(log, options, '.');
            OrComparison   isDotOrParenOrEnd = new OrComparison(log);

            isDotOrParenOrEnd.Add(isDot);
            isDotOrParenOrEnd.Add(vbScriptConcatCommaOrWhitespaceOrEnd);
            CharComparison openParen = new CharComparison(log, options, '(');

            isDotOrParenOrEnd.Add(openParen);
            var identifier = TokenComparison.CreateIdentifier(
                options,
                end: isDotOrParenOrEnd,
                exclusion: vbScriptKeywords,
                name: "identifier",
                log: log);

            // List of identifiers seperated by . which will include classes/namespaces
            var sub = TokenComparison.CreateListOfTokens(
                options,
                token: identifier,
                segment: isDot,
                name: "sub",
                log: log);

            statementList.Add(sub);

            // Skip whitespace
            {
                var skip = SkipWhitespace(log);
                skip.Name = "SkipWhitespace1";
                statementList.Add(skip);
            }

            // Skip the line wrap character
            statementList.Add(options.SkipLineWrapOperation);

            // Function parenthesis and arguments
            CharComparison close = new CharComparison(log, options, ')');

            // Either the end (and don't advance past it), or function argument list
            OrComparison isEndOrArgumentList = new OrComparison(log);

            isEndOrArgumentList.Name = "IsEndOrArgumentList";
            isEndOrArgumentList.Add(new CompareNoAdvance(log, vbScriptConcatCommaOrWhitespaceOrEnd));
            isEndOrArgumentList.Add(new NestedOpenCloseComparison(log, openParen, close));

            statementList.Add(isEndOrArgumentList);

            return(statementList);
        }
예제 #15
0
        //TODO: this doesn't work!
        //TODO: this only understands '*' at the moment. '?' would be useful
        public static IComparisonWithAdvance WildcardComparison(
            string wildcardStr,
            ILog log,
            Options options)
        {
            // The list of parser statements
            StatementList mainStatements = new StatementList(log);

            // Length of the input string
            var len = wildcardStr.Length;

            // The current fixed string that we are building up
            var currentString = new StringBuilder();

            // Add the current string as a string comparison
            Action ConditionalAddCurrentString = () =>
            {
                if (currentString.Length > 0)
                {
                    mainStatements.Add(new StringComparison(log, options, currentString.ToString(), null));
                    currentString.Clear();
                }
            };

            for (int i = 0; i < len; ++i)
            {
                var chr = wildcardStr[i];

                // * means match all
                if (chr == '*')
                {
                    // Escape an asterix?
                    if (++i < len)
                    {
                        var nextChar = wildcardStr[i];
                        if (nextChar == '*')
                        {
                            currentString.Append('*');
                        }

                        else
                        {
                            // Match the string so far
                            ConditionalAddCurrentString();
                            // Skip till we find the next character in the sequence (nextChr)
                            mainStatements.Add(new AdvanceUntilComparison(log, new CharComparison(log, options, nextChar)));
                        }
                    }
                    else
                    {
                        // We've reached the end of the wild character string and the last character is an asterix
                        // All we can do in this case is advance until the end of the string
                        // You wouldn't really use this if you had additional comparisons after this, because it wouldn't
                        // work as expected (it wouldn't match because after this you would be at the end of the string)
                        //sidtodo not tested
                        mainStatements.Add(new AdvanceToTheEnd(log));
                    }
                }

                else
                {
                    currentString.Append(chr);
                }
            }

            ConditionalAddCurrentString();

            return(mainStatements);
        }
예제 #16
0
        // From "response.write blah"
        // To "response.write(blah)"
        public static string AddParenthesisToFunctionCalls(
            ILog log,
            string input,
            out int numMatches,
            Action <Action <string, IAssertion> > fCreateAssertList = null)
        {
            // The parser object that will do the work
            Parser parser = new Parser(log);

            //// Parser options
            // This create a special line wrap
            Options parserOptions = TokenComparison.CreateVBScriptParserOptions(log);

            CharDelegateComparison isWhitespace   = new CharDelegateComparison(log, Char.IsWhiteSpace);
            IOperation             skipWhitespace = TokenComparison.SkipWhitespace(log);

            // State
            List <State> stateList = VBScriptState(log, parserOptions);

            // The main statement list
            StatementList mainStatements = new StatementList(log);

            // Capturing
            Dictionary <string, Capture> capturing = new Dictionary <string, Capture>();

            // Function name capture
            Capture funcNameCapture = new Capture(log);

            funcNameCapture.Name = "FunctionName";
            capturing.Add("funcName", funcNameCapture);
            mainStatements.Add(funcNameCapture);

            // Function name capture statements
            var           vbScriptKeywords          = TokenComparison.VBScriptKeywords(log, parserOptions);
            StatementList funcNameCaptureStatements = new StatementList(log);

            funcNameCapture.Comparison          = funcNameCaptureStatements;
            funcNameCaptureStatements.Exclusion = vbScriptKeywords;

            //// Starts with whitespace or is the start of the input string
            StartOfInputStringComparison isStartOfString            = new StartOfInputStringComparison(log);
            OrComparison startsWithWhitespaceOrIsStartOfInputString = new OrComparison(log);

            startsWithWhitespaceOrIsStartOfInputString.Add(isStartOfString);
            startsWithWhitespaceOrIsStartOfInputString.Add(isWhitespace);
            funcNameCaptureStatements.Add(startsWithWhitespaceOrIsStartOfInputString);

            // Skip whitespace
            funcNameCaptureStatements.Add(skipWhitespace);

            //// Function/sub name.
            //// For example: class1.Func or class1.Func"string arg"
            funcNameCaptureStatements.Add(TokenComparison.CreateVBScriptSub(parserOptions, name: null, log: log));

            //// Arguments list
            // Skip whitespace and don't capture it
            mainStatements.Add(skipWhitespace);

            // Capture
            Capture argListCapture = new Capture(log);

            capturing.Add("funcArgs", argListCapture);
            mainStatements.Add(argListCapture);

            // Function argument list capture statements
            StatementList argListCaptureStatements = new StatementList(log);

            argListCapture.Comparison = argListCaptureStatements;

            // Skip the line wrap (and capture it)
            argListCaptureStatements.Add(parserOptions.SkipLineWrapOperation);

            // Concatenated single arguments
            // Delimited by whitespace, comma & or +
            var vbScriptConcatCommaOrWhitespaceOrEnd =
                TokenComparison.CreateVBScriptConcatCommaOrWhitespaceOrEnd(log, parserOptions);

            // Numbers
            var numberComparison =
                TokenComparison.CreateNumber(parserOptions, vbScriptConcatCommaOrWhitespaceOrEnd, "ArgsNumber", log: log);
            // VB script quoted strings
            var quotedText = TokenComparison.CreateVBScriptQuotedString(parserOptions, name: "Args quoted text", log: log);
            // VB script function which could include arguments, or a variable name
            var VbScriptFunctionOrVar = TokenComparison.CreateVBScriptFunctionOrVar(
                parserOptions,
                vbScriptConcatCommaOrWhitespaceOrEnd,
                vbScriptKeywords,
                "VbScriptFunctionOrVar",
                log: log);

            // Individual arguments
            // Types
            OrComparison individualArgumentTypes = new OrComparison(log);

            individualArgumentTypes.Add(numberComparison);
            individualArgumentTypes.Add(quotedText);
            individualArgumentTypes.Add(VbScriptFunctionOrVar);

            // List delimiter for the individual arguments
            OrComparison vbScriptConcactOrComma = new OrComparison(log);

            {
                var matchAmpersand = new CharComparison(log, parserOptions, '&');
                matchAmpersand.Name = "ArgsStringConcatenation";
                vbScriptConcactOrComma.Add(matchAmpersand);
            }
            vbScriptConcactOrComma.Add(new CharComparison(log, parserOptions, '+'));
            var isComma = new CharComparison(log, parserOptions, ',');

            vbScriptConcactOrComma.Add(isComma);

            DelimitedListComparison individualArgumentList = new DelimitedListComparison(
                log, parserOptions, individualArgumentTypes, seperator: vbScriptConcactOrComma
                );

            individualArgumentList.MinAmount = 1;
            individualArgumentList.ItemTrim  = skipWhitespace;

            // The argument list - comma seperated list of function arguments
            DelimitedListComparison argumentList = new DelimitedListComparison(
                log, parserOptions, individualArgumentList, seperator: isComma
                );

            argumentList.Name      = "ArgumentList";
            argumentList.MinAmount = 1;
            argumentList.ItemTrim  = skipWhitespace;
            argListCaptureStatements.Add(argumentList);

            Action <RunState> InitRunState = null;

            if (fCreateAssertList != null)
            {
                InitRunState = (runState) => {
                    fCreateAssertList(runState.AddAssertion);
                };
            }

            const string replaceWith = "'funcName'('funcArgs')";
            string       replaced    = parser.Replace(
                input, replaceWith, mainStatements, capturing, stateList, out numMatches, null, InitRunState
                );

            return(replaced);
        }
예제 #17
0
        //TODO this is not fully tested. needs a new feature to allow repeats
        // - "repeated comparison"
        // - also doesn't work inside quotes. needs a state
        //  I think state ment list should have a state member
        // <script ..>function blah end function </script>
        public static string WrapFunctionsInScriptBlock(
            ILog log,
            string input)
        {
            // The parser object that will do the work
            Parser parser = new Parser(log);

            //// Parser options
            Options parserOptions = new Options(log);

            parserOptions.CaseSensitive = false;

            // Line wrap
            parserOptions.LineWrap = TokenComparison.VBScriptLineWrap(
                log,
                parserOptions);

            // State
            List <State> stateList = VBScriptState(log, parserOptions);

            // The main statement list
            StatementList mainStatements = new StatementList(log);

            // Begins with whitespace (don't capture the space)
            CharDelegateComparison isWhitespace = new CharDelegateComparison(log, Char.IsWhiteSpace);

            mainStatements.Add(isWhitespace);

            // Skip whitespace (don't capture the space)
            mainStatements.Add(TokenComparison.SkipWhitespace(log));

            // Capturing
            Dictionary <string, Capture> capturing = new Dictionary <string, Capture>();

            // The capture
            Capture capture = new Capture(log);

            capture.Name = "capture";
            capturing.Add("functionDefinition", capture);
            mainStatements.Add(capture);

            // Capture statements
            StatementList captureStatements = new StatementList(log);

            capture.Comparison = captureStatements;

            // Function or sub
            OrComparison functionOrSub = new OrComparison(log);

            functionOrSub.Add(new StringComparison(log, parserOptions, "function"));
            functionOrSub.Add(new StringComparison(log, parserOptions, "sub"));
            captureStatements.Add(functionOrSub);

            // Whitespace
            captureStatements.Add(isWhitespace);

            // Line wrap
            captureStatements.Add(parserOptions.SkipLineWrapOperation);

            //// Name of function/sub
            // End is either whitespace or (
            OrComparison whiteSpaceOrOpenParen = new OrComparison(log);

            whiteSpaceOrOpenParen.Add(isWhitespace);
            whiteSpaceOrOpenParen.Add(new CharComparison(log, parserOptions, '('));

            var funcName = TokenComparison.CreateIdentifier(
                parserOptions,
                end: whiteSpaceOrOpenParen,
                exclusion: null,
                name: null,
                log: log);

            captureStatements.Add(funcName);

            //// Find the end of the sub/function
            StatementList functionEnd = new StatementList(log);

            // Whitespace
            functionEnd.Add(isWhitespace);

            // 'end'
            functionEnd.Add(new StringComparison(log, parserOptions, "end"));

            // Whitespace
            functionEnd.Add(isWhitespace);

            // Skip the line wrap
            functionEnd.Add(parserOptions.SkipLineWrapOperation);

            // Function or sub
            functionEnd.Add(functionOrSub);

            // Whitespace or %>
            OrComparison whitespaceOrPageDirective = new OrComparison(log);

            whitespaceOrPageDirective.Add(isWhitespace);
            whitespaceOrPageDirective.Add(new CompareNoAdvance(log, new StringComparison(log, parserOptions, "%>")));

            functionEnd.Add(whitespaceOrPageDirective);

            // Skip till find the function name
            captureStatements.Add(new AdvanceUntilComparison(log, functionEnd));

            //const string replaceWith = "'funcName'('funcArgs')";
            string replaceWith = string.Format(
                "{0}<script language=\"VB\" runat=\"Server\">{0}'functionDefinition'{0}</script>{0}",
                Environment.NewLine);
            string replaced = parser.Replace(input, replaceWith, mainStatements, capturing, stateList);

            return(replaced);
        }