ExecutionStep.java

package org.eu.autogex.trace;

import java.util.Set;
import java.util.stream.Collectors;
import org.eu.autogex.core.State;

/**
 * Represents a single evaluation step within an automaton's execution trace. Maintains immutability
 * utilizing Java Records.
 *
 * @param fromStates The set of active states before reading the symbol.
 * @param symbolRead The input symbol read during this step (null represents an epsilon transition).
 * @param toStates The set of active states reached after evaluating the symbol.
 */
public record ExecutionStep(Set<State> fromStates, Character symbolRead, Set<State> toStates) {

    /**
     * Formats the execution step into a highly readable mathematical string. Example: {q0, q1}
     * --[a]--> {q2}
     *
     * @return The formatted trace string.
     */
    @Override
    public String toString() {
        String from = formatStates(fromStates);
        String to = formatStates(toStates);
        String sym = symbolRead == null ? "ε" : symbolRead.toString();

        return String.format("%s --[%s]--> %s", from, sym, to);
    }

    private String formatStates(Set<State> states) {
        if (states == null || states.isEmpty()) {
            return "∅";
        }
        return "{"
                + states.stream().map(State::getName).sorted().collect(Collectors.joining(", "))
                + "}";
    }
}