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 }
|