/*
* Copyright 2007-2013
* Licensed under GNU Lesser General Public License
*
* This file is part of EpochX: genetic programming software for research
*
* EpochX is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EpochX 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with EpochX. If not, see <http://www.gnu.org/licenses/>.
*
* The latest version is available from: http://www.epochx.org
*/
package org.epochx.grammar;
import java.util.*;
/**
* A GrammarRule is a component of a grammar parse tree that matches a
* rule of a BNF language grammar. Each rule should have one or more
* productions which are the possible valid mappings that this rule may resolve
* to.
*/
public class GrammarRule implements GrammarNode, Cloneable {
// The available options this rule may resolve to.
private final List<GrammarProduction> productions;
// The name of this rule without angle brackets.
private String name;
// Whether the rule is self-referencing.
private boolean recursive;
// The minimum depth required for this rule to be fully resolved.
private int minDepth;
/**
* Constructs a <code>GrammarRule</code> with the specified name label and
* the production choices.
*
* @param name the label that identifies this non-terminal rule.
* @param productions a list of all the <code>GrammarProductions</code> that
* are possible mappings for this rule.
*/
public GrammarRule(String name, List<GrammarProduction> productions) {
this.name = name;
this.productions = productions;
}
/**
* Constructs a <code>GrammarRule</code> with the specified name label and
* an empty list of <code>GrammarProductions</code>. Productions can be
* added after construction using the <code>addProduction</code> method.
*
* @param name the label that identifies this non-terminal rule.
*/
public GrammarRule(String name) {
this(name, new ArrayList<GrammarProduction>());
}
/**
* Constructs a <code>GrammarRule</code> with no specified name and an empty
* list of <code>GrammarProductions</code>.
*/
public GrammarRule() {
this(null);
}
/**
* Append the given production to the list of <code>GrammarProduction</code>
* options.
*
* @param production the <code>GrammarProduction</code> instance to be
* appended to this rule's list of productions.
*/
public void addProduction(GrammarProduction production) {
productions.add(production);
}
/**
* Inserts the given production at the specified position in the list
* of this rule's productions. The production currently at that position (if
* any) and any subsequent productions will be shifted to the right.
*
* @param index the position at which the specified production is to be
* inserted.
* @param production the <code>GrammarProduction</code> instance to be
* inserted.
*/
public void addProduction(int index, GrammarProduction production) {
productions.add(index, production);
}
/**
* Replaces the <code>GrammarProduction</code> at the specified position in
* the list of this rule's productions.
*
* @param index the position of the <code>GrammarProduction</code> to
* replace.
* @param production the <code>GrammarProduction</code> instance to be
* stored at the specified position.
*/
public void setProduction(int index, GrammarProduction production) {
productions.set(index, production);
}
/**
* Returns the <code>GrammarProduction</code> at the specified position in
* this rule's list of productions.
*
* @param index the index of the <code>GrammarProduction</code> to return.
* @return the <code>GrammarProduction</code> that is at the specified
* index in the list of productions.
*/
public GrammarProduction getProduction(int index) {
return productions.get(index);
}
/**
* Returns a list of this rule's productions.
*
* @return a list of this rule's productions.
*/
public List<GrammarProduction> getProductions() {
return productions;
}
/**
* Returns the quantity of productions in this rule.
*
* @return the number of productions in this rule.
*/
public int getNoProductions() {
return productions.size();
}
/**
* Returns the name of this rule, without the angle brackets.
*
* @return the name that references this rule.
*/
public String getName() {
return name;
}
/**
* Returns whether this grammar rule refers to itself directly or
* indirectly.
*
* <p>
* A rule is defined as recursive if any of the following are true:
* <ul>
* <li>The right hand side of the production rule contains the non-terminal
* on the left hand side.</li>
* <li>The right hand side of the rule contains a non-terminal which points
* to a rule that is recursive due to any of the other two reasons.</li>
* <li>The right hand side of the rule may contain a non-terminal that leads
* back to the same production rule.</li>
* </ul>
*
* @return true if this grammar rule is recursive, false otherwise.
*/
public boolean isRecursive() {
return recursive;
}
/**
* Specifies whether this rule recursively refers to itself either directly
* or indirectly.
*
* @param recursive whether this grammar rule recursively refers to itself.
*/
public void setRecursive(boolean recursive) {
this.recursive = recursive;
}
/**
* Gets the minimum depth required to resolve this rule fully to literals.
*
* @return the minimum depth required to resolve to terminal symbols.
*/
public int getMinDepth() {
return minDepth;
}
/**
* Sets the minimum depth required for this rule to resolve fully to all
* literals.
*
* @param minDepth the minimum depth required to get to all terminals.
*/
public void setMinDepth(int minDepth) {
this.minDepth = minDepth;
}
/**
* Makes and returns a copy of this <code>GrammarRule</code> instance.
*/
@Override
public GrammarRule clone() {
GrammarRule clone = null;
try {
clone = (GrammarRule) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
clone.name = name;
clone.recursive = recursive;
clone.minDepth = minDepth;
// Clone the grammar productions (but this will not clone their rules).
for (GrammarProduction p: productions) {
clone.productions.add(p.clone());
}
return clone;
}
/**
* Returns a string representation of this rule.
*
* @return a string representation of this rule.
*/
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder();
buffer.append('<');
buffer.append(name);
buffer.append('>');
buffer.append(" ::= ");
for (int i = 0; i < productions.size(); i++) {
if (i > 0) {
buffer.append(" | ");
}
buffer.append(productions.get(i).toString());
}
return buffer.toString();
}
}