aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiguel Costa <miguel.costa@qt.io>2022-10-19 14:53:43 +0200
committerMiguel Costa <miguel.costa@qt.io>2022-11-07 11:10:53 +0000
commit3d6f4a152e8a7bc8229fbf31a2b8e23518bfe5e3 (patch)
tree1252d4865e2b5210021aa8a762ede7ff35b6c48e
parenta74d8b027edcac0192953014bc72e468ced632b4 (diff)
Support multiple matches of RegExpr pattern
Previously, RegExpr would only match the first occurrence of the pattern. In case of more than one match, a virtual syntax tree node is now added as root, with all top-level tokens added as child nodes. Change-Id: Id034dcf38c0d87aa2e4fac0810e0b65d6d43c2b7 Reviewed-by: Karsten Heimrich <karsten.heimrich@qt.io>
-rw-r--r--QtVsTools.RegExpr/parser/Parser.cs66
-rw-r--r--Tests/Test_QtVsTools.RegExpr/Test_Matches.cs83
-rw-r--r--Tests/Test_QtVsTools.RegExpr/Test_QtVsTools.RegExpr.csproj1
3 files changed, 130 insertions, 20 deletions
diff --git a/QtVsTools.RegExpr/parser/Parser.cs b/QtVsTools.RegExpr/parser/Parser.cs
index 60c6a0f7..0ae7a36b 100644
--- a/QtVsTools.RegExpr/parser/Parser.cs
+++ b/QtVsTools.RegExpr/parser/Parser.cs
@@ -27,6 +27,7 @@
****************************************************************************/
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
@@ -88,26 +89,36 @@ namespace QtVsTools.SyntaxAnalysis
ParseTree GetParseTree(string text)
{
// Match regex pattern
- var match = Regex.Match(text);
- if (!match.Success || match.Length == 0)
+ var nodes = new List<ParseTree.Node>();
+ var matches = Regex.Matches(text);
+ if (matches.Count == 0)
throw new ParseErrorException();
+ foreach (Match match in matches) {
+ if (!match.Success || match.Length == 0) {
+ if (nodes.Any())
+ continue;
+ else
+ throw new ParseErrorException();
+ }
- // Flat list of parse-tree nodes, from Regex captures
- var nodes = match.Groups.Cast<Group>()
- .SelectMany((group, groupIdx) => group.Captures.Cast<Capture>()
- .Where(capture => !string.IsNullOrEmpty(capture.Value))
- .Select((capture, captureIdx) => new ParseTree.Node
- {
- CaptureId = Regex.GroupNameFromNumber(groupIdx),
- Token = Pattern.Tokens[Regex.GroupNameFromNumber(groupIdx)],
- Value = capture.Value,
- Begin = capture.Index,
- End = capture.Index + capture.Length,
- GroupIdx = groupIdx,
- CaptureIdx = captureIdx,
- }))
- .OrderBy(c => c.Begin)
- .ToList();
+ // Flat list of parse-tree nodes, from Regex captures
+ var matchNodes = match.Groups.Cast<Group>()
+ .SelectMany((group, groupIdx) => group.Captures.Cast<Capture>()
+ .Where(capture => !string.IsNullOrEmpty(capture.Value))
+ .Select((capture, captureIdx) => new ParseTree.Node
+ {
+ CaptureId = Regex.GroupNameFromNumber(groupIdx),
+ Token = Pattern.Tokens[Regex.GroupNameFromNumber(groupIdx)],
+ Value = capture.Value,
+ Begin = capture.Index,
+ End = capture.Index + capture.Length,
+ GroupIdx = groupIdx,
+ CaptureIdx = captureIdx,
+ }))
+ .OrderBy(c => c.Begin)
+ .ToList();
+ nodes.AddRange(matchNodes);
+ }
// Node list partitioned by token
var nodesByToken = nodes
@@ -131,8 +142,23 @@ namespace QtVsTools.SyntaxAnalysis
(node.Parent = parentNodes[idx]).ChildNodes.Add(node.Begin, node);
}
- // Return parse tree root
- return nodesByToken[Pattern.Root].FirstOrDefault();
+ var topNodes = nodesByToken[Pattern.Root];
+ if (topNodes.Length == 1)
+ return topNodes[0];
+
+ var root = new ParseTree.Node()
+ {
+ CaptureId = string.Empty,
+ Token = null,
+ Value = text,
+ Begin = 0,
+ End = text.Length,
+ GroupIdx = -1,
+ CaptureIdx = -1,
+ };
+ foreach (var node in nodesByToken[Pattern.Root])
+ (node.Parent = root).ChildNodes.Add(node.Begin, node);
+ return root;
}
}
diff --git a/Tests/Test_QtVsTools.RegExpr/Test_Matches.cs b/Tests/Test_QtVsTools.RegExpr/Test_Matches.cs
new file mode 100644
index 00000000..df52316b
--- /dev/null
+++ b/Tests/Test_QtVsTools.RegExpr/Test_Matches.cs
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt VS Tools.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace QtVsTools.Test.RegExpr
+{
+ using SyntaxAnalysis;
+ using static SyntaxAnalysis.RegExpr;
+
+ using Property = KeyValuePair<string, string>;
+
+ [TestClass]
+ public class Test_Matches
+ {
+ Parser propertyParser;
+
+ [TestInitialize]
+ public void GenerateParser()
+ {
+ var namePattern = (~CharSet['=', '\r', '\n']).Repeat(atLeast: 1);
+ var nameToken = new Token("name", namePattern);
+
+ var valuePattern = (~CharSet['\r', '\n']).Repeat(atLeast: 1);
+ var valueToken = new Token("value", valuePattern);
+
+ var propertyPattern
+ = StartOfLine & nameToken & "=" & valueToken & (LineBreak | EndOfFile);
+
+ var propertyToken = new Token("property", propertyPattern)
+ {
+ new Rule<Property>
+ {
+ Create("name", (string s) => new Property(s, string.Empty)),
+ Transform("value", (Property p, string s) => new Property(p.Key, s))
+ }
+ };
+ propertyParser = propertyToken.Render();
+ }
+
+ [TestMethod]
+ public void MultipleMatches()
+ {
+ string propertiesText = @"
+VSCMD_ARG_app_plat=Desktop
+VSCMD_ARG_HOST_ARCH=x64
+VSCMD_ARG_TGT_ARCH=x64
+VSCMD_VER=16.11.17";
+
+ IEnumerable<Property> properties = propertyParser
+ .Parse(propertiesText)
+ .GetValues<Property>("property");
+ Assert.IsTrue(properties.Count() == 4);
+ }
+ }
+}
diff --git a/Tests/Test_QtVsTools.RegExpr/Test_QtVsTools.RegExpr.csproj b/Tests/Test_QtVsTools.RegExpr/Test_QtVsTools.RegExpr.csproj
index a066d042..0fd1da52 100644
--- a/Tests/Test_QtVsTools.RegExpr/Test_QtVsTools.RegExpr.csproj
+++ b/Tests/Test_QtVsTools.RegExpr/Test_QtVsTools.RegExpr.csproj
@@ -102,6 +102,7 @@
<Link>MacroParser.cs</Link>
</Compile>
<Compile Include="Test_MacroParser.cs" />
+ <Compile Include="Test_Matches.cs" />
<Compile Include="Test_XmlIntParser.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Test_SubTokens.cs" />