DerivationTreeGenerator.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-7.1.2).
003  * Copyright (c) 2007-2023 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * You may obtain a copy of the License at
008  *
009  *      http://www.apache.org/licenses/LICENSE-2.0
010  *
011  * Unless required by applicable law or agreed to in writing, software
012  * distributed under the License is distributed on an "AS IS" BASIS,
013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014  * See the License for the specific language governing permissions and
015  * limitations under the License.
016  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019  */
020 package io.jenetics.ext.grammar;
021 
022 import static java.util.Objects.requireNonNull;
023 
024 import java.util.Optional;
025 
026 import io.jenetics.ext.grammar.Cfg.NonTerminal;
027 import io.jenetics.ext.grammar.Cfg.Symbol;
028 import io.jenetics.ext.util.Tree;
029 import io.jenetics.ext.util.TreeNode;
030 
031 /**
032  * Standard implementation of a derivation-tree generator. The following code
033  * snippet lets you generate a derivation tree from a given grammar.
034  <pre>{@code
035  * final Cfg<String> cfg = Bnf.parse("""
036  *     <expr> ::= ( <expr> <op> <expr> ) | <num> <var> |  <fun> <arg><arg> )
037  *     <fun>  ::= FUN1 | FUN2
038  *     <arg>  ::= <expr> <var> <num>
039  *     <op>   ::= + | - | * | /
040  *     <var>  ::= x | y
041  *     <num>  ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
042  *     """
043  * );
044  *
045  * final var random = RandomGenerator.of("L64X256MixRandom");
046  * final var generator = new DerivationTreeGenerator<String>(
047  *     SymbolIndex.of(random),
048  *     1_000
049  * );
050  * final Tree<Symbol<String>, ?> tree = generator.generate(cfg);
051  * }</pre>
052  *
053  @see SentenceGenerator
054  *
055  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
056  @since 7.1
057  @version 7.1
058  */
059 public final class DerivationTreeGenerator<T>
060     implements Generator<T, Tree<Symbol<T>, ?>>
061 {
062 
063     private final SymbolIndex _index;
064     private final int _limit;
065 
066     /**
067      * Create a new derivation tree generator from the given parameters.
068      *
069      @param index the symbol index function used for generating the derivation
070      *        tree
071      @param limit the maximal allowed nodes of the tree. If the generated
072      *        tree exceeds this length, the generation is interrupted and
073      *        an empty tree is returned. If a tree is empty can be checked with
074      *        {@link Tree#isEmpty()}.
075      */
076     public DerivationTreeGenerator(
077         final SymbolIndex index,
078         final int limit
079     ) {
080         _index = requireNonNull(index);
081         _limit = limit;
082     }
083 
084     /**
085      * Generates a new derivation tree from the given grammar, <em>cfg</em>.
086      *
087      @see Tree#isEmpty()
088      *
089      @param cfg the generating grammar
090      @return a newly created derivation tree, or an empty tree if
091      *         the number of nodes exceed the defined node limit
092      */
093     @Override
094     public Tree<Symbol<T>, ?> generate(final Cfg<? extends T> cfg) {
095         final Cfg<T> grammar = Cfg.upcast(cfg);
096         final NonTerminal<T> start = grammar.start();
097         final TreeNode<Symbol<T>> symbols = TreeNode.of(start);
098 
099         int count = 1;
100         boolean expanded = true;
101         while (expanded) {
102             final Optional<TreeNode<Symbol<T>>> tree = symbols.leaves()
103                 .filter(leave ->
104                     leave.value() instanceof NonTerminal<T> nt &&
105                     cfg.rule(nt).isPresent()
106                 )
107                 .findFirst();
108 
109             if (tree.isPresent()) {
110                 final var t = tree.orElseThrow();
111                 final var selection = Generator.select(
112                     (NonTerminal<T>)t.value(),
113                     grammar,
114                     _index
115                 );
116                 count += selection.size();
117 
118                 if (count > _limit) {
119                     return TreeNode.of();
120                 }
121 
122                 selection.forEach(t::attach);
123             }
124 
125             expanded = tree.isPresent();
126         }
127 
128         return symbols;
129     }
130 
131 }