Skip to content

Commit 18f8b62

Browse files
committed
Handle regex {} quantifiers correctly
1 parent fb646d9 commit 18f8b62

2 files changed

Lines changed: 113 additions & 10 deletions

File tree

RegExtract.Test/Usage.cs

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Text.RegularExpressions;
66
using System.Collections.Generic;
77
using Xunit.Abstractions;
8+
using RegExtract.RegexTools;
89

910
namespace RegExtract.Test
1011
{
@@ -142,6 +143,83 @@ public void a011()
142143
Assert.Equivalent((('&', "kx"), new List<string>() { "zs", "br", "jd", "bj", "vg"}), result);
143144
}
144145

146+
record Rule()
147+
{
148+
public static Rule? Parse(string str)
149+
{
150+
if (str.Contains(":"))
151+
return str.Extract<Conditional>();
152+
else
153+
return str.Extract<Absolute>();
154+
}
155+
}
156+
157+
record Absolute(Action step) : Rule
158+
{
159+
public const string REGEXTRACT_REGEX_PATTERN = @"(.*)";
160+
}
161+
record Conditional(Condition cond, Action step) : Rule
162+
{
163+
public const string REGEXTRACT_REGEX_PATTERN = @"((.)([<>])(\d+)):(\w+)";
164+
}
165+
record Condition(char prop, char test, int val);
166+
167+
record Action
168+
{
169+
public static Action Parse(string str)
170+
{
171+
return str switch
172+
{
173+
"A" => new Accept(),
174+
"R" => new Reject(),
175+
_ => new Workflow(str)
176+
};
177+
}
178+
}
179+
record Accept : Action;
180+
record Reject : Action;
181+
record Workflow(string workflow) : Action;
182+
183+
[Fact]
184+
public void a012()
185+
{
186+
var plan = CreateAndLogPlan<(string workflow, List<Rule> rules) >(/* language=regex */@"(\w+){(([^,]+),?)+}");
187+
188+
var result = plan.Extract("sxc{x>2414:jtp,s>954:R,m>2406:A,xfz}");
189+
190+
Assert.Equivalent(("sxc", new List<Rule> { new Conditional(new Condition('x', '>', 2414), new Workflow("jtp")), new Conditional(new Condition('s', '>', 954), new Reject()), new Conditional(new Condition('m', '>', 2406), new Accept()), new Absolute(new Workflow("xfz")) }), result);
191+
}
192+
193+
[Fact]
194+
public void a013()
195+
{
196+
var
197+
tree = new RegexCaptureGroupTree(new Regex("(asdf){}"));
198+
output.WriteLine(tree.TreeViz());
199+
tree = new RegexCaptureGroupTree(new Regex(@"(asdf){01"));
200+
output.WriteLine(tree.TreeViz());
201+
tree = new RegexCaptureGroupTree(new Regex(@"(asdf){01}"));
202+
output.WriteLine(tree.TreeViz());
203+
tree = new RegexCaptureGroupTree(new Regex(@"(asdf){01,}"));
204+
output.WriteLine(tree.TreeViz());
205+
tree = new RegexCaptureGroupTree(new Regex(@"(asdf){1,2}"));
206+
output.WriteLine(tree.TreeViz());
207+
tree = new RegexCaptureGroupTree(new Regex(@"(asdf){1,2}?"));
208+
output.WriteLine(tree.TreeViz());
209+
tree = new RegexCaptureGroupTree(new Regex(@"(asdf){,12}"));
210+
output.WriteLine(tree.TreeViz());
211+
tree = new RegexCaptureGroupTree(new Regex(@"(asdf){1\,2}"));
212+
output.WriteLine(tree.TreeViz());
213+
tree = new RegexCaptureGroupTree(new Regex(@"(asdf){\1,2}"));
214+
output.WriteLine(tree.TreeViz());
215+
tree = new RegexCaptureGroupTree(new Regex(@"(asdf)\{1}"));
216+
output.WriteLine(tree.TreeViz());
217+
tree = new RegexCaptureGroupTree(new Regex(@"(asdf){1\}"));
218+
output.WriteLine(tree.TreeViz());
219+
tree = new RegexCaptureGroupTree(new Regex(@"(asdf){,}"));
220+
output.WriteLine(tree.TreeViz());
221+
}
222+
145223
[Fact]
146224
public void slow()
147225
{
@@ -513,8 +591,8 @@ public void debug()
513591
//var result = plan.Execute(Regex.Match(data, @"^(.+) bags contain(?: (no other bags)\.| ((\d+) (.*?)) bags?[,.])+$"));
514592

515593
Regex rx;
516-
var plan = ExtractionPlan<bagdescription>.CreatePlan(rx = new Regex(@"^(?<name>.+) bags contain(?: (?<none>no other bags)\.| (?<contents>(\d+) (.*?)) bags?[,.])+$"));
517-
var result = plan.Extract(rx.Match("faded yellow bags contain 4 mirrored fuchsia bags, 4 dotted indigo bags, 3 faded orange bags, 5 plaid crimson bags."));
594+
var plan = CreateAndLogPlan<bagdescription>(/* language=regex */@"^(?<name>.+) bags contain(?: (?<none>no other bags)\.| (?<contents>(\d+) (.*?)) bags?[,.])+$");
595+
var result = plan.Extract("faded yellow bags contain 4 mirrored fuchsia bags, 4 dotted indigo bags, 3 faded orange bags, 5 plaid crimson bags.");
518596

519597
Assert.Equivalent(new bagdescription { name = "faded yellow", contents = new List<includedbags> { new includedbags(4, "mirrored fuchsia"), new includedbags(4, "dotted indigo"), new includedbags(3, "faded orange"), new includedbags(5, "plaid crimson") } }, result);
520598
}

RegExtract/RegexCaptureGroupTree.cs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,17 +157,42 @@ private RegexCaptureGroupNode BuildCaptureGroupTree(ref int loc, ref int num, in
157157
else if (_regexString[loc + 1] is '{')
158158
{
159159
var startloc = loc;
160-
// TODO: The actual regex grammar will bag out of this if the quantifier doesn't actually parse.
161-
while (loc < _regexString.Length && _regexString[loc] is not '}')
160+
var quantifierState = 0;
161+
loc ++;
162+
while (loc < _regexString.Length - 1 && quantifierState >= 0)
162163
{
163164
loc++;
165+
switch (quantifierState, _regexString[loc])
166+
{
167+
case (0, >= '0' and <= '9'):
168+
quantifierState = 1;
169+
break;
170+
case (1 or 2, >= '0' and <= '9'):
171+
break;
172+
case (1, ','):
173+
quantifierState = 2;
174+
break;
175+
case (1 or 2, '}'):
176+
quantifierState = 3;
177+
break;
178+
default:
179+
quantifierState = -1;
180+
break;
181+
}
182+
183+
if (quantifierState == 3)
184+
{
185+
if (loc < _regexString.Length - 1 && _regexString[loc + 1] is '?')
186+
loc++;
187+
188+
break;
189+
}
190+
else if (loc >= _regexString.Length - 1 || quantifierState == -1)
191+
{
192+
loc = startloc;
193+
break;
194+
}
164195
}
165-
if (loc >= _regexString.Length || _regexString[loc] is not '}')
166-
{
167-
loc = startloc;
168-
}
169-
else if (loc + 1 < _regexString.Length && _regexString[loc + 1] is '?')
170-
loc++;
171196
}
172197
}
173198
return new RegexCaptureGroupNode(myname, children.ToArray(), ((start, loc - start + 1),_regexString.Substring(start, loc - start + 1)));

0 commit comments

Comments
 (0)