Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,102 @@ public void BinaryToUnaryNumbersTest()
var output = transformer.Transform(input);
Assert.Equal(expectedOutput, output);
}

/// <summary>
/// Tests that terminating rules stop the algorithm immediately when applied.
/// </summary>
[Fact]
public void TerminatingRuleStopsAlgorithmTest()
{
var rules = new SubstitutionRule[]
{
("a", "b", 0, false), // Regular rule: a -> b
("b", "STOP", 0, true), // Terminating rule: b -> STOP (should stop here)
("STOP", "c", 0, false), // This rule should never be applied
};
var transformer = new TextTransformer(rules);
var input = "a";
var expectedOutput = "STOP";
var output = transformer.Transform(input);
Assert.Equal(expectedOutput, output);
}

/// <summary>
/// Tests that non-terminating rules continue processing.
/// </summary>
[Fact]
public void NonTerminatingRulesContinueProcessingTest()
{
var rules = new SubstitutionRule[]
{
("a", "b", 0, false), // Regular rule: a -> b
("b", "c", 0, false), // Regular rule: b -> c
("c", "d", 0, false), // Regular rule: c -> d
};
var transformer = new TextTransformer(rules);
var input = "a";
var expectedOutput = "d";
var output = transformer.Transform(input);
Assert.Equal(expectedOutput, output);
}

/// <summary>
/// Tests terminating rule with complex pattern.
/// </summary>
[Fact]
public void TerminatingRuleWithComplexPatternTest()
{
var rules = new SubstitutionRule[]
{
(@"(\d+)", "[$1]", 0, false), // Wrap numbers in brackets
(@"\[42\]", "FORTY-TWO", 0, true), // Terminating rule for [42]
(@"\[(\d+)\]", "NUM:$1", 0, false), // This should not be applied for [42]
};
var transformer = new TextTransformer(rules);
var input = "42";
var expectedOutput = "FORTY-TWO";
var output = transformer.Transform(input);
Assert.Equal(expectedOutput, output);
}

/// <summary>
/// Tests that terminating rules work with multiple matches in text.
/// </summary>
[Fact]
public void TerminatingRuleWithMultipleMatchesTest()
{
var rules = new SubstitutionRule[]
{
("x", "X", 0, false), // Regular rule: x -> X
("X", "TERMINATED", 0, true), // Terminating rule: X -> TERMINATED
("y", "Y", 0, false), // This rule should not be applied
};
var transformer = new TextTransformer(rules);
var input = "xyx"; // Should transform both x to X, then first X to TERMINATED and stop
var expectedOutput = "TERMINATEDyX"; // Both x converted to X, then first X terminated
var output = transformer.Transform(input);
Assert.Equal(expectedOutput, output);
}

/// <summary>
/// Tests the Wikipedia Markov algorithm example with terminating rule.
/// Example modified to include a terminating condition.
/// </summary>
[Fact]
public void WikipediaExampleWithTerminatingRuleTest()
{
var rules = new SubstitutionRule[]
{
("1", "0|", int.MaxValue), // "1" -> "0|" repeated forever
(@"\|0", "0||", int.MaxValue), // "\|0" -> "0||" repeated forever
("0", "", int.MaxValue), // "0" -> "" repeated forever
(@"\|\|\|\|\|", "FIVE", true), // Terminating rule: five bars -> FIVE
};
var transformer = new TextTransformer(rules);
var input = "101";
var expectedOutput = "FIVE"; // Should transform to ||||| then to FIVE and stop
var output = transformer.Transform(input);
Assert.Equal(expectedOutput, output);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,18 @@ int MaximumRepeatCount
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}

/// <summary>
/// <para>
/// Gets a value indicating whether this rule is terminating.
/// When a terminating rule is applied, the Markov algorithm stops execution.
/// </para>
/// <para></para>
/// </summary>
bool IsTerminating
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}
}
}
56 changes: 52 additions & 4 deletions csharp/Platform.RegularExpressions.Transformer/SubstitutionRule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,21 @@ public int MaximumRepeatCount
set;
}

/// <summary>
/// <para>
/// Gets or sets a value indicating whether this rule is terminating.
/// When a terminating rule is applied, the Markov algorithm stops execution.
/// </para>
/// <para></para>
/// </summary>
public bool IsTerminating
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set;
}

/// <summary>
/// <para>
/// Initializes a new <see cref="SubstitutionRule"/> instance.
Expand All @@ -113,12 +128,17 @@ public int MaximumRepeatCount
/// <para>A match timeout.</para>
/// <para></para>
/// </param>
/// <param name="isTerminating">
/// <para>A value indicating whether this rule is terminating.</para>
/// <para></para>
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SubstitutionRule(Regex matchPattern, string substitutionPattern, int maximumRepeatCount, RegexOptions? matchPatternOptions, TimeSpan? matchTimeout)
public SubstitutionRule(Regex matchPattern, string substitutionPattern, int maximumRepeatCount, RegexOptions? matchPatternOptions, TimeSpan? matchTimeout, bool isTerminating = false)
{
MatchPattern = matchPattern;
SubstitutionPattern = substitutionPattern;
MaximumRepeatCount = maximumRepeatCount;
IsTerminating = isTerminating;
OverrideMatchPatternOptions(matchPatternOptions ?? matchPattern.Options, matchTimeout ?? matchPattern.MatchTimeout);
}

Expand All @@ -144,8 +164,12 @@ public SubstitutionRule(Regex matchPattern, string substitutionPattern, int maxi
/// <para>A use default options.</para>
/// <para></para>
/// </param>
/// <param name="isTerminating">
/// <para>A value indicating whether this rule is terminating.</para>
/// <para></para>
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SubstitutionRule(Regex matchPattern, string substitutionPattern, int maximumRepeatCount, bool useDefaultOptions) : this(matchPattern, substitutionPattern, maximumRepeatCount, useDefaultOptions ? DefaultMatchPatternRegexOptions : (RegexOptions?)null, useDefaultOptions ? DefaultMatchTimeout : (TimeSpan?)null) { }
public SubstitutionRule(Regex matchPattern, string substitutionPattern, int maximumRepeatCount, bool useDefaultOptions, bool isTerminating = false) : this(matchPattern, substitutionPattern, maximumRepeatCount, useDefaultOptions ? DefaultMatchPatternRegexOptions : (RegexOptions?)null, useDefaultOptions ? DefaultMatchTimeout : (TimeSpan?)null, isTerminating) { }

/// <summary>
/// <para>
Expand All @@ -165,8 +189,12 @@ public SubstitutionRule(Regex matchPattern, string substitutionPattern, int maxi
/// <para>A maximum repeat count.</para>
/// <para></para>
/// </param>
/// <param name="isTerminating">
/// <para>A value indicating whether this rule is terminating.</para>
/// <para></para>
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SubstitutionRule(Regex matchPattern, string substitutionPattern, int maximumRepeatCount) : this(matchPattern, substitutionPattern, maximumRepeatCount, true) { }
public SubstitutionRule(Regex matchPattern, string substitutionPattern, int maximumRepeatCount, bool isTerminating = false) : this(matchPattern, substitutionPattern, maximumRepeatCount, true, isTerminating) { }

/// <summary>
/// <para>
Expand All @@ -182,8 +210,12 @@ public SubstitutionRule(Regex matchPattern, string substitutionPattern, int maxi
/// <para>A substitution pattern.</para>
/// <para></para>
/// </param>
/// <param name="isTerminating">
/// <para>A value indicating whether this rule is terminating.</para>
/// <para></para>
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SubstitutionRule(Regex matchPattern, string substitutionPattern) : this(matchPattern, substitutionPattern, 0) { }
public SubstitutionRule(Regex matchPattern, string substitutionPattern, bool isTerminating = false) : this(matchPattern, substitutionPattern, 0, isTerminating) { }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator SubstitutionRule(ValueTuple<string, string> tuple) => new SubstitutionRule(new Regex(tuple.Item1), tuple.Item2);
Expand All @@ -197,6 +229,18 @@ public SubstitutionRule(Regex matchPattern, string substitutionPattern) : this(m
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator SubstitutionRule(ValueTuple<Regex, string, int> tuple) => new SubstitutionRule(tuple.Item1, tuple.Item2, tuple.Item3);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator SubstitutionRule(ValueTuple<string, string, bool> tuple) => new SubstitutionRule(new Regex(tuple.Item1), tuple.Item2, tuple.Item3);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator SubstitutionRule(ValueTuple<Regex, string, bool> tuple) => new SubstitutionRule(tuple.Item1, tuple.Item2, tuple.Item3);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator SubstitutionRule(ValueTuple<string, string, int, bool> tuple) => new SubstitutionRule(new Regex(tuple.Item1), tuple.Item2, tuple.Item3, tuple.Item4);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator SubstitutionRule(ValueTuple<Regex, string, int, bool> tuple) => new SubstitutionRule(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4);

/// <summary>
/// <para>
/// Overrides the match pattern options using the specified options.
Expand Down Expand Up @@ -272,6 +316,10 @@ public override string ToString()
sb.Append(" times");
}
}
if (IsTerminating)
{
sb.Append(" (terminating)");
}
return sb.ToString();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,37 @@ public bool Next()
var matchPattern = rule.MatchPattern;
var substitutionPattern = rule.SubstitutionPattern;
var maximumRepeatCount = rule.MaximumRepeatCount;
var isTerminating = rule.IsTerminating;
var replaceCount = 0;
var text = Text;

// If this is a terminating rule, apply it only once and then stop
if (isTerminating)
{
if (matchPattern.IsMatch(text))
{
text = matchPattern.Replace(text, substitutionPattern, 1); // Apply only once
Text = text;
Current = current;
return false; // Stop processing further rules
}
else
{
// Terminating rule doesn't match, continue to next rule
Text = text;
Current = current;
return true;
}
}

// Non-terminating rule: apply according to repeat count
do
{
text = matchPattern.Replace(text, substitutionPattern);
replaceCount++;
}
while ((maximumRepeatCount == int.MaxValue || replaceCount <= maximumRepeatCount) && matchPattern.IsMatch(text));

Text = text;
Current = current;
return true;
Expand Down
28 changes: 28 additions & 0 deletions csharp/debug_test.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using Platform.RegularExpressions.Transformer;

var rules = new SubstitutionRule[]
{
("x", "X", false), // Regular rule: x -> X
("X", "TERMINATED", true), // Terminating rule: X -> TERMINATED
("y", "Y", false), // This rule should not be applied
};

var transformer = new TextTransformer(rules);
var input = "xyx";

Console.WriteLine($"Input: {input}");

var stepTransformer = new TextSteppedTransformer(rules, input);
var step = 0;

while (stepTransformer.Next())
{
step++;
Console.WriteLine($"Step {step}: {stepTransformer.Text} (Rule {stepTransformer.Current})");
}

Console.WriteLine($"Final result: {stepTransformer.Text}");

var output = transformer.Transform(input);
Console.WriteLine($"Transform result: {output}");
14 changes: 14 additions & 0 deletions experiments/DebugTerminating/DebugTerminating.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../../csharp/Platform.RegularExpressions.Transformer/Platform.RegularExpressions.Transformer.csproj" />
</ItemGroup>

</Project>
27 changes: 27 additions & 0 deletions experiments/DebugTerminating/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Platform.RegularExpressions.Transformer;

var rules = new SubstitutionRule[]
{
("x", "X", 0, false), // Regular rule: x -> X (single application)
("X", "TERMINATED", 0, true), // Terminating rule: X -> TERMINATED
("y", "Y", 0, false), // This rule should not be applied
};

var input = "xyx";

Console.WriteLine($"Input: {input}");

var stepTransformer = new TextSteppedTransformer(rules, input);
var step = 0;

while (stepTransformer.Next())
{
step++;
Console.WriteLine($"Step {step}: {stepTransformer.Text} (Rule {stepTransformer.Current})");
}

Console.WriteLine($"Final result: {stepTransformer.Text}");

var transformer = new TextTransformer(rules);
var output = transformer.Transform(input);
Console.WriteLine($"Transform result: {output}");
8 changes: 5 additions & 3 deletions nim/src/Rule.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ type
replace*: string
file*: string
count*: int
isTerminating*: bool

proc `$`*(r: RuleObj): string =
## Converts Rule object to string.
result = fmt"""("{r.rule.pattern}", "{r.replace}", "{r.file}", {r.count})"""
result = fmt"""("{r.rule.pattern}", "{r.replace}", "{r.file}", {r.count}, {r.isTerminating})"""

proc Rule*(rule: Regex = re"", replace: string = "",
file: string = "nil", count: int = 0): RuleObj =
file: string = "nil", count: int = 0, isTerminating: bool = false): RuleObj =
## Creates new Rule object.
##
## Keyword Arguments:
## - ``rule`` -- pattern for rule.
## - ``replace`` -- replace for rule.
## - ``count`` -- count for use pattern.
return RuleObj(rule: rule, replace: replace, file: file, count: count)
## - ``isTerminating`` -- whether this rule is terminating.
return RuleObj(rule: rule, replace: replace, file: file, count: count, isTerminating: isTerminating)
15 changes: 15 additions & 0 deletions nim/src/retranslator.nim
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,21 @@ proc transform*(t: TransformerRef, code=""): string =

for i in t.rules:
count = i.count

# If this is a terminating rule, apply it only once and then stop
if i.isTerminating:
if tr.find(i.rule).isSome:
tr = tr.replace(i.rule, i.replace, 1) # Apply only once
if t.debug:
echo i, " (terminated)"
return tr # Stop processing further rules
else:
# Terminating rule doesn't match, continue to next rule
if t.debug:
echo i, " (no match)"
continue

# Non-terminating rule: apply according to repeat count
tr = tr.replace(i.rule, i.replace)
if t.debug:
echo i
Expand Down
Loading
Loading