001/*
002 * Java Genetic Algorithm Library (jenetics-9.0.0).
003 * Copyright (c) 2007-2026 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 */
020package io.jenetics.ext.grammar;
021
022import static java.util.Objects.requireNonNull;
023
024import java.util.Optional;
025
026import io.jenetics.ext.grammar.Cfg.NonTerminal;
027import io.jenetics.ext.grammar.Cfg.Symbol;
028import io.jenetics.ext.grammar.Cfg.Terminal;
029import io.jenetics.ext.util.Tree;
030import io.jenetics.ext.util.TreeNode;
031
032/**
033 * Standard implementation of a derivation-tree generator. The following code
034 * snippet lets you generate a derivation tree from a given grammar.
035 * {@snippet lang="java":
036 * final Cfg<String> cfg = Bnf.parse("""
037 *     <expr> ::= ( <expr> <op> <expr> ) | <num> | <var> |  <fun> ( <arg>, <arg> )
038 *     <fun>  ::= FUN1 | FUN2
039 *     <arg>  ::= <expr> | <var> | <num>
040 *     <op>   ::= + | - | * | /
041 *     <var>  ::= x | y
042 *     <num>  ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
043 *     """
044 * );
045 *
046 * final var random = RandomGenerator.of("L64X256MixRandom");
047 * final var generator = new DerivationTreeGenerator<String>(
048 *     SymbolIndex.of(random),
049 *     1_000
050 * );
051 * final Tree<Symbol<String>, ?> tree = generator.generate(cfg);
052 * }
053 *
054 * @see SentenceGenerator
055 *
056 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
057 * @since 7.1
058 * @version 7.1
059 */
060public final class DerivationTreeGenerator<T>
061        implements Generator<T, Tree<Symbol<T>, ?>>
062{
063
064        private final SymbolIndex _index;
065        private final int _limit;
066
067        /**
068         * Create a new derivation tree generator from the given parameters.
069         *
070         * @param index the symbol index function used for generating the derivation
071         *        tree
072         * @param limit the maximal allowed nodes of the tree. If the generated
073         *        tree exceeds this length, the generation is interrupted and
074         *        an empty tree is returned. If a tree is empty can be checked with
075         *        {@link Tree#isEmpty()}.
076         */
077        public DerivationTreeGenerator(
078                final SymbolIndex index,
079                final int limit
080        ) {
081                _index = requireNonNull(index);
082                _limit = limit;
083        }
084
085        /**
086         * Generates a new derivation tree from the given grammar, <em>cfg</em>.
087         *
088         * @see Tree#isEmpty()
089         *
090         * @param cfg the generating grammar
091         * @return a newly created derivation tree, or an empty tree if
092         *         the number of nodes exceeds the defined node limit
093         */
094        @Override
095        public Tree<Symbol<T>, ?> generate(final Cfg<? extends T> cfg) {
096                final Cfg<T> grammar = Cfg.upcast(cfg);
097                final NonTerminal<T> start = grammar.start();
098                final TreeNode<Symbol<T>> symbols = TreeNode.of(start);
099
100                int count = 1;
101                boolean expanded = true;
102                while (expanded) {
103                        final Optional<TreeNode<Symbol<T>>> tree = symbols.leaves()
104                                .filter(leave ->
105                                        leave.value() instanceof NonTerminal<T> nt &&
106                                        cfg.rule(nt).isPresent()
107                                )
108                                .findFirst();
109
110                        if (tree.isPresent()) {
111                                final var t = tree.orElseThrow();
112                                final var selection = Generator.select(
113                                        (NonTerminal<T>)t.value(),
114                                        grammar,
115                                        _index
116                                );
117                                count += selection.size();
118
119                                if (count > _limit) {
120                                        return TreeNode.of();
121                                }
122
123                                selection.forEach(t::attach);
124                        }
125
126                        expanded = tree.isPresent();
127                }
128
129                return symbols;
130        }
131
132        public static <T> Tree<Terminal<T>, ?>
133        toAst(final Tree<Symbol<T>, ?> derivationTree) {
134                final TreeNode<Terminal<T>> abstractSyntaxTree = TreeNode.of();
135                prune(derivationTree, abstractSyntaxTree);
136                return abstractSyntaxTree;
137        }
138
139        private static <T> void prune(
140                final Tree<Symbol<T>, ?> derivationTree,
141                final TreeNode<Terminal<T>> abstractSyntaxTree
142        ) {
143                if (derivationTree.value() instanceof Terminal<T> terminal) {
144                        abstractSyntaxTree.value(terminal);
145                }
146
147                for (int i = 0; i < derivationTree.childCount(); ++i) {
148                        TreeNode<Terminal<T>> targetChild = abstractSyntaxTree;
149                        if (abstractSyntaxTree.value() != null) {
150                                targetChild = TreeNode.of();
151                                abstractSyntaxTree.attach(targetChild);
152                        }
153
154                        prune(derivationTree.childAt(i), targetChild);
155                }
156        }
157
158        /*
159        public static <T> void prune(
160                final Tree<? extends T, ?> source,
161                final TreeNode<T> target,
162                final Predicate<? super T> filter
163        ) {
164                if (filter.test(source.value())) {
165                        target.value(source.value());
166                }
167
168                for (int i = 0; i < source.childCount(); ++i) {
169                        TreeNode<T> targetChild = target;
170                        if (target.value() != null) {
171                                targetChild = TreeNode.of();
172                                target.attach(targetChild);
173                        }
174
175                        prune(source.childAt(i), targetChild, filter);
176                }
177        }
178         */
179
180}