KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > percederberg > grammatica > parser > Analyzer


1 /*
2  * Analyzer.java
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 2.1
7  * of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
17  * MA 02111-1307, USA.
18  *
19  * Copyright (c) 2003-2005 Per Cederberg. All rights reserved.
20  */

21
22 package net.percederberg.grammatica.parser;
23
24 import java.util.ArrayList JavaDoc;
25
26 /**
27  * A parse tree analyzer. This class provides callback methods that
28  * may be used either during parsing, or for a parse tree traversal.
29  * This class should be subclassed to provide adequate handling of the
30  * parse tree nodes.
31  *
32  * The general contract for the analyzer class does not guarantee a
33  * strict call order for the callback methods. Depending on the type
34  * of parser, the enter() and exit() methods for production nodes can
35  * be called either in a top-down or a bottom-up fashion. The only
36  * guarantee provided by this API, is that the calls for any given
37  * node will always be in the order enter(), child(), and exit(). If
38  * various child() calls are made, they will be made from left to
39  * right as child nodes are added (to the right).
40  *
41  * @author Per Cederberg, <per at percederberg dot net>
42  * @version 1.1
43  */

44 public class Analyzer {
45
46     /**
47      * Creates a new parse tree analyzer.
48      */

49     public Analyzer() {
50     }
51
52     /**
53      * Analyzes a parse tree node by traversing all it's child nodes.
54      * The tree traversal is depth-first, and the appropriate
55      * callback methods will be called. If the node is a production
56      * node, a new production node will be created and children will
57      * be added by recursively processing the children of the
58      * specified production node. This method is used to process a
59      * parse tree after creation.
60      *
61      * @param node the parse tree node to process
62      *
63      * @return the resulting parse tree node
64      *
65      * @throws ParserLogException if the node analysis discovered
66      * errors
67      */

68     public Node analyze(Node node) throws ParserLogException {
69         ParserLogException log = new ParserLogException();
70
71         node = analyze(node, log);
72         if (log.getErrorCount() > 0) {
73             throw log;
74         }
75         return node;
76     }
77
78     /**
79      * Analyzes a parse tree node by traversing all it's child nodes.
80      * The tree traversal is depth-first, and the appropriate
81      * callback methods will be called. If the node is a production
82      * node, a new production node will be created and children will
83      * be added by recursively processing the children of the
84      * specified production node. This method is used to process a
85      * parse tree after creation.
86      *
87      * @param node the parse tree node to process
88      * @param log the parser error log
89      *
90      * @return the resulting parse tree node
91      */

92     private Node analyze(Node node, ParserLogException log) {
93         Production prod;
94         int errorCount;
95
96         errorCount = log.getErrorCount();
97         if (node instanceof Production) {
98             prod = (Production) node;
99             prod = new Production(prod.getPattern());
100             try {
101                 enter(prod);
102             } catch (ParseException e) {
103                 log.addError(e);
104             }
105             for (int i = 0; i < node.getChildCount(); i++) {
106                 try {
107                     child(prod, analyze(node.getChildAt(i), log));
108                 } catch (ParseException e) {
109                     log.addError(e);
110                 }
111             }
112             try {
113                 return exit(prod);
114             } catch (ParseException e) {
115                 if (errorCount == log.getErrorCount()) {
116                     log.addError(e);
117                 }
118             }
119         } else {
120             node.removeAllValues();
121             try {
122                 enter(node);
123             } catch (ParseException e) {
124                 log.addError(e);
125             }
126             try {
127                 return exit(node);
128             } catch (ParseException e) {
129                 if (errorCount == log.getErrorCount()) {
130                     log.addError(e);
131                 }
132             }
133         }
134         return null;
135     }
136
137     /**
138      * Called when entering a parse tree node. By default this method
139      * does nothing. A subclass can override this method to handle
140      * each node separately.
141      *
142      * @param node the node being entered
143      *
144      * @throws ParseException if the node analysis discovered errors
145      */

146     protected void enter(Node node) throws ParseException {
147     }
148
149     /**
150      * Called when exiting a parse tree node. By default this method
151      * returns the node. A subclass can override this method to handle
152      * each node separately. If no parse tree should be created, this
153      * method should return null.
154      *
155      * @param node the node being exited
156      *
157      * @return the node to add to the parse tree, or
158      * null if no parse tree should be created
159      *
160      * @throws ParseException if the node analysis discovered errors
161      */

162     protected Node exit(Node node) throws ParseException {
163         return node;
164     }
165
166     /**
167      * Called when adding a child to a parse tree node. By default
168      * this method adds the child to the production node. A subclass
169      * can override this method to handle each node separately. Note
170      * that the child node may be null if the corresponding exit()
171      * method returned null.
172      *
173      * @param node the parent node
174      * @param child the child node, or null
175      *
176      * @throws ParseException if the node analysis discovered errors
177      */

178     protected void child(Production node, Node child)
179         throws ParseException {
180
181         node.addChild(child);
182     }
183
184     /**
185      * Returns a child at the specified position. If either the node
186      * or the child node is null, this method will throw a parse
187      * exception with the internal error type.
188      *
189      * @param node the parent node
190      * @param pos the child position
191      *
192      * @return the child node
193      *
194      * @throws ParseException if either the node or the child node
195      * was null
196      */

197     protected Node getChildAt(Node node, int pos) throws ParseException {
198         Node child;
199
200         if (node == null) {
201             throw new ParseException(
202                 ParseException.INTERNAL_ERROR,
203                 "attempt to read 'null' parse tree node",
204                 -1,
205                 -1);
206         }
207         child = node.getChildAt(pos);
208         if (child == null) {
209             throw new ParseException(
210                 ParseException.INTERNAL_ERROR,
211                 "node '" + node.getName() + "' has no child at " +
212                 "position " + pos,
213                 node.getStartLine(),
214                 node.getStartColumn());
215         }
216         return child;
217     }
218
219     /**
220      * Returns the first child with the specified id. If the node is
221      * null, or no child with the specified id could be found, this
222      * method will throw a parse exception with the internal error
223      * type.
224      *
225      * @param node the parent node
226      * @param id the child node id
227      *
228      * @return the child node
229      *
230      * @throws ParseException if the node was null, or a child node
231      * couldn't be found
232      */

233     protected Node getChildWithId(Node node, int id)
234         throws ParseException {
235
236         Node child;
237
238         if (node == null) {
239             throw new ParseException(
240                 ParseException.INTERNAL_ERROR,
241                 "attempt to read 'null' parse tree node",
242                 -1,
243                 -1);
244         }
245         for (int i = 0; i < node.getChildCount(); i++) {
246             child = node.getChildAt(i);
247             if (child != null && child.getId() == id) {
248                 return child;
249             }
250         }
251         throw new ParseException(
252             ParseException.INTERNAL_ERROR,
253             "node '" + node.getName() + "' has no child with id " + id,
254             node.getStartLine(),
255             node.getStartColumn());
256     }
257
258     /**
259      * Returns the node value at the specified position. If either
260      * the node or the value is null, this method will throw a parse
261      * exception with the internal error type.
262      *
263      * @param node the parse tree node
264      * @param pos the child position
265      *
266      * @return the value object
267      *
268      * @throws ParseException if either the node or the value was null
269      */

270     protected Object JavaDoc getValue(Node node, int pos) throws ParseException {
271         Object JavaDoc value;
272
273         if (node == null) {
274             throw new ParseException(
275                 ParseException.INTERNAL_ERROR,
276                 "attempt to read 'null' parse tree node",
277                 -1,
278                 -1);
279         }
280         value = node.getValue(pos);
281         if (value == null) {
282             throw new ParseException(
283                 ParseException.INTERNAL_ERROR,
284                 "node '" + node.getName() + "' has no value at " +
285                 "position " + pos,
286                 node.getStartLine(),
287                 node.getStartColumn());
288         }
289         return value;
290     }
291
292     /**
293      * Returns the node integer value at the specified position. If
294      * either the node is null, or the value is not an instance of
295      * the Integer class, this method will throw a parse exception
296      * with the internal error type.
297      *
298      * @param node the parse tree node
299      * @param pos the child position
300      *
301      * @return the value object
302      *
303      * @throws ParseException if either the node was null, or the
304      * value wasn't an integer
305      */

306     protected int getIntValue(Node node, int pos) throws ParseException {
307         Object JavaDoc value;
308
309         value = getValue(node, pos);
310         if (value instanceof Integer JavaDoc) {
311             return ((Integer JavaDoc) value).intValue();
312         } else {
313             throw new ParseException(
314                 ParseException.INTERNAL_ERROR,
315                 "node '" + node.getName() + "' has no integer value " +
316                 "at position " + pos,
317                 node.getStartLine(),
318                 node.getStartColumn());
319         }
320     }
321
322     /**
323      * Returns the node string value at the specified position. If
324      * either the node is null, or the value is not an instance of
325      * the String class, this method will throw a parse exception
326      * with the internal error type.
327      *
328      * @param node the parse tree node
329      * @param pos the child position
330      *
331      * @return the value object
332      *
333      * @throws ParseException if either the node was null, or the
334      * value wasn't a string
335      */

336     protected String JavaDoc getStringValue(Node node, int pos)
337         throws ParseException {
338
339         Object JavaDoc value;
340
341         value = getValue(node, pos);
342         if (value instanceof String JavaDoc) {
343             return (String JavaDoc) value;
344         } else {
345             throw new ParseException(
346                 ParseException.INTERNAL_ERROR,
347                 "node '" + node.getName() + "' has no string value " +
348                 "at position " + pos,
349                 node.getStartLine(),
350                 node.getStartColumn());
351         }
352     }
353
354     /**
355      * Returns all the node values for all child nodes.
356      *
357      * @param node the parse tree node
358      *
359      * @return a list with all the child node values
360      *
361      * @since 1.3
362      */

363     protected ArrayList JavaDoc getChildValues(Node node) {
364         ArrayList JavaDoc result = new ArrayList JavaDoc();
365         Node child;
366         ArrayList JavaDoc values;
367
368         for (int i = 0; i < node.getChildCount(); i++) {
369             child = node.getChildAt(i);
370             values = child.getAllValues();
371             if (values != null) {
372                 result.addAll(values);
373             }
374         }
375         return result;
376     }
377 }
378
Popular Tags