diff options
author | Miguel Costa <miguel.costa@qt.io> | 2022-10-19 14:53:43 +0200 |
---|---|---|
committer | Miguel Costa <miguel.costa@qt.io> | 2022-11-07 11:10:53 +0000 |
commit | 3d6f4a152e8a7bc8229fbf31a2b8e23518bfe5e3 (patch) | |
tree | 1252d4865e2b5210021aa8a762ede7ff35b6c48e | |
parent | a74d8b027edcac0192953014bc72e468ced632b4 (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.cs | 66 | ||||
-rw-r--r-- | Tests/Test_QtVsTools.RegExpr/Test_Matches.cs | 83 | ||||
-rw-r--r-- | Tests/Test_QtVsTools.RegExpr/Test_QtVsTools.RegExpr.csproj | 1 |
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" /> |