1 /*
2  * [The "BSD license"]
3  * Copyright (c) 2013 Terence Parr
4  * Copyright (c) 2013 Sam Harwell
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 module antlr.v4.runtime.tree.pattern.ParseTreeMatch;
32 
33 import std.format;
34 import antlr.v4.runtime.IllegalArgumentException;
35 import antlr.v4.runtime.tree.ParseTree;
36 import antlr.v4.runtime.tree.pattern.ParseTreePattern;
37 
38 // Class ParseTreeMatch
39 /**
40  * TODO add class description
41  * @uml
42  * Represents the result of matching a {@link ParseTree} against a tree pattern.
43  */
44 class ParseTreeMatch
45 {
46 
47     /**
48      * @uml
49      * This is the backing field for {@link #getTree()}.
50      */
51     public ParseTree tree;
52 
53     /**
54      * @uml
55      * This is the backing field for {@link #getPattern()}.
56      */
57     private ParseTreePattern pattern;
58 
59     /**
60      * @uml
61      * This is the backing field for {@link #getLabels()}.
62      */
63     private ParseTree[][string] labels;
64 
65     /**
66      * @uml
67      * This is the backing field for {@link #getMismatchedNode()}.
68      */
69     private ParseTree mismatchedNode;
70 
71     /**
72      * @uml
73      * Constructs a new instance of {@link ParseTreeMatch} from the specified
74      * parse tree and pattern.
75      *
76      *  @param tree The parse tree to match against the pattern.
77      *  @param pattern The parse tree pattern.
78      *  @param labels A mapping from label names to collections of
79      *  {@link ParseTree} objects located by the tree pattern matching process.
80      *  @param mismatchedNode The first node which failed to match the tree
81      * pattern during the matching process.
82      *
83      *  @exception IllegalArgumentException if {@code tree} is {@code null}
84      *  @exception IllegalArgumentException if {@code pattern} is {@code null}
85      *  @exception IllegalArgumentException if {@code labels} is {@code null}
86      */
87     public this(ParseTree tree, ParseTreePattern pattern, ParseTree[][string] labels, ParseTree mismatchedNode)
88     {
89         if (tree is null) {
90             throw new IllegalArgumentException("tree cannot be null");
91         }
92         if (pattern is null) {
93             throw new IllegalArgumentException("pattern cannot be null");
94         }
95         if (labels is null) {
96             throw new IllegalArgumentException("labels cannot be null");
97         }
98         this.tree = tree;
99         this.pattern = pattern;
100         this.labels = labels;
101         this.mismatchedNode = mismatchedNode;
102     }
103 
104     /**
105      * @uml
106      * Get the last node associated with a specific {@code label}.
107      *
108      * <p>For example, for pattern {@code <id:ID>}, {@code get("id")} returns the
109      * node matched for that {@code ID}. If more than one node
110      * matched the specified label, only the last is returned. If there is
111      * no node associated with the label, this returns {@code null}.</p>
112      *
113      * <p>Pattern tags like {@code <ID>} and {@code <expr>} without labels are
114      * considered to be labeled with {@code ID} and {@code expr}, respectively.</p>
115      *
116      *  @param label The label to check.
117      *
118      *  @return The last {@link ParseTree} to match a tag with the specified
119      * label, or {@code null} if no parse tree matched a tag with the label.
120      */
121     public ParseTree get(string label)
122     {
123         ParseTree[] parseTrees = labels[label];
124         if (parseTrees is null || parseTrees.length == 0) {
125             return null;
126         }
127         return parseTrees[parseTrees.length-1]; // return last if multiple
128     }
129 
130     /**
131      * @uml
132      * Return all nodes matching a rule or token tag with the specified label.
133      * 	 *
134      * <p>If the {@code label} is the name of a parser rule or token in the
135      * grammar, the resulting list will contain both the parse trees matching
136      * rule or tags explicitly labeled with the label and the complete set of
137      * parse trees matching the labeled and unlabeled tags in the pattern for
138      * the parser rule or token. For example, if {@code label} is {@code "foo"},
139      * the result will contain <em>all</em> of the following.</p>
140      *
141      * <ul>
142      * <li>Parse tree nodes matching tags of the form {@code <foo:anyRuleName>} and
143      * {@code <foo:AnyTokenName>}.</li>
144      * <li>Parse tree nodes matching tags of the form {@code <anyLabel:foo>}.</li>
145      * <li>Parse tree nodes matching tags of the form {@code <foo>}.</li>
146      * </ul>
147      *
148      *  @param label The label.
149      *
150      *  @return A collection of all {@link ParseTree} nodes matching tags with
151      * the specified {@code label}. If no nodes matched the label, an empty list
152      * is returned.
153      */
154     public ParseTree[] getAll(string label)
155     {
156         ParseTree[] nodes = labels[label];
157         if (nodes is null) {
158             ParseTree[] empty;
159             return empty;
160         }
161         return nodes;
162     }
163 
164     /**
165      * @uml
166      * Return a mapping from label &rarr; [list of nodes].
167      *
168      * <p>The map includes special entries corresponding to the names of rules and
169      * tokens referenced in tags in the original pattern. For additional
170      * information, see the description of {@link #getAll(String)}.</p>
171      *
172      *  @return A mapping from labels to parse tree nodes. If the parse tree
173      * pattern did not contain any rule or token tags, this map will be empty.
174      */
175     public ParseTree[][string] getLabels()
176     {
177         return labels;
178     }
179 
180     /**
181      * @uml
182      * Get the node at which we first detected a mismatch.
183      *
184      *  @return the node at which we first detected a mismatch, or {@code null}
185      * if the match was successful.
186      */
187     public ParseTree getMismatchedNode()
188     {
189         return mismatchedNode;
190     }
191 
192     /**
193      * @uml
194      * Gets a value indicating whether the match operation succeeded.
195      *
196      *  @return {@code true} if the match operation succeeded; otherwise,
197      * {@code false}.
198      */
199     public bool succeeded()
200     {
201         return mismatchedNode is null;
202     }
203 
204     /**
205      * @uml
206      * Get the tree pattern we are matching against.
207      *
208      *  @return The tree pattern we are matching against.
209      */
210     public ParseTreePattern getPattern()
211     {
212         return pattern;
213     }
214 
215     /**
216      * @uml
217      * Get the parse tree we are trying to match to a pattern.
218      *
219      *  @return The {@link ParseTree} we are trying to match to a pattern.
220      */
221     public ParseTree getTree()
222     {
223         return tree;
224     }
225 
226     /**
227      * @uml
228      * @override
229      */
230     public override string toString()
231     {
232         return format("Match %s; found %d labels",
233 			succeeded() ? "succeeded" : "failed",
234 			getLabels().length);
235     }
236 
237 }