001/* 002 * Java Genetic Algorithm Library (jenetics-8.1.0). 003 * Copyright (c) 2007-2024 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 == 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}