io.jenetics.ext.grammar.DerivationTreeGenerator Maven / Gradle / Ivy
The newest version!
/*
* Java Genetic Algorithm Library (jenetics-8.1.0).
* Copyright (c) 2007-2024 Franz Wilhelmstötter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author:
* Franz Wilhelmstötter ([email protected])
*/
package io.jenetics.ext.grammar;
import static java.util.Objects.requireNonNull;
import java.util.Optional;
import io.jenetics.ext.grammar.Cfg.NonTerminal;
import io.jenetics.ext.grammar.Cfg.Symbol;
import io.jenetics.ext.util.Tree;
import io.jenetics.ext.util.TreeNode;
/**
* Standard implementation of a derivation-tree generator. The following code
* snippet lets you generate a derivation tree from a given grammar.
* {@snippet lang="java":
* final Cfg cfg = Bnf.parse("""
* ::= ( ) | | | ( , )
* ::= FUN1 | FUN2
* ::= | |
* ::= + | - | * | /
* ::= x | y
* ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
* """
* );
*
* final var random = RandomGenerator.of("L64X256MixRandom");
* final var generator = new DerivationTreeGenerator(
* SymbolIndex.of(random),
* 1_000
* );
* final Tree, ?> tree = generator.generate(cfg);
* }
*
* @see SentenceGenerator
*
* @author Franz Wilhelmstötter
* @since 7.1
* @version 7.1
*/
public final class DerivationTreeGenerator
implements Generator, ?>>
{
private final SymbolIndex _index;
private final int _limit;
/**
* Create a new derivation tree generator from the given parameters.
*
* @param index the symbol index function used for generating the derivation
* tree
* @param limit the maximal allowed nodes of the tree. If the generated
* tree exceeds this length, the generation is interrupted and
* an empty tree is returned. If a tree is empty can be checked with
* {@link Tree#isEmpty()}.
*/
public DerivationTreeGenerator(
final SymbolIndex index,
final int limit
) {
_index = requireNonNull(index);
_limit = limit;
}
/**
* Generates a new derivation tree from the given grammar, cfg.
*
* @see Tree#isEmpty()
*
* @param cfg the generating grammar
* @return a newly created derivation tree, or an empty tree if
* the number of nodes exceeds the defined node limit
*/
@Override
public Tree, ?> generate(final Cfg extends T> cfg) {
final Cfg grammar = Cfg.upcast(cfg);
final NonTerminal start = grammar.start();
final TreeNode> symbols = TreeNode.of(start);
int count = 1;
boolean expanded = true;
while (expanded) {
final Optional>> tree = symbols.leaves()
.filter(leave ->
leave.value() instanceof NonTerminal nt &&
cfg.rule(nt).isPresent()
)
.findFirst();
if (tree.isPresent()) {
final var t = tree.orElseThrow();
final var selection = Generator.select(
(NonTerminal)t.value(),
grammar,
_index
);
count += selection.size();
if (count > _limit) {
return TreeNode.of();
}
selection.forEach(t::attach);
}
expanded = tree.isPresent();
}
return symbols;
}
}