Browse Source

Add simple backtracking support to pattern matching.

pull/37/head
Daniel Grunwald 15 years ago
parent
commit
9b1b38c72b
  1. 54
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs
  2. 24
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs
  3. 20
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs
  4. 21
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Repeat.cs

54
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory.CSharp.PatternMatching; using ICSharpCode.NRefactory.CSharp.PatternMatching;
@ -156,29 +157,44 @@ namespace ICSharpCode.NRefactory.CSharp
internal bool DoMatch(AstNodeCollection<T> other, Match match) internal bool DoMatch(AstNodeCollection<T> other, Match match)
{ {
AstNode cur1 = this.node.FirstChild; Stack<AstNode> patternStack = new Stack<AstNode>();
AstNode cur2 = other.node.FirstChild; Stack<Pattern.PossibleMatch> stack = new Stack<Pattern.PossibleMatch>();
while (true) { patternStack.Push(this.node.FirstChild);
while (cur1 != null && cur1.Role != role) 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; 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; 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;
} }
} }
} }

24
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs

@ -25,4 +25,28 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
return match.Get(referencedGroupName).Last().Match(other) != null; return match.Get(referencedGroupName).Last().Match(other) != null;
} }
} }
/// <summary>
/// Matches identifier expressions that have the same identifier as the referenced variable/type definition/method definition.
/// </summary>
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;
}
}
} }

20
NRefactory/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) // This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ICSharpCode.NRefactory.CSharp.PatternMatching namespace ICSharpCode.NRefactory.CSharp.PatternMatching
{ {
@ -14,11 +16,21 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
get { return NodeType.Pattern; } get { return NodeType.Pattern; }
} }
protected internal virtual bool DoMatchCollection(Role role, ref AstNode other, Match match) internal struct PossibleMatch
{ {
bool result = DoMatch(other, match); public readonly AstNode NextOther; // next node after the last matched node
other = other.NextSibling; public readonly int Checkpoint; // checkpoint
return result;
public PossibleMatch(AstNode nextOther, int checkpoint)
{
this.NextOther = nextOther;
this.Checkpoint = checkpoint;
}
}
internal virtual bool DoMatchCollection(Role role, AstNode pos, Match match, Stack<PossibleMatch> backtrackingStack)
{
return DoMatch(pos, match);
} }
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data) public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)

21
NRefactory/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) // This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
namespace ICSharpCode.NRefactory.CSharp.PatternMatching namespace ICSharpCode.NRefactory.CSharp.PatternMatching
@ -20,24 +21,22 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
AddChild(childNode, ElementRole); 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<Pattern.PossibleMatch> backtrackingStack)
{ {
Debug.Assert(other != null && other.Role == role); Debug.Assert(pos == null || pos.Role == role);
int matchCount = 0; int matchCount = 0;
var lastValidCheckpoint = match.CheckPoint(); if (this.MinCount <= 0)
backtrackingStack.Push(new PossibleMatch(pos, match.CheckPoint()));
AstNode element = GetChildByRole(ElementRole); AstNode element = GetChildByRole(ElementRole);
AstNode pos = other; while (matchCount < this.MaxCount && pos != null && element.DoMatch(pos, match)) {
while (pos != null && element.DoMatch(pos, match)) {
matchCount++; matchCount++;
lastValidCheckpoint = match.CheckPoint();
do { do {
pos = pos.NextSibling; pos = pos.NextSibling;
} while (pos != null && pos.Role != role); } while (pos != null && pos.Role != role);
// set 'other' (=pointer in collection) to the next node after the valid match if (matchCount >= this.MinCount)
other = pos; backtrackingStack.Push(new PossibleMatch(pos, match.CheckPoint()));
} }
match.RestoreCheckPoint(lastValidCheckpoint); // restote old checkpoint after failed match return false; // never do a normal (single-element) match; always make the caller look at the results on the back-tracking stack.
return matchCount >= MinCount && matchCount <= MaxCount;
} }
protected internal override bool DoMatch(AstNode other, Match match) protected internal override bool DoMatch(AstNode other, Match match)
@ -45,7 +44,7 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching
if (other == null || other.IsNull) if (other == null || other.IsNull)
return this.MinCount <= 0; return this.MinCount <= 0;
else else
return GetChildByRole(ElementRole).DoMatch(other, match); return this.MaxCount >= 1 && GetChildByRole(ElementRole).DoMatch(other, match);
} }
} }
} }

Loading…
Cancel
Save