diff --git a/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs b/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs index 5a5218773b..0287b2eb2a 100644 --- a/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs +++ b/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using ICSharpCode.NRefactory.CSharp.PatternMatching; @@ -156,29 +157,44 @@ namespace ICSharpCode.NRefactory.CSharp internal bool DoMatch(AstNodeCollection other, Match match) { - AstNode cur1 = this.node.FirstChild; - AstNode cur2 = other.node.FirstChild; - while (true) { - while (cur1 != null && cur1.Role != role) + Stack patternStack = new Stack(); + Stack stack = new Stack(); + patternStack.Push(this.node.FirstChild); + stack.Push(new Pattern.PossibleMatch(other.node.FirstChild, match.CheckPoint())); + while (stack.Count > 0) { + AstNode cur1 = patternStack.Pop(); + AstNode cur2 = stack.Peek().NextOther; + match.RestoreCheckPoint(stack.Pop().Checkpoint); + bool success = true; + while (cur1 != null && success) { + while (cur1 != null && cur1.Role != role) + cur1 = cur1.NextSibling; + while (cur2 != null && cur2.Role != role) + cur2 = cur2.NextSibling; + if (cur1 == null) + break; + + Pattern pattern = cur1 as Pattern; + if (pattern == null && cur1.NodeType == NodeType.Pattern) + pattern = cur1.GetChildByRole(TypePlaceholder.ChildRole) as Pattern; + if (pattern != null) { + Debug.Assert(stack.Count == patternStack.Count); + success = pattern.DoMatchCollection(role, cur2, match, stack); + Debug.Assert(stack.Count >= patternStack.Count); + while (stack.Count > patternStack.Count) + patternStack.Push(cur1.NextSibling); + } else { + success = cur1.DoMatch(cur2, match); + } cur1 = cur1.NextSibling; - while (cur2 != null && cur2.Role != role) - cur2 = cur2.NextSibling; - if (cur1 == null || cur2 == null) - break; - Pattern pattern = cur1 as Pattern; - if (pattern == null && cur1.NodeType == NodeType.Pattern) - pattern = cur1.GetChildByRole(TypePlaceholder.ChildRole) as Pattern; - if (pattern != null) { - if (!pattern.DoMatchCollection(role, ref cur2, match)) - return false; - } else { - if (!cur1.DoMatch(cur2, match)) - return false; cur2 = cur2.NextSibling; } - cur1 = cur1.NextSibling; + while (cur2 != null && cur2.Role != role) + cur2 = cur2.NextSibling; + if (success && cur2 == null) + return true; } - return cur1 == null && cur2 == null; + return false; } } } diff --git a/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs b/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs index db3e3ecebf..dd9f2b5aa2 100644 --- a/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs +++ b/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs @@ -25,4 +25,28 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching return match.Get(referencedGroupName).Last().Match(other) != null; } } + + /// + /// Matches identifier expressions that have the same identifier as the referenced variable/type definition/method definition. + /// + public class IdentifierExpressionBackreference : Pattern + { + readonly string referencedGroupName; + + public IdentifierExpressionBackreference(string referencedGroupName) + { + if (referencedGroupName == null) + throw new ArgumentNullException("referencedGroupName"); + this.referencedGroupName = referencedGroupName; + } + + protected internal override bool DoMatch(AstNode other, Match match) + { + IdentifierExpression ident = other as IdentifierExpression; + if (ident == null || ident.TypeArguments.Any()) + return false; + AstNode referenced = match.Get(referencedGroupName).Last(); + return ident.Identifier == referenced.GetChildByRole(AstNode.Roles.Identifier).Name; + } + } } diff --git a/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs b/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs index ab5efbe8dc..1c68d670e9 100644 --- a/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs +++ b/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs @@ -2,6 +2,8 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Collections.Generic; +using System.Diagnostics; namespace ICSharpCode.NRefactory.CSharp.PatternMatching { @@ -14,11 +16,21 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching get { return NodeType.Pattern; } } - protected internal virtual bool DoMatchCollection(Role role, ref AstNode other, Match match) + internal struct PossibleMatch { - bool result = DoMatch(other, match); - other = other.NextSibling; - return result; + public readonly AstNode NextOther; // next node after the last matched node + public readonly int Checkpoint; // checkpoint + + public PossibleMatch(AstNode nextOther, int checkpoint) + { + this.NextOther = nextOther; + this.Checkpoint = checkpoint; + } + } + + internal virtual bool DoMatchCollection(Role role, AstNode pos, Match match, Stack backtrackingStack) + { + return DoMatch(pos, match); } public override S AcceptVisitor(IAstVisitor visitor, T data) diff --git a/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Repeat.cs b/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Repeat.cs index 3c2bc56530..2ec1183da0 100644 --- a/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Repeat.cs +++ b/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Repeat.cs @@ -2,6 +2,7 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Collections.Generic; using System.Diagnostics; namespace ICSharpCode.NRefactory.CSharp.PatternMatching @@ -20,24 +21,22 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching AddChild(childNode, ElementRole); } - protected internal override bool DoMatchCollection(Role role, ref AstNode other, Match match) + internal override bool DoMatchCollection(Role role, AstNode pos, Match match, Stack backtrackingStack) { - Debug.Assert(other != null && other.Role == role); + Debug.Assert(pos == null || pos.Role == role); int matchCount = 0; - var lastValidCheckpoint = match.CheckPoint(); + if (this.MinCount <= 0) + backtrackingStack.Push(new PossibleMatch(pos, match.CheckPoint())); AstNode element = GetChildByRole(ElementRole); - AstNode pos = other; - while (pos != null && element.DoMatch(pos, match)) { + while (matchCount < this.MaxCount && pos != null && element.DoMatch(pos, match)) { matchCount++; - lastValidCheckpoint = match.CheckPoint(); do { pos = pos.NextSibling; } while (pos != null && pos.Role != role); - // set 'other' (=pointer in collection) to the next node after the valid match - other = pos; + if (matchCount >= this.MinCount) + backtrackingStack.Push(new PossibleMatch(pos, match.CheckPoint())); } - match.RestoreCheckPoint(lastValidCheckpoint); // restote old checkpoint after failed match - return matchCount >= MinCount && matchCount <= MaxCount; + return false; // never do a normal (single-element) match; always make the caller look at the results on the back-tracking stack. } protected internal override bool DoMatch(AstNode other, Match match) @@ -45,7 +44,7 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching if (other == null || other.IsNull) return this.MinCount <= 0; else - return GetChildByRole(ElementRole).DoMatch(other, match); + return this.MaxCount >= 1 && GetChildByRole(ElementRole).DoMatch(other, match); } } }