001/* 002 * Java Genetic Algorithm Library (jenetics-8.3.0). 003 * Copyright (c) 2007-2025 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.rewriting; 021 022import static io.jenetics.internal.util.SerialIO.readInt; 023import static io.jenetics.internal.util.SerialIO.writeInt; 024 025import java.io.IOException; 026import java.io.InvalidObjectException; 027import java.io.ObjectInput; 028import java.io.ObjectInputStream; 029import java.io.ObjectOutput; 030import java.io.Serial; 031import java.io.Serializable; 032import java.util.Objects; 033import java.util.function.Function; 034import java.util.stream.Collectors; 035 036import io.jenetics.util.ISeq; 037import io.jenetics.util.MSeq; 038 039import 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 * {@snippet lang="java": 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 * } 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 */ 065public 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 instanceof TRS<?> other && 112 _rules.equals(other._rules); 113 } 114 115 @Override 116 public String toString() { 117 return _rules.stream() 118 .map(Objects::toString) 119 .collect(Collectors.joining("; ")); 120 } 121 122 /** 123 * Create a new TRS from the given rewrite rules and type mapper. 124 * 125 * @param mapper the tree value type mapper 126 * @param rules the rewrite rules 127 * @param <V> the tree value type the rewriter is working on 128 * @return a new TRS 129 * @throws NullPointerException if one of the arguments is {@code null} 130 * @throws IllegalArgumentException if the given {@code rules} sequence is 131 * empty 132 */ 133 public static <V> TRS<V> parse( 134 final Function<? super String, ? extends V> mapper, 135 final String... rules 136 ) { 137 return new TRS<>( 138 ISeq.of(rules) 139 .map(rule -> TreeRewriteRule.parse(rule, mapper)) 140 ); 141 } 142 143 /** 144 * Create a new TRS from the given rewrite rules. 145 * 146 * @param rules the rewrite rules 147 * @return a new TRS 148 * @throws NullPointerException if one of the arguments is {@code null} 149 * @throws IllegalArgumentException if the given {@code rules} sequence is 150 * empty 151 */ 152 public static TRS<String> parse(final String... rules) { 153 return parse(Function.identity(), rules); 154 } 155 156 /* ************************************************************************* 157 * Java object serialization 158 * ************************************************************************/ 159 160 @Serial 161 private Object writeReplace() { 162 return new SerialProxy(SerialProxy.TRS_KEY, this); 163 } 164 165 @Serial 166 private void readObject(final ObjectInputStream stream) 167 throws InvalidObjectException 168 { 169 throw new InvalidObjectException("Serialization proxy required."); 170 } 171 172 void write(final ObjectOutput out) throws IOException { 173 writeInt(_rules.length(), out); 174 for (int i = 0; i < _rules.length(); ++i) { 175 out.writeObject(_rules.get(i)); 176 } 177 } 178 179 @SuppressWarnings({"unchecked", "rawtypes"}) 180 static Object read(final ObjectInput in) 181 throws IOException, ClassNotFoundException 182 { 183 final var length = readInt(in); 184 final var rules = MSeq.ofLength(length); 185 for (int i = 0; i < length; ++i) { 186 rules.set(i, in.readObject()); 187 } 188 189 return new TRS(rules.toISeq()); 190 } 191 192}