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.rewriting;
021
022 import static io.jenetics.internal.util.SerialIO.readInt;
023 import static io.jenetics.internal.util.SerialIO.writeInt;
024
025 import java.io.IOException;
026 import java.io.InvalidObjectException;
027 import java.io.ObjectInput;
028 import java.io.ObjectInputStream;
029 import java.io.ObjectOutput;
030 import java.io.Serial;
031 import java.io.Serializable;
032 import java.util.Objects;
033 import java.util.function.Function;
034 import java.util.stream.Collectors;
035
036 import io.jenetics.util.ISeq;
037 import io.jenetics.util.MSeq;
038
039 import io.jenetics.ext.util.TreeNode;
040
041 /**
042 * This class represents a Tree Rewrite System, which consists of a set of
043 * Tree Rewrite Rules.
044 * <pre>{@code
045 * final TRS<String> trs = TRS.parse(
046 * "add(0,$x) -> $x",
047 * "add(S($x),$y) -> S(add($x,$y))",
048 * "mul(0,$x) -> 0",
049 * "mul(S($x),$y) -> add(mul($x,$y),$y)"
050 * );
051 *
052 * // Converting the input tree into its normal form.
053 * final TreeNode<String> tree = TreeNode.parse("add(S(0),S(mul(S(0),S(S(0)))))");
054 * trs.rewrite(tree);
055 * assert tree.equals(TreeNode.parse("S(S(S(S(0))))"));
056 * }</pre>
057 *
058 * @see TreeRewriteRule
059 * @see <a href="https://en.wikipedia.org/wiki/Rewriting">TRS</a>
060 *
061 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
062 * @version 5.0
063 * @since 5.0
064 */
065 public final class TRS<V> implements TreeRewriter<V>, Serializable {
066
067 @Serial
068 private static final long serialVersionUID = 1L;
069
070 private final ISeq<TreeRewriteRule<V>> _rules;
071
072 /**
073 * Create a new TRS from the given rewrite rules.
074 *
075 * @param rules the rewrite rules the TRS consists of
076 * @throws NullPointerException if the given {@code rules} are {@code null}
077 * @throws IllegalArgumentException if the given {@code rules} sequence is
078 * empty
079 */
080 public TRS(final ISeq<TreeRewriteRule<V>> rules) {
081 if (rules.isEmpty()) {
082 throw new IllegalArgumentException("Rewrite rules must not be empty.");
083 }
084 _rules = rules;
085 }
086
087 @Override
088 public int rewrite(final TreeNode<V> tree, final int limit) {
089 return TreeRewriter.rewrite(tree, limit, _rules);
090 }
091
092 /**
093 * Maps {@code this} TRS from type {@code V} to type {@code B}.
094 *
095 * @param mapper the type mapper
096 * @param <B> the target type
097 * @return a new TRS for the mapped type
098 * @throws NullPointerException if the {@code mapper} is {@code null}
099 */
100 public <B> TRS<B> map(final Function<? super V, ? extends B> mapper) {
101 return new TRS<>(_rules.map(rule -> rule.map(mapper)));
102 }
103
104 @Override
105 public int hashCode() {
106 return _rules.hashCode();
107 }
108
109 @Override
110 public boolean equals(final Object obj) {
111 return obj == this ||
112 obj instanceof TRS<?> other &&
113 _rules.equals(other._rules);
114 }
115
116 @Override
117 public String toString() {
118 return _rules.stream()
119 .map(Objects::toString)
120 .collect(Collectors.joining("; "));
121 }
122
123 /**
124 * Create a new TRS from the given rewrite rules and type mapper.
125 *
126 * @param mapper the tree value type mapper
127 * @param rules the rewrite rules
128 * @param <V> the tree value type the rewriter is working on
129 * @return a new TRS
130 * @throws NullPointerException if one of the arguments is {@code null}
131 * @throws IllegalArgumentException if the given {@code rules} sequence is
132 * empty
133 */
134 public static <V> TRS<V> parse(
135 final Function<? super String, ? extends V> mapper,
136 final String... rules
137 ) {
138 return new TRS<>(
139 ISeq.of(rules)
140 .map(rule -> TreeRewriteRule.parse(rule, mapper))
141 );
142 }
143
144 /**
145 * Create a new TRS from the given rewrite rules.
146 *
147 * @param rules the rewrite rules
148 * @return a new TRS
149 * @throws NullPointerException if one of the arguments is {@code null}
150 * @throws IllegalArgumentException if the given {@code rules} sequence is
151 * empty
152 */
153 public static TRS<String> parse(final String... rules) {
154 return parse(Function.identity(), rules);
155 }
156
157 /* *************************************************************************
158 * Java object serialization
159 * ************************************************************************/
160
161 @Serial
162 private Object writeReplace() {
163 return new SerialProxy(SerialProxy.TRS_KEY, this);
164 }
165
166 @Serial
167 private void readObject(final ObjectInputStream stream)
168 throws InvalidObjectException
169 {
170 throw new InvalidObjectException("Serialization proxy required.");
171 }
172
173 void write(final ObjectOutput out) throws IOException {
174 writeInt(_rules.length(), out);
175 for (int i = 0; i < _rules.length(); ++i) {
176 out.writeObject(_rules.get(i));
177 }
178 }
179
180 @SuppressWarnings({"unchecked", "rawtypes"})
181 static Object read(final ObjectInput in)
182 throws IOException, ClassNotFoundException
183 {
184 final var length = readInt(in);
185 final var rules = MSeq.ofLength(length);
186 for (int i = 0; i < length; ++i) {
187 rules.set(i, in.readObject());
188 }
189
190 return new TRS(rules.toISeq());
191 }
192
193 }
|