/* Copyright 2009-2016 David Hadka * * This file is part of the MOEA Framework. * * The MOEA Framework 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. * * The MOEA Framework 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 the MOEA Framework. If not, see <http://www.gnu.org/licenses/>. */ package org.moeaframework.util.grammar; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Stack; /** * A context-free grammar. The rule at index {@code 0} is the starting rule when * building derivation trees. * * @see Rule */ public class ContextFreeGrammar { /** * The rules contained in this grammar. */ private final List<Rule> rules; /** * The maximum number of times the builder will wrap around the codon array * before failing to produce a valid derivation. */ private int wrapLimit; /** * Constructs an empty grammar. */ public ContextFreeGrammar() { super(); rules = new ArrayList<Rule>(); wrapLimit = 10; } /** * Returns the maximum number of times the builder will wrap around the * codon array before failing to produce a valid derivation. * * @return the maximum number of times the builder will wrap around the * codon array before failing to produce a valid derivation */ public int getWrapLimit() { return wrapLimit; } /** * Sets the maximum number of times the builder will wrap around the codon * array before failing to produce a valid derivation. * * @param wrapLimit the maximum number of times the builder will wrap around * the codon array before failing to produce a valid derivation */ public void setWrapLimit(int wrapLimit) { this.wrapLimit = wrapLimit; } /** * Adds a rule to this grammar. * * @param rule the rule to be added */ public void add(Rule rule) { rules.add(rule); } /** * Removes a rule from this grammar. * * @param rule the rule to be removed */ public void remove(Rule rule) { rules.remove(rule); } /** * Returns the number of rules contained in this grammar. * * @return the number of rules contained in this grammar */ public int size() { return rules.size(); } /** * Returns the rule at the specified index. * * @param index the index of the rule to be returned * @return the rule at the specified index * @throws IndexOutOfBoundsException if index is out of range {@code ((index * < 0) || (index >= size())} */ public Rule get(int index) { return rules.get(index); } /** * Returns the rule for the specified symbol; or {@code null} if no rule * with the specified symbol exists. * * @param symbol the symbol of the rule to be returned * @return the rule for the specified symbol; or {@code null} if no rule * with the specified symbol exists */ public Rule get(Symbol symbol) { for (Rule rule : rules) { if (rule.getSymbol().equals(symbol)) { return rule; } } return null; } /** * Returns the grammar derivation using the construction rules of * Grammatical Evolution on the specified codon array; or {@code null} if * the codon array failed to produce a valid derivation Whenever the * derivation encounters a rule with multiple productions, the production * used to expand the rule is chosen using this codon array. * * @param array the codon array * @return the grammar derivation using the construction rules of * Grammatical Evolution on the specified codon array; or * {@code null} if the codon array failed to produce a valid * derivation * @throws GrammarException if the codon array is empty */ public String build(int[] array) { if (array.length == 0) { throw new GrammarException("codon array is empty"); } StringBuilder sb = new StringBuilder(); Stack<Symbol> remaining = new Stack<Symbol>(); int index = 0; int wraps = 0; remaining.push(rules.get(0).getSymbol()); while (!remaining.isEmpty()) { Symbol symbol = remaining.pop(); if (symbol.isTerminal()) { sb.append(symbol.getValue()); } else { Rule rule = get(symbol); int productionIndex = 0; if (rule.size() > 1) { productionIndex = array[index] % rule.size(); index++; if (index >= array.length) { index = 0; wraps++; if (wraps > wrapLimit) { return null; } } } Production production = rule.get(productionIndex); for (int i = production.size() - 1; i >= 0; i--) { remaining.push(production.get(i)); } } } return sb.toString(); } /** * Returns {@code true} if this grammar is valid; {@code false} otherwise. A * grammar is valid if it is non-empty and all non-terminal symbols are * defined by a rule in the grammar. * * @return {@code true} if this grammar is valid; {@code false} otherwise */ public boolean isValid() { if (size() == 0) { return false; } Set<Symbol> symbols = new HashSet<Symbol>(); for (int i = 0; i < size(); i++) { Rule rule = get(i); for (int j = 0; j < rule.size(); j++) { Production production = rule.get(j); for (int k = 0; k < production.size(); k++) { Symbol symbol = production.get(k); if (!symbol.isTerminal()) { symbols.add(symbol); } } } } for (Symbol symbol : symbols) { if (get(symbol) == null) { return false; } } return true; } }