import gleam/list
/// Automaton(state, symbol) represents a deterministic finite
/// automaton: a current state, a transition function, and an
/// acceptance predicate.
pub opaque type Automaton(state, symbol) {
Automaton(
initial_state: state,
transition: fn(state, symbol) -> state,
accepting: fn(state) -> Bool,
)
}
/// Construct an automaton from its initial state, transition
/// function, and acceptance predicate.
pub fn new(
initial_state initial_state: state,
transition transition: fn(state, symbol) -> state,
accepting accepting: fn(state) -> Bool,
) -> Automaton(state, symbol) {
Automaton(
initial_state: initial_state,
transition: transition,
accepting: accepting,
)
}
/// Return the initial state.
pub fn initial_state(automaton: Automaton(state, symbol)) -> state {
automaton.initial_state
}
/// Apply one transition from the supplied state using one input
/// symbol.
pub fn step(
automaton: Automaton(state, symbol),
state: state,
symbol: symbol,
) -> state {
automaton.transition(state, symbol)
}
/// Fold a list of input symbols over the automaton's transition
/// function and return the resulting state.
pub fn run(automaton: Automaton(state, symbol), inputs: List(symbol)) -> state {
list.fold(inputs, automaton.initial_state, fn(state, symbol) {
automaton.transition(state, symbol)
})
}
/// Return True when the automaton accepts the supplied input.
pub fn accepts(
automaton: Automaton(state, symbol),
inputs: List(symbol),
) -> Bool {
automaton.accepting(run(automaton, inputs))
}