All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.jenetics.ext.rewriting.TreeRewriteRule Maven / Gradle / Ivy

/*
 * Java Genetic Algorithm Library (jenetics-8.1.0).
 * Copyright (c) 2007-2024 Franz Wilhelmstötter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author:
 *    Franz Wilhelmstötter ([email protected])
 */
package io.jenetics.ext.rewriting;

import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static io.jenetics.internal.util.Hashes.hash;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.Serial;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import io.jenetics.ext.rewriting.TreePattern.Var;
import io.jenetics.ext.util.Tree;
import io.jenetics.ext.util.Tree.Path;
import io.jenetics.ext.util.TreeNode;

/**
 * Represents a tree rewrite rule. A rewrite rule consists of a match pattern,
 * which must be matched, and a substitution pattern, which is expanded and
 * replaces the variables in the pattern. Some simple arithmetic
 * rewrite rules.
 * 
 {@code
 *     add($x,0) -> $x
 *     mul($x,1) -> $x
 * } 
* The substitution pattern may only use variables, already defined in * the match pattern. So, the creation of the following rewrite rule s * would lead to an {@link IllegalArgumentException}: *
 {@code
 *     add($x,0) -> $y
 *     mul(0,1) -> mul($x,1)
 * } 
* * @see * Tree rewriting systems * * @author Franz Wilhelmstötter * @version 5.0 * @since 5.0 */ public final class TreeRewriteRule implements TreeRewriter, Serializable { @Serial private static final long serialVersionUID = 1L; private final TreePattern _left; private final TreePattern _right; /** * Create a new rewrite rule from the given matching ({@code left}) * and replacement ({@code right}) pattern. * * @param left the matching pattern of the rule * @param right the substitution pattern * @throws NullPointerException if one of the arguments is {@code null} * @throws IllegalArgumentException if the template pattern uses * variables not defined in the matcher pattern */ public TreeRewriteRule( final TreePattern left, final TreePattern right ) { _left = requireNonNull(left); _right = requireNonNull(right); final Set> undefined = new HashSet<>(_right.vars()); undefined.removeAll(_left.vars()); if (!undefined.isEmpty()) { throw new IllegalArgumentException(format( "Some template variables are not defined in the matcher '%s': %s", this, undefined.stream() .map(v -> format("%s", v)) .collect(Collectors.joining(", ")) )); } } /** * Return the rule matching pattern. * * @return the rule matching pattern */ public TreePattern left() { return _left; } /** * Return the replacement pattern of the rule. * * @return the replacement pattern of the rule */ public TreePattern right() { return _right; } /** * Maps {@code this} rewrite rule from type {@code V} to type {@code B}. * * @param mapper the type mapper * @param the target type * @return a new rewrite rule for the mapped type * @throws NullPointerException if the {@code mapper} is {@code null} */ public TreeRewriteRule map(final Function mapper) { return new TreeRewriteRule<>(_left.map(mapper), _right.map(mapper)); } @Override public int rewrite(final TreeNode tree, final int limit) { requireNonNull(tree); int rewritten = 0; Optional> result; do { result = left().matcher(tree).results().findFirst(); result.ifPresent(res -> rewrite(res, tree)); rewritten += result.isPresent() ? 1 : 0; } while (result.isPresent() && rewritten < limit); return rewritten; } private void rewrite( final TreeMatchResult result, final TreeNode tree ) { final Map, Tree> vars = result.vars(); final TreeNode r = _right.expand(vars); final Path path = result.tree().childPath(); tree.replaceAtPath(path, r); } @Override public int hashCode() { return hash(_left, hash(_right)); } @Override public boolean equals(final Object obj) { return obj == this || obj instanceof TreeRewriteRule other && _left.equals(other._left) && _right.equals(other._right); } @Override public String toString() { return format("%s -> %s", _left, _right); } /** * Compiles the string representation of a rewrite rule: *
 {@code
	 *     add($x,0) -> $x
	 *     mul($x,1) -> $x
	 * } 
* * @param the tree node type * @param rule the rewrite rule * @param mapper the mapper function which converts the node value into the * actual type {@code V} * @return a new rewrite rule, compiled from the given rule string * @throws IllegalArgumentException if the rewrite rule is invalid * @throws NullPointerException if on of the arguments is {@code null} */ public static TreeRewriteRule parse( final String rule, final Function mapper ) { final String[] parts = rule.split("->"); if (parts.length == 1) { throw new IllegalArgumentException(format( "Invalid rewrite rule; missing separator '->': %s", rule )); } if (parts.length > 2) { throw new IllegalArgumentException(format( "Invalid rewrite rule; found %d separators '->': %s", parts.length - 1, rule )); } return new TreeRewriteRule<>( TreePattern.compile(parts[0], mapper), TreePattern.compile(parts[1], mapper) ); } /** * Compiles the string representation of a rewrite rule: *
 {@code
	 *     add($x,0) -> $x
	 *     mul($x,1) -> $x
	 * } 
* * @param rule the rewrite rule * @return a new rewrite rule, compiled from the given rule string * @throws IllegalArgumentException if the rewrite rule is invalid * @throws NullPointerException if on of the arguments is {@code null} */ public static TreeRewriteRule parse(final String rule) { return parse(rule, Function.identity()); } /* ************************************************************************* * Java object serialization * ************************************************************************/ @Serial private Object writeReplace() { return new SerialProxy(SerialProxy.TREE_REWRITE_RULE, this); } @Serial private void readObject(final ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException("Serialization proxy required."); } void write(final ObjectOutput out) throws IOException { out.writeObject(_left); out.writeObject(_right); } @SuppressWarnings({"unchecked", "rawtypes"}) static Object read(final ObjectInput in) throws IOException, ClassNotFoundException { final TreePattern left = (TreePattern)in.readObject(); final TreePattern right = (TreePattern)in.readObject(); return new TreeRewriteRule(left, right); } }