Skip to content

Commit

Permalink
Merge pull request #77 from m-ender/feature/match-index
Browse files Browse the repository at this point in the history
Add match-index feature
  • Loading branch information
m-ender authored Apr 12, 2019
2 parents 9992af1 + 2cf1d4e commit 6a7231c
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 17 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.2.0

- Substitution syntax: added `$:&` and `$;&` to generate the match's index from the left and right, respectively. `<>[]` modifiers can be (ab)used for some off-by-one shenanigans.

## 1.1.1

This version switches from .NET Framework to .NET Core.
Expand Down
47 changes: 38 additions & 9 deletions Retina/Retina/Replace/Nodes/DynamicElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ namespace Retina.Replace.Nodes
public class DynamicElement : Node
{
public Node Child { get; set; }
private bool CyclicMatches;
private readonly bool CyclicMatches;

private History History;
private readonly History History;

public DynamicElement(Node child, History history, bool cyclicMatches)
{
Expand Down Expand Up @@ -49,8 +49,8 @@ private string ResolveChild(string input, List<MatchContext> matches, List<Match
[<>[\]]
)?
(
(?<modifier> # Capture count or random capture.
[#?]
(?<modifier> # Generic capture modifier.
[#:;?]
)?
(
(?<entireMatch> # $& and $0 refer to the entire match.
Expand Down Expand Up @@ -97,30 +97,50 @@ private string ResolveChild(string input, List<MatchContext> matches, List<Match
if (parserMatch.Groups["modifier"].Success)
modifier = parserMatch.Groups["modifier"].Value[0];

// Will be set to false if an adjacency modifier shifts
// the context to a separator.
bool contextIsMatch = true;
MatchContext match;
switch (adjacent)
{
case '<':
match = separators[index];
contextIsMatch = false;
break;
case '>':
match = separators[index+1];
++index;
match = separators[index];
contextIsMatch = false;
break;
case '[':
if (CyclicMatches)
match = matches[(index - 1 + matches.Count) % matches.Count];
{
index = (index - 1 + matches.Count) % matches.Count;
match = matches[index];
}
else if (index == 0)
return "";
else
match = matches[index - 1];
{
--index;
match = matches[index];
}

break;
case ']':
if (CyclicMatches)
match = matches[(index + 1) % matches.Count];
{
index = (index + 1) % matches.Count;
match = matches[index];
}
else if (index == matches.Count - 1)
return "";
else
match = matches[index + 1];
{
++index;
match = matches[index];
}

break;
default:
match = matches[index];
Expand All @@ -137,6 +157,15 @@ private string ResolveChild(string input, List<MatchContext> matches, List<Match
// Subtract 1 to account for group 0.
value = (match.Regex.GetGroupNumbers().Count(i => match.Match.Groups[i].Success) - 1).ToString();
break;
case ':':
value = index.ToString();
break;
case ';':
{
int maxIndex = (contextIsMatch ? matches : separators).Count - 1;
value = (maxIndex - index).ToString();
break;
}
case '?':
int[] groups = match.Regex.GetGroupNumbers();
// Offset by 1 to account for group 0.
Expand Down
6 changes: 3 additions & 3 deletions Retina/Retina/Replace/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ public ParserError(string message) : base(message) { }
public ParserError(string message, Exception innerException) : base(message, innerException) { }
}

private bool CyclicMatches;
private readonly bool CyclicMatches;

private List<Token> Tokens;
private int Current;

private History History;
private readonly History History;

public Parser(History history)
{
Expand Down Expand Up @@ -70,7 +70,7 @@ private void Tokenize(string replacement)
(
(?<numbered> # $n are numbered groups.
[<>[\]]? # Pull the value from an adjacent separator or match.
[#?]? # Capture count or random capture.
[#:;?]? # Generic capture modifiers.
(?:\d+|&) # & is an alias for 0.
)
|
Expand Down
7 changes: 3 additions & 4 deletions Retina/Retina/Stages/AtomicStage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ public abstract class AtomicStage : Stage
private int PatternIndex;

protected History History;
private bool RegisterWithHistory;
private int HistoryIndex;
private readonly bool RegisterWithHistory;
private readonly int HistoryIndex;

protected AtomicStage(Config config, History history, bool registerByDefault) : base(config) {
History = history;
Expand Down Expand Up @@ -203,8 +203,7 @@ private void LimitMatches()
if (Config.SingleRandomMatch && Matches.Count > 0)
{
var chosenMatch = Matches[Random.RNG.Next(Matches.Count)];
Matches = new List<MatchContext>();
Matches.Add(chosenMatch);
Matches = new List<MatchContext> { chosenMatch };
}
}

Expand Down
26 changes: 25 additions & 1 deletion Retina/RetinaTest/ReplacerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,31 @@ public void TestCaptureCount()
AssertProgram(new TestSuite { Sources = { "(a|b)+(cd)|d(e)f", "$#0${#0}${#00}" }, TestCases = { { "abcd\ndef", "222\n111" } } });
}

[TestMethod]
public void TestMatchIndex()
{
AssertProgram(new TestSuite { Sources = { @"\w+", "$:0" }, TestCases = { { "123,321,123,321", "0,1,2,3" } } });
AssertProgram(new TestSuite { Sources = { @"\w+", "$:&" }, TestCases = { { "123,321,123,321", "0,1,2,3" } } });

AssertProgram(new TestSuite { Sources = { @"\w+", "$;0" }, TestCases = { { "123,321,123,321", "3,2,1,0" } } });
AssertProgram(new TestSuite { Sources = { @"\w+", "$;&" }, TestCases = { { "123,321,123,321", "3,2,1,0" } } });

// Test interaction with adjacency modifiers
AssertProgram(new TestSuite { Sources = { @"\w+", "$<:&" }, TestCases = { { "123,321,123,321", "0,1,2,3" } } });
AssertProgram(new TestSuite { Sources = { @"\w+", "$>:&" }, TestCases = { { "123,321,123,321", "1,2,3,4" } } });
AssertProgram(new TestSuite { Sources = { @"\w+", "$[:&" }, TestCases = { { "123,321,123,321", ",0,1,2" } } });
AssertProgram(new TestSuite { Sources = { @"y`\w+", "$[:&" }, TestCases = { { "123,321,123,321", "3,0,1,2" } } });
AssertProgram(new TestSuite { Sources = { @"\w+", "$]:&" }, TestCases = { { "123,321,123,321", "1,2,3," } } });
AssertProgram(new TestSuite { Sources = { @"y`\w+", "$]:&" }, TestCases = { { "123,321,123,321", "1,2,3,0" } } });

AssertProgram(new TestSuite { Sources = { @"\w+", "$<;&" }, TestCases = { { "123,321,123,321", "4,3,2,1" } } });
AssertProgram(new TestSuite { Sources = { @"\w+", "$>;&" }, TestCases = { { "123,321,123,321", "3,2,1,0" } } });
AssertProgram(new TestSuite { Sources = { @"\w+", "$[;&" }, TestCases = { { "123,321,123,321", ",3,2,1" } } });
AssertProgram(new TestSuite { Sources = { @"y`\w+", "$[;&" }, TestCases = { { "123,321,123,321", "0,3,2,1" } } });
AssertProgram(new TestSuite { Sources = { @"\w+", "$];&" }, TestCases = { { "123,321,123,321", "2,1,0," } } });
AssertProgram(new TestSuite { Sources = { @"y`\w+", "$];&" }, TestCases = { { "123,321,123,321", "2,1,0,3" } } });
}

[TestMethod]
public void TestRandomCapture()
{
Expand All @@ -235,7 +260,6 @@ public void TestRandomCapture()
});
}


[TestMethod]
public void TestRandomGroup()
{
Expand Down

0 comments on commit 6a7231c

Please sign in to comment.