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

io.github.qudtlib.support.parse.State Maven / Gradle / Ivy

package io.github.qudtlib.support.parse;

import java.util.*;
import java.util.stream.Collectors;

class State {
    private static int idCounter = 1;
    private final int id = State.idCounter++;
    private final String remainingInput;
    private final String leftoverInput;
    private final List parsedUnits;
    private final Deque> stateTransitionStack;
    private final boolean dividerSeen;
    private final boolean negativeExponentSeen;

    public State(
            String remainingInput,
            String leftoverInput,
            List parsedUnits,
            boolean dividerSeen,
            boolean negativeExponentSeen,
            Deque> transitionStack) {
        this.remainingInput = remainingInput;
        this.leftoverInput = leftoverInput;
        this.stateTransitionStack = new ArrayDeque<>(transitionStack);
        this.dividerSeen = dividerSeen;
        this.negativeExponentSeen = negativeExponentSeen;
        this.parsedUnits = new ArrayList<>(parsedUnits);
    }

    public State(String input, StateTransition... stateTransitionStack) {
        this.remainingInput = input;
        this.leftoverInput = null;
        this.stateTransitionStack = new ArrayDeque<>();
        this.stateTransitionStack.push(Arrays.asList(stateTransitionStack));
        this.dividerSeen = false;
        this.negativeExponentSeen = false;
        this.parsedUnits = new ArrayList<>();
    }

    private int currentBadness() {
        return this.parsedUnits.size();
    }

    private int expectedWorstCaseBadness() {
        return this.remainingInput.length();
    }

    public int badness() {
        return this.currentBadness() + this.expectedWorstCaseBadness();
    }

    List nextTransition() {
        //        System.out.println("state: " + this.toString());
        Deque> stackCopy = new ArrayDeque<>(this.stateTransitionStack);
        List resultingStates = new ArrayList<>();
        while (!this.stateTransitionStack.isEmpty()) {
            List topLevelTransitions = this.stateTransitionStack.pop();
            for (StateTransition transition : topLevelTransitions) {
                List resultingFromTransition = transition.apply(this);
                resultingStates.addAll(resultingFromTransition);
                //                System.out.println("transition: " + transition);
                //                    resultingFromTransition.stream()
                //                            .map(Object::toString)
                //                            .map(s -> " -> " + s)
                //                            .forEach(System.out::println);
                //                    if (resultingFromTransition.isEmpty()){
                ////                        System.out.println(" -> [no results]");
                // }
            }
        }
        if (!remainingInput.isEmpty()) {
            Optional nopTransitionState = this.nopTransition(stackCopy);
            nopTransitionState.ifPresent(resultingStates::add);
            //            if (nopTransitionState.isPresent()) {
            //                System.out.println("transition: NOP");
            //                System.out.println(" -> " + nopTransitionState);
            //            }
        }

        return resultingStates;
    }

    private Optional nopTransition(Deque> transitionStack) {
        String nextInput = remainingInput.isEmpty() ? "" : remainingInput.substring(1);
        String firstChar = remainingInput.isEmpty() ? "" : remainingInput.substring(0, 1);
        String newLeftover = Optional.ofNullable(leftoverInput).orElse("") + firstChar;
        String nextMatchable = newLeftover + (nextInput.isEmpty() ? "" : nextInput.substring(0, 1));
        Deque> newStack = new ArrayDeque<>(transitionStack);
        while (!newStack.isEmpty()) {
            List transitions = newStack.pop();
            transitions =
                    transitions.stream()
                            .filter(t -> t.mayMatchLater(nextMatchable))
                            .collect(Collectors.toList());
            if (!transitions.isEmpty()) {
                newStack.push(transitions);
                break;
            }
        }
        if (newStack.isEmpty()) {
            return Optional.empty();
        }
        return Optional.of(
                new State(
                        nextInput,
                        newLeftover,
                        parsedUnits,
                        dividerSeen,
                        negativeExponentSeen,
                        newStack));
    }

    boolean hasLeftoverInput() {
        return leftoverInput != null;
    }

    public String getRemainingInput() {
        return remainingInput;
    }

    public String getLeftoverInput() {
        return leftoverInput;
    }

    public String getMatchableInput() {
        return new StringBuilder()
                .append(this.leftoverInput == null ? "" : this.leftoverInput)
                .append(
                        this.remainingInput == null
                                ? ""
                                : this.remainingInput.isEmpty()
                                        ? ""
                                        : this.remainingInput.substring(0, 1))
                .toString();
    }

    public List getParsedUnits() {
        return parsedUnits;
    }

    public Deque> getStateTransitionStack() {
        return stateTransitionStack;
    }

    public boolean isDividerSeen() {
        return dividerSeen;
    }

    public boolean isNegativeExponentSeen() {
        return negativeExponentSeen;
    }

    @Override
    public String toString() {
        return "State{"
                + "id="
                + id
                + ", leftover='"
                + leftoverInput
                + '\''
                + ", remaining='"
                + remainingInput
                + '\''
                + ", dividerSeen="
                + dividerSeen
                + ", parsedUnits="
                + parsedUnits
                + ", transitions="
                + stateTransitionStack
                + '}';
    }

    public State withDividerSeen(String remainingInput, Deque> transitions) {
        if (this.negativeExponentSeen) {
            throw new IllegalStateException(
                    "negative exponent and divider are not allowed in the same unit expression");
        }
        return new State(
                remainingInput,
                null,
                new ArrayList<>(this.parsedUnits),
                true,
                this.negativeExponentSeen,
                transitions);
    }

    public String remainingInputForNextState() {
        if (isAtEnd()) {
            return "";
        }
        return this.remainingInput.substring(1);
    }

    public boolean isAtEnd() {
        return this.remainingInput == null || this.remainingInput.isEmpty();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        State state = (State) o;
        return dividerSeen == state.dividerSeen
                && Objects.equals(remainingInput, state.remainingInput)
                && Objects.equals(leftoverInput, state.leftoverInput)
                && Objects.equals(parsedUnits, state.parsedUnits)
                && Objects.equals(stateTransitionStack, state.stateTransitionStack);
    }

    @Override
    public int hashCode() {
        return Objects.hash(
                remainingInput, leftoverInput, parsedUnits, stateTransitionStack, dividerSeen);
    }

    public boolean isParseComplete() {
        return this.isAtEnd()
                && this.leftoverInput == null
                && this.stateTransitionStack.size()
                        == 1; // stack size 1 means we did not push a set of transition options that
        // we need to handle
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy