Skip to content

Commit 2138c47

Browse files
authored
Merge pull request #114 from Jcparkyn/dev
Add Experimental Nodes
2 parents 22c5155 + 31209d1 commit 2138c47

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1009
-189
lines changed

Nodexr.Tests/QuantifierTests.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public string Greedy_ReturnsOutput(string contents, Reps repetitions)
3131
public string GetOutput_GroupedContents_ReturnsContentsWithAsterisk(string contents)
3232
{
3333
var node = CreateDefaultQuantifier(contents);
34+
node.InputCount.Value = Reps.ZeroOrMore;
3435
return node.CachedOutput.Expression;
3536
}
3637

@@ -40,9 +41,10 @@ public string GetOutput_GroupedContents_ReturnsContentsWithAsterisk(string conte
4041
public string GetOutput_UngroupedContents_ReturnsContentsGroupedWithAsterisk(string contents)
4142
{
4243
var node = new QuantifierNode();
44+
node.InputCount.Value = Reps.ZeroOrMore;
4345
var input = new TextNode();
44-
input.Input.Contents = contents;
45-
input.InputEscapeSpecials.IsChecked = false;
46+
input.Input.Value = contents;
47+
input.InputEscapeSpecials.Checked = false;
4648

4749
node.InputContents.ConnectedNode = input;
4850
node.InputSearchType.Value = QuantifierNode.SearchMode.Greedy;
@@ -55,7 +57,9 @@ public string GetOutput_UngroupedContents_ReturnsContentsGroupedWithAsterisk(str
5557
[TestCase(Reps.ZeroOrOne, ExpectedResult = @"?")]
5658
public string GetSuffix_BasicModes_ReturnsSuffix(Reps mode)
5759
{
58-
return IQuantifiableNode.GetSuffix(mode);
60+
var node = new QuantifierNode();
61+
node.InputCount.Value = mode;
62+
return IQuantifiableNode.GetSuffix(node);
5963
}
6064

6165
[TestCase(0, ExpectedResult = @"{0}")]
@@ -64,29 +68,25 @@ public string GetSuffix_BasicModes_ReturnsSuffix(Reps mode)
6468
[TestCase(null, ExpectedResult = @"{0}")]
6569
public string GetSuffix_Number_ReturnsSuffix(int? number)
6670
{
67-
return IQuantifiableNode.GetSuffix(Reps.Number, number: number);
71+
var node = new QuantifierNode();
72+
node.InputCount.Value = Reps.Number;
73+
node.InputNumber.Value = number;
74+
return IQuantifiableNode.GetSuffix(node);
6875
}
6976

70-
[TestCase(0, 0, ExpectedResult = @"{0,0}")]
77+
[TestCase(0, 0, ExpectedResult = @"{0,}")]
7178
[TestCase(0, 1, ExpectedResult = @"{0,1}")]
7279
[TestCase(0, 99, ExpectedResult = @"{0,99}")]
7380
[TestCase(null, 1, ExpectedResult = @"{0,1}")]
7481
[TestCase(1, 99, ExpectedResult = @"{1,99}")]
7582
[TestCase(1, null, ExpectedResult = @"{1,}")]
7683
public string GetSuffix_Range_ReturnsSuffix(int? min, int? max)
7784
{
78-
return IQuantifiableNode.GetSuffix(Reps.Range, min: min, max: max);
79-
}
80-
81-
private QuantifierNode CreateQuantifierWithRange(string contents, int min, int max)
82-
{
83-
var node = CreateDefaultQuantifier(contents);
84-
85+
var node = new QuantifierNode();
8586
node.InputCount.Value = Reps.Range;
86-
node.InputMin.InputContents = min;
87-
node.InputMax.InputContents = max;
88-
89-
return node;
87+
node.InputRange.Min = min;
88+
node.InputRange.Max = max;
89+
return IQuantifiableNode.GetSuffix(node);
9090
}
9191

9292
private QuantifierNode CreateDefaultQuantifier(string contents)

Nodexr.Tests/RegexParserTests/CharSetParserTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,24 @@ class CharSetParserTests
1818
public void CharSetNode_NoSpecials_ReturnsContents(string input, string expectedContents)
1919
{
2020
CharSetNode result = ParseCharSet.ParseOrThrow(input);
21-
Assert.AreEqual(expectedContents, result.InputCharacters.Contents);
21+
Assert.AreEqual(expectedContents, result.InputCharacters.Value);
2222
}
2323

2424
[TestCase(@"[\]]", @"\]")]
2525
[TestCase(@"[abc\]def]", @"abc\]def")]
2626
public void CharSetNode_EscapedBracket_ReturnsContents(string input, string expectedContents)
2727
{
2828
var result = ParseCharSet.ParseOrThrow(input);
29-
Assert.AreEqual(expectedContents, result.InputCharacters.Contents);
29+
Assert.AreEqual(expectedContents, result.InputCharacters.Value);
3030
}
3131

3232
[TestCase(@"[^a]", @"a")]
3333
[TestCase(@"[^abc]", @"abc")]
3434
public void Inverted_ReturnsContents(string input, string expectedContents)
3535
{
3636
var result = ParseCharSet.ParseOrThrow(input);
37-
Assert.That(result.InputDoInvert.IsChecked, Is.True);
38-
Assert.AreEqual(expectedContents, result.InputCharacters.Contents);
37+
Assert.That(result.InputDoInvert.Checked, Is.True);
38+
Assert.AreEqual(expectedContents, result.InputCharacters.Value);
3939
}
4040
}
4141
}

Nodexr.Tests/TextNodeTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public class TextNodeTests
2020
public void VariousStrings_MatchesString(string contents, string shouldMatch)
2121
{
2222
var node = new TextNode();
23-
node.Input.Contents = contents;
23+
node.Input.Value = contents;
2424

2525
string nodeVal = node.CachedOutput.Expression;
2626

Nodexr/Shared/Components/NodeList.razor

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,15 @@
6464
new CommentNode(),
6565
new FlagNode(),
6666
}),
67+
68+
("Experimental",
69+
new Node[]
70+
{
71+
new DecimalNode(),
72+
new IntegerNode(),
73+
new OptionalNode(),
74+
new ListNode(),
75+
new RecursionNode(),
76+
}),
6777
};
6878
}

Nodexr/Shared/Components/NoodleCollection.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
{
1010
var inputsWithNoodles = node.GetAllInputs()
1111
.OfType<InputProcedural>()
12-
.Where(input => input.Enabled);
12+
.Where(input => input.Connected);
1313

1414
@foreach (var input in inputsWithNoodles)
1515
{

Nodexr/Shared/Components/NoodleSvg.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
@implements IDisposable
33

44
<path id="@DomId" class="noodle" d="@PathInstructions"
5-
style="@(Noodle.Enabled ? "" : "display:none;") @CssStrokeStyle"/>
5+
style="@(Noodle.Connected ? "" : "display:none;") @CssStrokeStyle"/>
66

77

88
@functions{

Nodexr/Shared/ExtensionMethods.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,16 @@ public static string EscapeCharacters(this string input, IEnumerable<char> chars
3131
}
3232
return result;
3333
}
34+
35+
public static string EscapeSpecialCharacters(this string input, bool escapeBackslashes = true)
36+
{
37+
const string specialCharacters = "()[]{}$^?.+*|";
38+
39+
string charsToEscape = escapeBackslashes ?
40+
specialCharacters + "\\" :
41+
specialCharacters;
42+
43+
return EscapeCharacters(input, charsToEscape);
44+
}
3445
}
3546
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
//Algorithm based on answers in https://stackoverflow.com/questions/33512037/a-regular-expression-generator-for-number-ranges
8+
9+
namespace Nodexr.Shared
10+
{
11+
public class IntegerRangeGenerator
12+
{
13+
private readonly struct Pair
14+
{
15+
public readonly int min;
16+
public readonly int max;
17+
18+
public Pair(int min, int max)
19+
{
20+
this.min = min;
21+
this.max = max;
22+
}
23+
24+
public Pair Flipped()
25+
{
26+
return new Pair(max, min);
27+
}
28+
}
29+
30+
public List<string> GenerateRegexRange(int start, int end)
31+
{
32+
if (start < 0)
33+
throw new ArgumentOutOfRangeException(nameof(start), "Must be non-negative");
34+
if (end < 0)
35+
throw new ArgumentOutOfRangeException(nameof(end), "Must be non-negative");
36+
37+
//order inputs so start <= end
38+
if (start > end)
39+
(start, end) = (end, start);
40+
41+
return GenerateRegexRanges(start, end);
42+
}
43+
44+
private List<string> GenerateRegexRanges(int start, int end)
45+
{
46+
var pairs = GetRegexPairsRecursion(start, end);
47+
return FormatPairsToRegEx(pairs);
48+
}
49+
50+
/**
51+
* return the regular expressions that match the ranges in the given
52+
* list of integers. The list is in the form firstRangeStart, firstRangeEnd,
53+
* secondRangeStart, secondRangeEnd, etc. Each regular expression is 0-left-padded,
54+
* if necessary, to match strings of the given width.
55+
*/
56+
private List<string> FormatPairsToRegEx(List<Pair> pairs)
57+
{
58+
List<string> list = new List<string>();
59+
for (int i = 0; i < pairs.Count; i ++)
60+
{
61+
string start = pairs[i].min.ToString();
62+
string end = pairs[i].max.ToString();
63+
64+
StringBuilder result = new StringBuilder();
65+
66+
for (int pos = 0; pos < start.Length; pos++)
67+
{
68+
if (start[pos] == end[pos])
69+
{
70+
result.Append(start[pos]);
71+
}
72+
else if(start[pos] == '0' && end[pos] == '9')
73+
{
74+
result.Append("\\d");
75+
}
76+
else
77+
{
78+
result.Append('[').Append(start[pos]).Append('-')
79+
.Append(end[pos]).Append(']');
80+
}
81+
}
82+
83+
list.Add(result.ToString());
84+
}
85+
return list;
86+
}
87+
88+
/**
89+
* return the list of integers that are the paired integers
90+
* used to generate the regular expressions for the given
91+
* range. Each pair of integers in the list -- 0,1, then 2,3,
92+
* etc., represents a range for which a single regular expression
93+
* is generated.
94+
*/
95+
private List<Pair> GetRegexPairsRecursion(int start, int end)
96+
{
97+
var pairs = new List<Pair>();
98+
99+
if (start == 0)
100+
{
101+
if (end <= 9)
102+
{
103+
pairs.Add(new Pair(0, end));
104+
return pairs;
105+
}
106+
else
107+
{
108+
pairs.Add(new Pair(0, 9));
109+
start = 10;
110+
}
111+
}
112+
113+
if (start > end)
114+
{
115+
return pairs;
116+
}
117+
118+
/*
119+
* Calculate first number ending with 0, which is greater than the start value.
120+
* This will tell us whether or not start and end values differ only at last digit.
121+
*/
122+
int firstEndingWith0 = 10 * ((start + 9) / 10);
123+
124+
/*
125+
* Start and end values differ only at the last digit.
126+
*/
127+
if (firstEndingWith0 > end) // not in range?
128+
{
129+
pairs.Add(new Pair(start, end));
130+
return pairs;
131+
}
132+
133+
/*
134+
* start is not ending in 0.
135+
*/
136+
if (start < firstEndingWith0)
137+
{
138+
pairs.Add(new Pair(start, firstEndingWith0 - 1));
139+
}
140+
141+
//Largest number ending with 9, which is <= the Range end.
142+
int lastEndingWith9 = ((end + 1) / 10 * 10) - 1;
143+
144+
/*
145+
* All RegEx for the range [firstEndingWith0,lastEndingWith9] end with [0-9],
146+
* hence, remove the rightmost 0 from new working range start and remove the rightmost
147+
* 9 from new working range end.
148+
*/
149+
var pairsMiddle = GetRegexPairsRecursion(firstEndingWith0 / 10, lastEndingWith9 / 10);
150+
151+
/*
152+
* Append digits to start and end of each pair. 0 will be appended to the low value of
153+
* the pair and 9 will be appended to the high value of the pair.
154+
* This is equivalent of multiplying low value by 10, and multiplying high value by 10
155+
* and adding 9 to it.
156+
*/
157+
foreach (Pair pair in pairsMiddle)
158+
{
159+
pairs.Add(new Pair(
160+
(pair.min * 10) + 0,
161+
(pair.max * 10) + 9
162+
));
163+
}
164+
165+
if (lastEndingWith9 < end) // end is not ending in 9
166+
{
167+
pairs.Add(new Pair(
168+
lastEndingWith9 + 1,
169+
end
170+
));
171+
}
172+
173+
return pairs;
174+
}
175+
}
176+
}

Nodexr/Shared/NodeInputs/InputCheckbox.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
{
33
public class InputCheckbox : NodeInput
44
{
5-
private bool isChecked;
5+
private bool _checked;
66

7-
public bool IsChecked
7+
public bool Checked
88
{
9-
get => isChecked;
9+
get => _checked;
1010
set
1111
{
12-
isChecked = value;
12+
_checked = value;
1313
OnValueChanged();
1414
}
1515
}
@@ -18,7 +18,7 @@ public bool IsChecked
1818

1919
public InputCheckbox(bool isChecked = false)
2020
{
21-
this.isChecked = isChecked;
21+
this._checked = isChecked;
2222
}
2323
}
2424
}

Nodexr/Shared/NodeInputs/InputDropdown.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Collections.Generic;
33
using System.Linq;
44

5-
65
namespace Nodexr.Shared.NodeInputs
76
{
87
public abstract class InputDropdown : NodeInput
@@ -16,7 +15,7 @@ public class InputDropdown<TValue> : InputDropdown
1615
where TValue : struct, Enum
1716
{
1817
//private string dropdownValue;
19-
private Dictionary<TValue, string> displayNames;
18+
private readonly Dictionary<TValue, string> displayNames;
2019

2120
private TValue value = default;
2221

@@ -53,7 +52,6 @@ public override IEnumerable<string> Options
5352
get
5453
{
5554
if (displayNames != null) return displayNames.Values;
56-
5755
else return Enum.GetNames(typeof(TValue));
5856
}
5957
}

0 commit comments

Comments
 (0)