public void ShouldUnnegateNegatedCharacterClass() { // Arrange var characters = new List <RegexNode> { new CharacterNode('a'), new CharacterNode('b'), new CharacterNode('c') }; var characterSet = new CharacterClassCharacterSetNode(characters); var subtractionCharacterSet = new CharacterClassCharacterSetNode(new CharacterNode('a')); var subtraction = new CharacterClassNode(subtractionCharacterSet, false); var characterClass = new CharacterClassNode(characterSet, subtraction, true); var childNodes = new List <RegexNode> { new CharacterNode('x'), characterClass, new CharacterNode('y') }; var root = new ConcatenationNode(childNodes); var target = new CharacterClassNegationMutator(); // Act var result = target.ApplyMutations(characterClass, root); // Assert var mutation = result.ShouldHaveSingleItem(); mutation.OriginalNode.ShouldBe(characterClass); mutation.ReplacementNode.ToString().ShouldBe("[abc-[a]]"); mutation.ReplacementPattern.ShouldBe("x[abc-[a]]y"); mutation.DisplayName.ShouldBe("Regex character class negation mutation"); mutation.Description.ShouldBe("Character class \"[^abc-[a]]\" was replaced with \"[abc-[a]]\" at offset 1."); }
public void ToStringOnNegatedCharacterClassNodeWithCharacterSetShouldReturnCharactersBetweenBrackets() { // Arrange var characterSet = new CharacterClassCharacterSetNode(new List <RegexNode> { new CharacterNode('a'), new CharacterNode('b'), new CharacterNode('c') }); var target = new CharacterClassNode(characterSet, true); // Act var result = target.ToString(); // Assert result.ShouldBe("[^abc]"); }
public void CharacterSetShouldReturnOriginalCharacterSet() { // Arrange var characterSet = new CharacterClassCharacterSetNode(new List <RegexNode> { new CharacterNode('a'), new CharacterNode('b'), new CharacterNode('c') }); var target = new CharacterClassNode(characterSet, false); // Act var result = target.CharacterSet; // Assert result.ShouldBe(characterSet); }
public void SubtractionShouldReturnNullIfNoSubtraction() { // Arrange var characterSet = new CharacterClassCharacterSetNode(new List <RegexNode> { new CharacterNode('a'), new CharacterNode('b'), new CharacterNode('c') }); var target = new CharacterClassNode(characterSet, false); // Act var result = target.Subtraction; // Assert result.ShouldBeNull(); }
public void CharacterSetSpanShouldStartAt2IfNegatedIsTrue() { // Arrange var characterSet = new CharacterClassCharacterSetNode(new List <RegexNode> { new CharacterNode('a'), new CharacterNode('b'), new CharacterNode('c') }); _ = new CharacterClassNode(characterSet, true); // Act var(Start, _) = characterSet.GetSpan(); // Assert Start.ShouldBe(2); }
public void ToStringOnCharacterClassNodeWithSubtractionSubtractionBetweenBrackets() { // Arrange var subtractionCharacterSet = new CharacterClassCharacterSetNode(new CharacterNode('a')); var subtraction = new CharacterClassNode(subtractionCharacterSet, false); var characterSet = new CharacterClassCharacterSetNode(new List <RegexNode> { new CharacterNode('a'), new CharacterNode('b'), new CharacterNode('c') }); var target = new CharacterClassNode(characterSet, subtraction, false); // Act var result = target.ToString(); // Assert result.ShouldBe("[abc-[a]]"); }
public void SubtractionShouldReturnOriginalSubtraction() { // Arrange var subtractionCharacterSet = new CharacterClassCharacterSetNode(new CharacterNode('a')); var subtraction = new CharacterClassNode(subtractionCharacterSet, false); var characterSet = new CharacterClassCharacterSetNode(new List <RegexNode> { new CharacterNode('a'), new CharacterNode('b'), new CharacterNode('c') }); var target = new CharacterClassNode(characterSet, subtraction, false); // Act var result = target.Subtraction; // Assert result.ShouldBe(subtraction); }
public void SubtractionSpanShouldStartAfterCharacterSetAndDash() { // Arrange var subtractionCharacterSet = new CharacterClassCharacterSetNode(new CharacterNode('a')); var subtraction = new CharacterClassNode(subtractionCharacterSet, false); var characterSet = new CharacterClassCharacterSetNode(new List <RegexNode> { new CharacterNode('a'), new CharacterNode('b'), new CharacterNode('c') }); _ = new CharacterClassNode(characterSet, subtraction, false); // Act var(Start, _) = subtraction.GetSpan(); // Assert Start.ShouldBe(5); }
/// <summary> /// Parse a character class subtraction inside a character class. /// Throws an exception if the outer character class is not closed with a closing ']' or if the subtraction is not the last element in the outer character class. /// </summary> /// <returns></returns> private CharacterClassNode ParseCharacterClassSubtraction() { CharacterClassNode subtraction = ParseCharacterClass(); // No closing ']' for outer character class. if (CharsRight() == 0) { throw MakeException(RegexParseError.UnterminatedCharacterClass); } // Subtraction must be the last element in a character class. if (RightChar() != ']') { throw MakeException(RegexParseError.SubtractionMustBeLast); } return(subtraction); }
public void ToStringOnCharacterClassNodeWithPrefixShouldReturnPrefixBeforeCharacterClass() { // Arrange var comment = new CommentGroupNode("This is a comment."); var characterSet = new CharacterClassCharacterSetNode(new List <RegexNode> { new CharacterNode('a'), new CharacterNode('b'), new CharacterNode('c') }); var target = new CharacterClassNode(characterSet, false) { Prefix = comment }; // Act var result = target.ToString(); // Assert result.ShouldBe("(?#This is a comment.)[abc]"); }
public void CopyingCharacterClassNodeShouldCopyNegation() { // Arrange var characterSet = new CharacterClassCharacterSetNode(new List <RegexNode> { new CharacterNode('a'), new CharacterNode('b'), new CharacterNode('c') }); var replacementCharacterSet = new CharacterClassCharacterSetNode(new List <RegexNode> { new CharacterNode('b'), new CharacterNode('c') }); var target = new CharacterClassNode(characterSet, true); // Act // ReplaceNode returns a copy of the current node. var result = target.ReplaceNode(characterSet, replacementCharacterSet); // Assert CharacterClassNode characterClassNode = result.ShouldBeOfType <CharacterClassNode>(); characterClassNode.Negated.ShouldBe(target.Negated); }
/// <summary> /// Parse characters following a dash '-' in a character class [...]. It could be part of a character range x-y or character class subtraction -[...] /// </summary> /// <param name="characterSet">The set of characters already in the characterclass.</param> /// <param name="previousNode">The character class element left of the dash '-'.</param> /// <param name="subtraction">A character class will be saved here if the dash '-' is part of a character class subtraction.</param> /// <returns></returns> private RegexNode ParseCharacterClassDash(List <RegexNode> characterSet, RegexNode previousNode, ref CharacterClassNode subtraction) { // '-' at the and of a character class is a literal character. if (RightChar() == ']') { characterSet.Add(previousNode); return(new CharacterNode('-')); } // Subtraction [...-[...]] else if (RightChar() == '[') { MoveRight(); subtraction = ParseCharacterClassSubtraction(); return(previousNode); } // Could be range. else { // '-' after character class shorthand, unicode category/block or range is a literal character. if (previousNode is CharacterClassShorthandNode || previousNode is UnicodeCategoryNode || previousNode is CharacterClassRangeNode) { characterSet.Add(previousNode); return(new CharacterNode('-')); } // Range. else { return(ParseCharacterRange(previousNode)); } } }
/// <summary> /// Parse a character class [...] in response to an opening '['. /// </summary> private CharacterClassNode ParseCharacterClass() { if (CharsRight() == 0) { throw MakeException(RegexParseError.UnterminatedCharacterClass); } char ch; RegexNode previousNode; CharacterClassNode subtraction = null; var negated = false; var characterSet = new List <RegexNode>(); if (RightChar() == '^') { MoveRight(); negated = true; } previousNode = FirstCharacterClassElement(); while (CharsRight() > 1 && (ch = RightChar()) != ']') { MoveRight(); switch (ch) { // Could be range or subtraction. case '-': previousNode = ParseCharacterClassDash(characterSet, previousNode, ref subtraction); break; // Escape character, shorthand or unicode category. case '\\': characterSet.Add(previousNode); previousNode = ParseBasicBackslash(); break; // Literal character default: characterSet.Add(previousNode); previousNode = new CharacterNode(ch); break; } } characterSet.Add(previousNode); if (RightChar() == ']') { MoveRight(); var characterSetNode = new CharacterClassCharacterSetNode(characterSet); if (subtraction == null) { return(new CharacterClassNode(characterSetNode, negated)); } return(new CharacterClassNode(characterSetNode, subtraction, negated)); } // No closing ']'. throw MakeException(RegexParseError.UnterminatedCharacterClass); }