/* * Analyzer.cs * * This work is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * * This work is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * As a special exception, the copyright holders of this library give * you permission to link this library with independent modules to * produce an executable, regardless of the license terms of these * independent modules, and to copy and distribute the resulting * executable under terms of your choice, provided that you also meet, * for each linked independent module, the terms and conditions of the * license of that module. An independent module is a module which is * not derived from or based on this library. If you modify this * library, you may extend this exception to your version of the * library, but you are not obligated to do so. If you do not wish to * do so, delete this exception statement from your version. * * Copyright (c) 2003 Per Cederberg. All rights reserved. */ using System.Collections; namespace PerCederberg.Grammatica.Parser { /** * A parse tree analyzer. This class provides callback methods that * may be used either during parsing, or for a parse tree traversal. * This class should be subclassed to provide adequate handling of the * parse tree nodes. * * The general contract for the analyzer class does not guarantee a * strict call order for the callback methods. Depending on the type * of parser, the enter() and exit() methods for production nodes can * be called either in a top-down or a bottom-up fashion. The only * guarantee provided by this API, is that the calls for any given * node will always be in the order enter(), child(), and exit(). If * various child() calls are made, they will be made from left to * right as child nodes are added (to the right). * * @author Per Cederberg, <per at percederberg dot net> * @version 1.1 */ public class Analyzer { /** * Creates a new parse tree analyzer. */ public Analyzer() { } /** * Analyzes a parse tree node by traversing all it's child nodes. * The tree traversal is depth-first, and the appropriate * callback methods will be called. If the node is a production * node, a new production node will be created and children will * be added by recursively processing the children of the * specified production node. This method is used to process a * parse tree after creation. * * @param node the parse tree node to process * * @return the resulting parse tree node * * @throws ParserLogException if the node analysis discovered * errors */ public Node Analyze(Node node) { ParserLogException log = new ParserLogException(); node = Analyze(node, log); if (log.GetErrorCount() > 0) { throw log; } return node; } /** * Analyzes a parse tree node by traversing all it's child nodes. * The tree traversal is depth-first, and the appropriate * callback methods will be called. If the node is a production * node, a new production node will be created and children will * be added by recursively processing the children of the * specified production node. This method is used to process a * parse tree after creation. * * @param node the parse tree node to process * @param log the parser error log * * @return the resulting parse tree node */ private Node Analyze(Node node, ParserLogException log) { Production prod; int errorCount; errorCount = log.GetErrorCount(); if (node is Production) { prod = (Production) node; prod = new Production(prod.GetPattern()); try { Enter(prod); } catch (ParseException e) { log.AddError(e); } for (int i = 0; i < node.GetChildCount(); i++) { try { Child(prod, Analyze(node.GetChildAt(i), log)); } catch (ParseException e) { log.AddError(e); } } try { return Exit(prod); } catch (ParseException e) { if (errorCount == log.GetErrorCount()) { log.AddError(e); } } } else { node.RemoveAllValues(); try { Enter(node); } catch (ParseException e) { log.AddError(e); } try { return Exit(node); } catch (ParseException e) { if (errorCount == log.GetErrorCount()) { log.AddError(e); } } } return null; } /** * Called when entering a parse tree node. By default this method * does nothing. A subclass can override this method to handle * each node separately. * * @param node the node being entered * * @throws ParseException if the node analysis discovered errors */ public virtual void Enter(Node node) { } /** * Called when exiting a parse tree node. By default this method * returns the node. A subclass can override this method to handle * each node separately. If no parse tree should be created, this * method should return null. * * @param node the node being exited * * @return the node to add to the parse tree, or * null if no parse tree should be created * * @throws ParseException if the node analysis discovered errors */ public virtual Node Exit(Node node) { return node; } /** * Called when adding a child to a parse tree node. By default * this method adds the child to the production node. A subclass * can override this method to handle each node separately. Note * that the child node may be null if the corresponding exit() * method returned null. * * @param node the parent node * @param child the child node, or null * * @throws ParseException if the node analysis discovered errors */ public virtual void Child(Production node, Node child) { node.AddChild(child); } /** * Returns a child at the specified position. If either the node * or the child node is null, this method will throw a parse * exception with the internal error type. * * @param node the parent node * @param pos the child position * * @return the child node * * @throws ParseException if either the node or the child node * was null */ protected Node GetChildAt(Node node, int pos) { Node child; if (node == null) { throw new ParseException( ParseException.ErrorType.INTERNAL, "attempt to read 'null' parse tree node", -1, -1); } child = node.GetChildAt(pos); if (child == null) { throw new ParseException( ParseException.ErrorType.INTERNAL, "node '" + node.GetName() + "' has no child at " + "position " + pos, node.GetStartLine(), node.GetStartColumn()); } return child; } /** * Returns the first child with the specified id. If the node is * null, or no child with the specified id could be found, this * method will throw a parse exception with the internal error * type. * * @param node the parent node * @param id the child node id * * @return the child node * * @throws ParseException if the node was null, or a child node * couldn't be found */ protected Node GetChildWithId(Node node, int id) { Node child; if (node == null) { throw new ParseException( ParseException.ErrorType.INTERNAL, "attempt to read 'null' parse tree node", -1, -1); } for (int i = 0; i < node.GetChildCount(); i++) { child = node.GetChildAt(i); if (child != null && child.GetId() == id) { return child; } } throw new ParseException( ParseException.ErrorType.INTERNAL, "node '" + node.GetName() + "' has no child with id " + id, node.GetStartLine(), node.GetStartColumn()); } /** * Returns the node value at the specified position. If either * the node or the value is null, this method will throw a parse * exception with the internal error type. * * @param node the parse tree node * @param pos the child position * * @return the value object * * @throws ParseException if either the node or the value was null */ protected object GetValue(Node node, int pos) { object value; if (node == null) { throw new ParseException( ParseException.ErrorType.INTERNAL, "attempt to read 'null' parse tree node", -1, -1); } value = node.GetValue(pos); if (value == null) { throw new ParseException( ParseException.ErrorType.INTERNAL, "node '" + node.GetName() + "' has no value at " + "position " + pos, node.GetStartLine(), node.GetStartColumn()); } return value; } /** * Returns the node integer value at the specified position. If * either the node is null, or the value is not an instance of * the Integer class, this method will throw a parse exception * with the internal error type. * * @param node the parse tree node * @param pos the child position * * @return the value object * * @throws ParseException if either the node was null, or the * value wasn't an integer */ protected int GetIntValue(Node node, int pos) { object value; value = GetValue(node, pos); if (value is int) { return (int) value; } else { throw new ParseException( ParseException.ErrorType.INTERNAL, "node '" + node.GetName() + "' has no integer value " + "at position " + pos, node.GetStartLine(), node.GetStartColumn()); } } /** * Returns the node string value at the specified position. If * either the node is null, or the value is not an instance of * the String class, this method will throw a parse exception * with the internal error type. * * @param node the parse tree node * @param pos the child position * * @return the value object * * @throws ParseException if either the node was null, or the * value wasn't a string */ protected string GetStringValue(Node node, int pos) { object value; value = GetValue(node, pos); if (value is string) { return (string) value; } else { throw new ParseException( ParseException.ErrorType.INTERNAL, "node '" + node.GetName() + "' has no string value " + "at position " + pos, node.GetStartLine(), node.GetStartColumn()); } } /** * Returns all the node values for all child nodes. * * @param node the parse tree node * * @return a list with all the child node values * * @since 1.3 */ protected ArrayList GetChildValues(Node node) { ArrayList result = new ArrayList(); Node child; ArrayList values; for (int i = 0; i < node.GetChildCount(); i++) { child = node.GetChildAt(i); values = child.GetAllValues(); if (values != null) { result.AddRange(values); } } return result; } } }