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 /**
39  * TODO add class description
40  * @uml
41  * Represents the result of matching a {@link ParseTree} against a tree pattern.
42  */
43 class ParseTreeMatch
44 {
45 
46     /**
47      * @uml
48      * This is the backing field for {@link #getTree()}.
49      */
50     public ParseTree tree;
51 
52     /**
53      * @uml
54      * This is the backing field for {@link #getPattern()}.
55      */
56     private ParseTreePattern pattern;
57 
58     /**
59      * @uml
60      * This is the backing field for {@link #getLabels()}.
61      */
62     private ParseTree[][string] labels;
63 
64     /**
65      * @uml
66      * This is the backing field for {@link #getMismatchedNode()}.
67      */
68     private ParseTree mismatchedNode;
69 
70     /**
71      * @uml
72      * Constructs a new instance of {@link ParseTreeMatch} from the specified
73      * parse tree and pattern.
74      *
75      *  @param tree The parse tree to match against the pattern.
76      *  @param pattern The parse tree pattern.
77      *  @param labels A mapping from label names to collections of
78      *  {@link ParseTree} objects located by the tree pattern matching process.
79      *  @param mismatchedNode The first node which failed to match the tree
80      * pattern during the matching process.
81      *
82      *  @exception IllegalArgumentException if {@code tree} is {@code null}
83      *  @exception IllegalArgumentException if {@code pattern} is {@code null}
84      *  @exception IllegalArgumentException if {@code labels} is {@code null}
85      */
86     public this(ParseTree tree, ParseTreePattern pattern, ParseTree[][string] labels, ParseTree mismatchedNode)
87     {
88         if (tree is null) {
89             throw new IllegalArgumentException("tree cannot be null");
90         }
91         if (pattern is null) {
92             throw new IllegalArgumentException("pattern cannot be null");
93         }
94         if (labels is null) {
95             throw new IllegalArgumentException("labels cannot be null");
96         }
97         this.tree = tree;
98         this.pattern = pattern;
99         this.labels = labels;
100         this.mismatchedNode = mismatchedNode;
101     }
102 
103     /**
104      * @uml
105      * Get the last node associated with a specific {@code label}.
106      *
107      * <p>For example, for pattern {@code <id:ID>}, {@code get("id")} returns the
108      * node matched for that {@code ID}. If more than one node
109      * matched the specified label, only the last is returned. If there is
110      * no node associated with the label, this returns {@code null}.</p>
111      *
112      * <p>Pattern tags like {@code <ID>} and {@code <expr>} without labels are
113      * considered to be labeled with {@code ID} and {@code expr}, respectively.</p>
114      *
115      *  @param label The label to check.
116      *
117      *  @return The last {@link ParseTree} to match a tag with the specified
118      * label, or {@code null} if no parse tree matched a tag with the label.
119      */
120     public ParseTree get(string label)
121     {
122         ParseTree[] parseTrees = labels[label];
123         if (parseTrees is null || parseTrees.length == 0) {
124             return null;
125         }
126         return parseTrees[parseTrees.length-1]; // return last if multiple
127     }
128 
129     /**
130      * @uml
131      * Return all nodes matching a rule or token tag with the specified label.
132      * 	 *
133      * <p>If the {@code label} is the name of a parser rule or token in the
134      * grammar, the resulting list will contain both the parse trees matching
135      * rule or tags explicitly labeled with the label and the complete set of
136      * parse trees matching the labeled and unlabeled tags in the pattern for
137      * the parser rule or token. For example, if {@code label} is {@code "foo"},
138      * the result will contain <em>all</em> of the following.</p>
139      *
140      * <ul>
141      * <li>Parse tree nodes matching tags of the form {@code <foo:anyRuleName>} and
142      * {@code <foo:AnyTokenName>}.</li>
143      * <li>Parse tree nodes matching tags of the form {@code <anyLabel:foo>}.</li>
144      * <li>Parse tree nodes matching tags of the form {@code <foo>}.</li>
145      * </ul>
146      *
147      *  @param label The label.
148      *
149      *  @return A collection of all {@link ParseTree} nodes matching tags with
150      * the specified {@code label}. If no nodes matched the label, an empty list
151      * is returned.
152      */
153     public ParseTree[] getAll(string label)
154     {
155         ParseTree[] nodes = labels[label];
156         if (nodes is null) {
157             ParseTree[] empty;
158             return empty;
159         }
160         return nodes;
161     }
162 
163     /**
164      * @uml
165      * Return a mapping from label &rarr; [list of nodes].
166      *
167      * <p>The map includes special entries corresponding to the names of rules and
168      * tokens referenced in tags in the original pattern. For additional
169      * information, see the description of {@link #getAll(String)}.</p>
170      *
171      *  @return A mapping from labels to parse tree nodes. If the parse tree
172      * pattern did not contain any rule or token tags, this map will be empty.
173      */
174     public ParseTree[][string] getLabels()
175     {
176         return labels;
177     }
178 
179     /**
180      * @uml
181      * Get the node at which we first detected a mismatch.
182      *
183      *  @return the node at which we first detected a mismatch, or {@code null}
184      * if the match was successful.
185      */
186     public ParseTree getMismatchedNode()
187     {
188         return mismatchedNode;
189     }
190 
191     /**
192      * @uml
193      * Gets a value indicating whether the match operation succeeded.
194      *
195      *  @return {@code true} if the match operation succeeded; otherwise,
196      * {@code false}.
197      */
198     public bool succeeded()
199     {
200         return mismatchedNode is null;
201     }
202 
203     /**
204      * @uml
205      * Get the tree pattern we are matching against.
206      *
207      *  @return The tree pattern we are matching against.
208      */
209     public ParseTreePattern getPattern()
210     {
211         return pattern;
212     }
213 
214     /**
215      * @uml
216      * Get the parse tree we are trying to match to a pattern.
217      *
218      *  @return The {@link ParseTree} we are trying to match to a pattern.
219      */
220     public ParseTree getTree()
221     {
222         return tree;
223     }
224 
225     /**
226      * @uml
227      * @override
228      */
229     public override string toString()
230     {
231         return format("Match %s; found %d labels",
232 			succeeded() ? "succeeded" : "failed",
233 			getLabels().length);
234     }
235 
236 }