//~V public State( IComparisonWithAdvance enable, IComparisonWithAdvance disable, bool initialState) { m_Enable = enable; m_Disable = disable; IsEnabled = initialState; }
//~V public DelimitedListComparison( ILog log, Options options, IComparisonWithAdvance comp, IComparisonWithAdvance seperator) : base(log, options) { m_Comparison = comp; m_Seperator = seperator; }
//~V public AdvanceUntilComparison( ILog log, IComparisonWithAdvance comp, bool forwards = true, IComparisonWithAdvance continueComp = null) : base(log) { m_Forwards = forwards; m_Comparison = comp; m_ContinueComp = continueComp; }
//~V public NestedOpenCloseComparison( ILog log, IComparisonWithAdvance open, IComparisonWithAdvance close, string name = null) : base(log) { m_Open = open; m_Close = close; Name = name; }
public string Replace( string input, string replaceWith, IComparisonWithAdvance mainComparison, Dictionary <string, Capture> capturing, List <State> stateList, out int numMatches, Func <RunState, string, int, string> ReplaceFunc = null, Action <RunState> InitRunState = null) { return(Process(input, replaceWith, mainComparison, capturing, stateList, true, out numMatches, ReplaceFunc, InitRunState)); }
public static IComparisonWithAdvance CreateListOfTokens( Options options, IComparisonWithAdvance token, IComparisonWithAdvance segment, string name = null, ILog log = null) { DelimitedListComparison list = new DelimitedListComparison(log, options, token, segment); list.MinAmount = 1; list.Name = name; return(list); }
// 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); }
// 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); }
// 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); }
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); }
//~V public AdvanceIfOperation( ILog log, IComparisonWithAdvance comp) : base(log) { m_Comparison = comp; }
protected override bool CompareAndAdvanceImp( string str, int firstIndex, int depth, RunState runState, out int index) { index = -1; // Statement index int stmtIndex; bool failedMatch = false; int pos; int depthPlusOne = depth + 1; for (stmtIndex = 0, pos = firstIndex; !failedMatch && stmtIndex < m_Statements.Count; ++stmtIndex) { // Key nuance design decision. It's OK to perform operations/comparisons even if we have reached the end of // the string. This is necessary for things like setting variables, comparing against the end of the // string e.t.c. No doubt there will be bugs presuming this isn't the case but I've only realised // retrospectively that this is a requirement. IStatement stmt = m_Statements[stmtIndex]; if (stmt is IOperation) { IOperation op = (IOperation)stmt; op.Perform(str, pos, depthPlusOne, runState, out pos); } else { if (stmt is IComparisonWithAdvance) { IComparisonWithAdvance comp = (IComparisonWithAdvance)stmt; int previousPos = pos; if (!comp.CompareAndAdvance(str, pos, depthPlusOne, runState, out pos)) { // This part of the pattern match failed. Start again (using the outer loop/index) but at // the next character failedMatch = true; } } else if (stmt is IComparison) { //sidtodo not sure about this - I'm not sure the design of the statement classes is right IComparison comp = (IComparison)stmt; if (!comp.Compare(str, pos, depthPlusOne, runState)) { failedMatch = true; } } } } // Got this far without a match? if (!failedMatch) { // Did we execute all statements? if (stmtIndex == m_Statements.Count) { // Exclusions? if (Exclusion != null) { string subStr = str.Substring(firstIndex, pos - firstIndex); failedMatch = Exclusion.Compare(subStr, 0, depthPlusOne, runState); } if (!failedMatch) { // Successful match index = pos; } } else { // We did not complete all of the comparisons // Go to the next character // Potentially a speedup here? If we reach the end of the input string but cannot complete all // comparisons then copy the remaining string rather than trying to do any more comparisons? failedMatch = true; } } return(!failedMatch); }
// 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); }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // The order in which you add is very important because it is one which matches that controls how far to advance // after the match. Comparisons are executed in the order in which they are added. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Add( IComparisonWithAdvance comp) { m_ComparisonList.Add(comp); }
private string Process( string input, string replaceWith, IComparisonWithAdvance mainComparison, Dictionary <string, Capture> capturing, List <State> stateList, bool replace, out int numMatches, Func <RunState, string, int, string> ReplaceFunc = null, Action <RunState> InitRunState = null) { // The replaced string StringBuilder replaced = new StringBuilder(); var runState = new RunState(); if (InitRunState != null) { InitRunState(runState); } numMatches = 0; // Iterate the input string character by character for (int outerIndex = 0; outerIndex < input.Length;) { runState.Clear(); runState.BeginPos = outerIndex; #if DEBUG int dbgInitialOuterIndex = outerIndex; if (dbgInitialOuterIndex == 1374) { Misc.Break(); } string dbgInputPortion = input.Substring(dbgInitialOuterIndex, Math.Min(20, input.Length - dbgInitialOuterIndex)); #endif //// All states enabled? int afterState = outerIndex; bool stateEnabled; if (stateList != null) { for (; ;) { // If the state becomes enabled, check that directly afterwards we don't encounter a close state // Imagine we have a state which opens on '<%' and closes on '%>' and the input string is '<%%> ...' // We enable then instantly disable again. We only stop trying to update the state when updating the // state doesn't advance the position int curIndex = afterState; stateEnabled = UpdateState(stateList, input, afterState, runState, out afterState); if (curIndex == afterState) { // Updating the state did not advance the position - continue with the replace break; } } } else { stateEnabled = true; } // Copy the state string (if any) if (afterState != outerIndex) { string stateString = input.Substring(outerIndex, afterState - outerIndex); replaced.Append(stateString); } // Update the main index outerIndex = afterState; if (outerIndex < input.Length) { bool matched = stateEnabled; if (stateEnabled) { // Run the statements beginning from 'afterState' int innerIndex = outerIndex; int afterMatch; matched = mainComparison.CompareAndAdvance(input, innerIndex, 0, runState, out afterMatch); if (matched) { // The begin of the matching string runState.BeginPos = outerIndex; ++numMatches; // This is a match! :) // Do the replace string iterReplaced = ReplaceMatch(replaceWith, ReplaceFunc, capturing, runState, input, afterMatch); replaced.Append(iterReplaced); LogMatchReplace( input, innerIndex, afterMatch, iterReplaced, capturing); // Update the outer index and continue iterating from there outerIndex = afterMatch; } } if (!matched) { if (replace) { // Match failed: copy this character to the output string and move on to the next one (if this is replace mode) replaced.Append(input[outerIndex]); } ++outerIndex; } } } return(replaced.ToString()); }