//****************************************************************** #region [CopyLeafAndChildren() Method] //****************************************************************** /// <summary> /// For the given oReplacePatternNode, this method finds the /// corresponding find-pattern node and the parse-tree node it /// matched. A new node is created by copying features from the /// parse-tree node and merging features from the /// oReplacePatternNode. Then children are copied recursively from /// the parse-tree node. Children are also copied recursively from /// the oReplacePatternNode if they do not correspond to children of /// the parse-tree node. Returns null if the oReplacePatternNode was /// optional and the parse-tree node was not found. Otherwise, /// returns the new node after adding it to the ReplacedNodes list. /// (Note that this method is only for copying leaf nodes.) /// </summary> SyntaxNode CopyLeafAndChildren(SyntaxNode oReplacePatternNode) { Debug.Assert(oReplacePatternNode != null); //************************************************************** // For the given oReplacePatternNode, find the corresponding // find-pattern node and the parse-tree node it matched: // // Search the MatchingNodes list for a node pair where the // .FindPatternNode has the same label as the given // oReplacePatternNode. // // If a matching node pair is found, set oParseTreeNode and // oFindPatternNode to the node-pair values. Otherwise, set // oParseTreeNode and oFindPatternNode both to null. SyntaxNode oParseTreeNode = null; SyntaxNode oFindPatternNode = null; SyntaxNodePair oNodePair = FindMatchingPairFromLabel(oReplacePatternNode.Label); if (oNodePair != null) { oParseTreeNode = oNodePair.ParseTreeNode; oFindPatternNode = oNodePair.FindPatternNode; } //************************************************************** // If a matching node pair was not found and oReplacePatternNode // is optional, nothing will be copied, so return null. if ((oNodePair == null) && (oReplacePatternNode.IsOptionalNode)) { return(null); } //************************************************************** // This method should only be called for leaf nodes. if (oParseTreeNode != null) { Debug.Assert(oParseTreeNode.IsSyntaxLeaf || oReplacePatternNode.IsSyntaxLeaf); } else { Debug.Assert(oReplacePatternNode.IsSyntaxLeaf); } //************************************************************** // Create a new parse-tree node by copying the features from the // matching oParseTreeNode (if found) and then merging the // features from oReplacePatternNode. if (VerboseDebugging) { string sParseString = "null"; if (oParseTreeNode != null) { sParseString = oParseTreeNode.ToString(); } string sReplaceString = "null"; if (oReplacePatternNode != null) { sReplaceString = oReplacePatternNode.ToString(); } Debug.WriteLine("Copying leaf: CopyFeatures(" + sParseString + "," + sReplaceString + ")."); } SyntaxNode oNewNode = CopyFeatures(oParseTreeNode, oReplacePatternNode); //************************************************************** // Each child of a syntax leaf is the root of a morphology // parse. (A syntax leaf may have one or more alternative // morphology parses.) // // Each child will be copied recursively from oParseTreeNode. // Additional children will also be copied recursively from // oReplacePatternNode if they do not correspond to children of // oParseTreeNode. // // Create a list to keep track of the oReplacePatternNode // children as they are used. (A replace-pattern child is marked // as used if it has the same label as a find-pattern child // matching one of the oParseTreeNode children.) ArrayList oUsedReplaceChildren = new ArrayList(); //************************************************************** // Copy the oParseTreeNode children. if (oParseTreeNode != null) { foreach (SyntaxNode oParseChild in oParseTreeNode.ChildNodes) { //****************************************************** // Look for the find-pattern node that matched this // parse-tree child: // // Search the MatchingNodes list for a node pair where // the .ParseTreeNode is the same as oParseChild. // // If found, set oFindChild to the .FindPatternNode // value from the node pair. Otherwise, set oFindChild // to null. SyntaxNodePair oChildNodePair = FindMatchingPairFromNode(oParseChild); SyntaxNode oFindChild = null; if (oChildNodePair != null) { oFindChild = oChildNodePair.FindPatternNode; } //****************************************************** // If oFindChild is null (no find-pattern matched // this parse-tree child), copy oParseChild. // // Otherwise, copy each oReplacePatternNode child that // has the same label as oFindChild. if (oFindChild == null) { //************************************************** // Copy oParseChild. SyntaxNode oNewChild = oParseChild.CloneBranch(); oNewNode.ChildNodes.Add(oNewChild); } else { //************************************************** // Copy each oReplacePatternNode child that has the // same label as oFindChild. foreach (SyntaxNode oReplaceChild in oReplacePatternNode.ChildNodes) { if (oReplaceChild.Label == oFindChild.Label) { //****************************************** // Copy the oReplacePatternNode child. // // Since oParseTreeNode is a syntax leaf, // each of its children is the root of a // morphology parse. So oParseChild is used // as the oMorphologyParseRoot argument to // the CopyNodeAndChildren() method. SyntaxNode oNewChild = CopyNodeAndChildren( oReplaceChild, oParseChild); if (oNewChild != null) { oNewNode.ChildNodes.Add(oNewChild); } //****************************************** // Add this oReplaceChild to the list of // oUsedReplaceChildren (if not already // there). if (!oUsedReplaceChildren .Contains(oReplaceChild)) { oUsedReplaceChildren.Add(oReplaceChild); } } } } } } //************************************************************** // Copy any oReplacePatternNode children that have not yet been // used. foreach (SyntaxNode oReplaceChild in oReplacePatternNode.ChildNodes) { if (!oUsedReplaceChildren.Contains(oReplaceChild)) { SyntaxNode oNewChild = CopyNodeAndChildren(oReplaceChild); if (oNewChild != null) { oNewNode.ChildNodes.Add(oNewChild); } } } //************************************************************** // Return the new parse-tree node after adding it to the // ReplacedNodes list. SyntaxNodeTriple oNodeTriple = new SyntaxNodeTriple(); oNodeTriple.ParseTreeNode = oNewNode; oNodeTriple.FindPatternNode = oFindPatternNode; oNodeTriple.ReplacePatternNode = oReplacePatternNode; ReplacedNodes.Push(oNodeTriple); return(oNewNode); }
//****************************************************************** #region [ReplaceCurrentMatchingBranch() Method] //****************************************************************** /// <summary> /// Replaces the sub-tree dominated by the CurrentParseTreeNode, /// using the tree dominated by the ReplacePatternRoot as a pattern. /// Corresponding nodes (matched by a find-pattern node with the /// same label as the replace-pattern node) are copied from the /// MatchingNodes stack and their features are merged with features /// from the replace pattern. The replaced nodes in the new sub-tree /// are also stored in the ReplacedNodes stack. /// </summary> public void ReplaceCurrentMatchingBranch() { Debug.WriteLineIf(VerboseDebugging, "ReplaceAlgorithm.ReplaceCurrentMatchingBranch() called."); //************************************************************** // Validate the current state. if (ParseTreeRoot == null) { string sMessage = "ReplaceCurrentMatchingBranch() " + "called with an invalid state: " + "ParseTreeRoot is null."; throw new Exception(sMessage); } if (MatchingNodes.Count < 1) { string sMessage = "ReplaceCurrentMatchingBranch() " + "called with an invalid state: " + "MatchingNodes is empty."; throw new Exception(sMessage); } foreach (SyntaxNodePair oNodePair in MatchingNodes) { if (oNodePair.ParseTreeNode == null) { string sMessage = "ReplaceCurrentMatchingBranch() " + "called with an invalid state: " + "MatchingNodes contains an item where " + ".ParseTreeNode is null."; throw new Exception(sMessage); } if (oNodePair.FindPatternNode == null) { string sMessage = "ReplaceCurrentMatchingBranch() " + "called with an invalid state: " + "MatchingNodes contains an item where " + ".FindPatternNode is null."; throw new Exception(sMessage); } } if (ReplacedNodes.Count > 0) { string sMessage = "ReplaceCurrentMatchingBranch() " + "called with an invalid state: " + "ReplacedNodes is not empty."; throw new Exception(sMessage); } if (CurrentParseTreeNode == null) { string sMessage = "ReplaceCurrentMatchingBranch() " + "called with an invalid state: " + "CurrentParseTreeNode is null."; throw new Exception(sMessage); } //************************************************************** // Clear the ReplacedNodes list. ReplacedNodes.Clear(); //************************************************************** // If ReplacePatternRoot is null, use an empty SyntaxNode (one // with no features and no children) instead. SyntaxNode oReplacePatternRoot = ReplacePatternRoot; if (oReplacePatternRoot == null) { oReplacePatternRoot = new SyntaxNode(); } //************************************************************** // Use the oReplacePatternRoot and the MatchingNodes list to // create a new branch to replace the CurrentParseTreeNode. SyntaxNode oNewBranch = CopyNodeAndChildren(oReplacePatternRoot); //************************************************************** // BUGBUG: We may need to make another pass through the new // branch to: // // (1) copy features (or substrings) from another node using a // path <../DP/NP/featurename>. // // (2) recompute any feature values after other features have // changed (like changes to the word string because // morphology strings have changed). //************************************************************** // If the new branch is null, use an empty SyntaxNode (one with // no features and no children) instead, and add this to the // ReplacedNodes list. if (oNewBranch == null) { oNewBranch = new SyntaxNode(); SyntaxNodeTriple oNodeTriple = new SyntaxNodeTriple(); oNodeTriple.ParseTreeNode = oNewBranch; oNodeTriple.FindPatternNode = null; oNodeTriple.ReplacePatternNode = new SyntaxNode(); ReplacedNodes.Push(oNodeTriple); } Debug.Assert(ReplacedNodes.Count > 0); //************************************************************** // If the CurrentParseTreeNode has a parent node, find its // index in the parent's ChildNodes collection, and replace the // child at that index with the new branch. SyntaxNode oParent = CurrentParseTreeNode.ParentNode; if (oParent != null) { int iIndex = oParent.ChildNodes.IndexOf(CurrentParseTreeNode); oParent.ChildNodes.Insert(iIndex, oNewBranch); oParent.ChildNodes.Remove(CurrentParseTreeNode); } //************************************************************** // If the CurrentParseTreeNode is the same as the ParseTreeRoot, // replace the ParseTreeRoot with the new branch. if (CurrentParseTreeNode == ParseTreeRoot) { ParseTreeRoot = oNewBranch; } //************************************************************** // Set CurrentParseTreeNode to the new branch. CurrentParseTreeNode = oNewBranch; //************************************************************** // Dump the ReplacedNodes list for verbose debugging. if (VerboseDebugging) { foreach (SyntaxNodeTriple oNodeTriple in ReplacedNodes) { Debug.Assert(oNodeTriple.ParseTreeNode != null); Debug.Assert(oNodeTriple.ReplacePatternNode != null); } Debug.WriteLine("ReplacedNodes: " + ReplacedNodes.ToString() + "."); } Debug.WriteLineIf(VerboseDebugging, "ReplaceAlgorithm.ReplaceCurrentMatchingBranch() returns."); }
//****************************************************************** /// <summary> /// For the given oReplacePatternNode, this method finds the /// corresponding find-pattern node and the parse-tree node it /// matched. (The parse-tree node must be in the branch indicated by /// oMorphologyParseRoot if this optional argument is not null.) A /// new node is created by copying features from the parse-tree node /// and merging features from the oReplacePatternNode. Then children /// are copied recursively either from the parse-tree node or from /// the oReplacePatternNode. Returns null if the oReplacePatternNode /// was optional and the parse-tree node was not found. Otherwise, /// returns the new node after adding it to the ReplacedNodes list. /// (Note that this method calls the CopyLeafAndChildren() method if /// it determines that the node is a leaf node.) /// </summary> SyntaxNode CopyNodeAndChildren(SyntaxNode oReplacePatternNode, SyntaxNode oMorphologyParseRoot) { Debug.Assert(oReplacePatternNode != null); //************************************************************** // For the given oReplacePatternNode, find the corresponding // find-pattern node and the parse-tree node it matched: // // Search the MatchingNodes list for a node pair where the // .FindPatternNode has the same label as the given // oReplacePatternNode. // // If oMorphologyParseRoot is not null, restrict this search to // node pairs where the .ParseTreeNode is contained in the // branch dominated by this oMorphologyParseRoot. // // If a matching node pair is found, set oParseTreeNode and // oFindPatternNode to the node-pair values. Otherwise, set // oParseTreeNode and oFindPatternNode both to null. SyntaxNode oParseTreeNode = null; SyntaxNode oFindPatternNode = null; SyntaxNodePair oNodePair = FindMatchingPairFromLabel( oReplacePatternNode.Label, oMorphologyParseRoot); if (oNodePair != null) { oParseTreeNode = oNodePair.ParseTreeNode; oFindPatternNode = oNodePair.FindPatternNode; } //************************************************************** // If a matching node pair was not found and oReplacePatternNode // is optional, nothing will be copied, so return null. if ((oNodePair == null) && (oReplacePatternNode.IsOptionalNode)) { return(null); } //************************************************************** // If this is a leaf node, call CopyLeafAndChildren() instead. // // Each child of a syntax leaf is the root of a morphology // parse. (A syntax leaf may have one or more alternative // morphology parses.) CopyLeafAndChildren() is called because // a different algorithm is needed to copy these children. bool bIsSyntaxLeaf = false; if (oParseTreeNode != null) { if (oParseTreeNode.IsSyntaxLeaf) { bIsSyntaxLeaf = true; } } if (oReplacePatternNode.IsSyntaxLeaf) { bIsSyntaxLeaf = true; } if (bIsSyntaxLeaf) { return(CopyLeafAndChildren(oReplacePatternNode)); } //************************************************************** // Create a new parse-tree node by copying the features from the // matching oParseTreeNode (if found) and then merging the // features from oReplacePatternNode. if (VerboseDebugging) { string sParseString = "null"; if (oParseTreeNode != null) { sParseString = oParseTreeNode.ToString(); } string sReplaceString = "null"; if (oReplacePatternNode != null) { sReplaceString = oReplacePatternNode.ToString(); } Debug.WriteLine("Copying node: CopyFeatures(" + sParseString + "," + sReplaceString + ")."); } SyntaxNode oNewNode = CopyFeatures(oParseTreeNode, oReplacePatternNode); //************************************************************** // Check if any children are specified by oFindPatternNode or // oReplacePatternNode. bool bFindPatternHasChildren = false; if (oFindPatternNode != null) { if (oFindPatternNode.ChildNodes.Count > 0) { bFindPatternHasChildren = true; } } bool bReplacePatternHasChildren = false; if (oReplacePatternNode.ChildNodes.Count > 0) { bReplacePatternHasChildren = true; } //************************************************************** // Copy children either from the matching oParseTreeNode or from // the oReplacePatternNode: // // If oParseTreeNode is not null and no children are specified // by oFindPatternNode or oReplacePatternNode, copy the // oParseTreeNode children. // // Otherwise, copy the oReplacePatternNode children. if ((oParseTreeNode != null) && (!bFindPatternHasChildren) && (!bReplacePatternHasChildren)) { //********************************************************** // Copy the oParseTreeNode children. foreach (SyntaxNode oParseChild in oParseTreeNode.ChildNodes) { SyntaxNode oNewChild = oParseChild.CloneBranch(); oNewNode.ChildNodes.Add(oNewChild); } } else { //********************************************************** // Copy the oReplacePatternNode children. foreach (SyntaxNode oReplaceChild in oReplacePatternNode.ChildNodes) { SyntaxNode oNewChild = CopyNodeAndChildren( oReplaceChild, oMorphologyParseRoot); if (oNewChild != null) { oNewNode.ChildNodes.Add(oNewChild); } } } //************************************************************** // Return the new parse-tree node after adding it to the // ReplacedNodes list. SyntaxNodeTriple oNodeTriple = new SyntaxNodeTriple(); oNodeTriple.ParseTreeNode = oNewNode; oNodeTriple.FindPatternNode = oFindPatternNode; oNodeTriple.ReplacePatternNode = oReplacePatternNode; ReplacedNodes.Push(oNodeTriple); return(oNewNode); }
//****************************************************************** #region [FindNextMatchingBranch() Method] //****************************************************************** /// <summary> /// Moves the CurrentParseTreeNode forward in the parse tree, until /// the sub-tree dominated by this node matches the pattern /// dominated by FindPatternRoot. If CurrentParseTreeNode is null /// when this method is called, the search starts with the first /// node in the parse tree. Otherwise, the search continues with the /// next node in the tree. If a matching sub-tree is found, true is /// returned and the MatchingNodes stack contains the list of /// matching node pairs. If no match is found, CurrentParseTreeNode /// is set to null and false is returned. /// </summary> public bool FindNextMatchingBranch() { Debug.WriteLineIf(VerboseDebugging, "FindAlgorithm.FindNextMatchingBranch() called."); //************************************************************** // Validate the current state. if (CurrentParseTreeNode != null) { SyntaxNode oNode = CurrentParseTreeNode; while (oNode != ParseTreeRoot) { if (oNode.ParentNode == null) { string sMessage = "FindNextMatchingBranch() " + "called with an invalid state: " + "the ParseTreeRoot tree does not contain " + "the CurrentParseTreeNode."; throw new Exception(sMessage); } oNode = oNode.ParentNode; } } //************************************************************** // Clear the lists of MatchingNodes and ReplacedNodes. MatchingNodes.Clear(); ReplacedNodes.Clear(); //************************************************************** // Save the previous value of the CurrentParseTreeNode. SyntaxNode oPreviousParseTreeNode = CurrentParseTreeNode; //************************************************************** // Move the CurrentParseTreeNode to the next branch to compare: // // If CurrentParseTreeNode is null, start with the first // parse-tree node in post order. // // Otherwise, move to the next node in post order. // // Note: The parse tree is traversed in post-order (parent after // its children) to prevent infinite recursion that could occur // if an XP node was replaced by a node with XP children, and // then the rule was applied recursively to these children. if (CurrentParseTreeNode == null) { CurrentParseTreeNode = GetFirstNodeInPostOrder(ParseTreeRoot); } else { CurrentParseTreeNode = GetNextNodeInPostOrder(CurrentParseTreeNode); } //************************************************************** // Check if the previous branch should be deleted. // // When the ReplaceCurrentMatchingBranch() method needs to // delete the current parse-tree node, it sets it to an empty // node (one with no features and no children) instead. This is // so we can use the current parse-tree node to determine where // to find the next node to compare. // // If the previous branch is an empty node (with no features and // no children), delete it. if (oPreviousParseTreeNode != null) { if ((oPreviousParseTreeNode.Features.Count == 0) && (oPreviousParseTreeNode.ChildNodes.Count == 0)) { //****************************************************** // Remove the empty node from its parent's ChildNodes // collection. If the empty node is the same as the // ParseTreeRoot, set the ParseTreeRoot to null. SyntaxNode oParent = oPreviousParseTreeNode.ParentNode; if (oParent != null) { oParent.ChildNodes.Remove(oPreviousParseTreeNode); } if (oPreviousParseTreeNode == ParseTreeRoot) { ParseTreeRoot = null; } } } //************************************************************** // If FindPatternRoot is null, nothing can match this pattern. // In this case, set CurrentParseTreeNode to null so there will // be no comparisons and false will be returned. if (FindPatternRoot == null) { CurrentParseTreeNode = null; } //************************************************************** // Traverse the parse tree (in post order), looking for a match, // until there are no more nodes to traverse. Return true if a // match is found. while (CurrentParseTreeNode != null) { //********************************************************** // Return true if the current branch matches the pattern. if (CurrentBranchMatchesPattern()) { Debug.Assert(MatchingNodes.Count > 0); Debug.Assert(ReplacedNodes.Count == 0); Debug.Assert(CurrentParseTreeNode != null); Debug.WriteLineIf(VerboseDebugging, "FindAlgorithm.FindNextMatchingBranch() returns " + "true."); return(true); } //********************************************************** // Move to the next node in post order. CurrentParseTreeNode = GetNextNodeInPostOrder(CurrentParseTreeNode); } //************************************************************** // A matching branch was not found, so return false. Debug.Assert(MatchingNodes.Count == 0); Debug.Assert(ReplacedNodes.Count == 0); Debug.Assert(CurrentParseTreeNode == null); Debug.WriteLineIf(VerboseDebugging, "FindAlgorithm.FindNextMatchingBranch() returns false."); return(false); }