diff --git a/lib/automaton.dart b/lib/automaton.dart deleted file mode 100644 index 04cf116..0000000 --- a/lib/automaton.dart +++ /dev/null @@ -1,99 +0,0 @@ -class NoTransitionPossibleException implements Exception { - @override - String errMsg() => "The transition graph allows no transition"; -} - -/// A deterministic finite automaton. [T] is the state type while -/// [I] is the input type. -/// Edges of the node must be added with [addTransition]. If a trap state -/// is required, it can be set in the constructor. -class DeterministicFiniteAutomaton { - /// The current state of the DFA - T _state; - /// The edges of the DFA: State x Input -> State - Map> _transitions; - /// Trap state - T? trapState; - - /// The argument is the initial state - DeterministicFiniteAutomaton(this._state, { this.trapState }) : _transitions = {}; - - T get state => _state; - - void addTransition(T oldState, I input, T newState) { - assert(oldState != trapState); - // These are handled implicitly if no transition has been found - assert(newState != trapState); - - if (!_transitions.containsKey(oldState)) { - _transitions[oldState] = {}; - } - - _transitions[oldState]![input] = newState; - } - - /// Transition the DFA based on its current state and the input [input]. - void onInput(I input) { - final newState = _transitions[_state]?[input]; - if (newState == null) { - // Go to the trap state if we can - if (trapState != null) { - _state = trapState!; - return; - } else { - throw NoTransitionPossibleException(); - } - } - - _state = newState; - } - - /// Returns where [input] would take the automaton to. Returns null if no transition - /// is possible, ignoring trap transitions. - T? peekTransition(I input) { - if (!_transitions.containsKey(_state) || !_transitions[_state]!.containsKey(input)) { - return null; - } - - return _transitions[_state]![input]!; - } -} - -typedef MealyAutomatonCallback = void Function(T oldState, I input); -class MealyAutomaton { - /// The base automaton - final DeterministicFiniteAutomaton _automaton; - /// Mapping of State x Input -> Output callback - Map>> _outputs; - /// Trap state - MealyAutomatonCallback? trapCallback; - - // TODO: Assert that trapState != null implies trapCallback != null. - MealyAutomaton(T initialState, { T? trapState, this.trapCallback }) - : _outputs = {}, - _automaton = DeterministicFiniteAutomaton(initialState, trapState: trapState); - - T get state => _automaton.state; - - void addTransition(T oldState, I input, T newState, MealyAutomatonCallback callback) { - _automaton.addTransition(oldState, input, newState); - - if (!_outputs.containsKey(oldState)) { - _outputs[oldState] = {}; - } - - _outputs[oldState]![input] = callback; - } - - void onInput(I input) { - final _state = _automaton.state; - if (_automaton.peekTransition(input) == null && trapCallback == null) { - throw new NoTransitionPossibleException(); - } - - final callback = _outputs[_state]?[input] ?? trapCallback!; - - _automaton.onInput(input); - callback(_state, input); - } -} diff --git a/lib/awaitabledatasender.dart b/lib/awaitabledatasender.dart index 2fa34e0..341ea80 100644 --- a/lib/awaitabledatasender.dart +++ b/lib/awaitabledatasender.dart @@ -72,6 +72,7 @@ abstract class AwaitableDataSender< /// Future will be returned that can be used to await a response. If it /// is false, then null will be imediately resolved. Future sendData(S data, { bool awaitable = true, @visibleForTesting String? id }) async { + // ignore: no_leading_underscores_for_local_identifiers final _id = id ?? _uuid.v4(); Future future = Future.value(null); _log.fine("sendData: Waiting to acquire lock..."); diff --git a/lib/lists.dart b/lib/lists.dart new file mode 100644 index 0000000..42cd83d --- /dev/null +++ b/lib/lists.dart @@ -0,0 +1,16 @@ +/// A wrapper around List.firstWhere that does not throw but instead just +/// returns true if [test] returns true for an element or false if [test] never +/// returned true. +bool listContains(List list, bool Function(T element) test) { + return firstWhereOrNull(list, test) != null; +} + +/// A wrapper around [List.firstWhere] that does not throw but instead just +/// return null if [test] never returned true +T? firstWhereOrNull(List list, bool Function(T element) test) { + try { + return list.firstWhere(test); + } catch(e) { + return null; + } +} diff --git a/lib/moxlib.dart b/lib/moxlib.dart index 25c0192..1b8fee9 100644 --- a/lib/moxlib.dart +++ b/lib/moxlib.dart @@ -1,5 +1,5 @@ library moxlib; export "awaitabledatasender.dart"; -export "automaton.dart"; +export "lists.dart"; export "math.dart"; diff --git a/test/automaton_test.dart b/test/automaton_test.dart deleted file mode 100644 index 70a1f97..0000000 --- a/test/automaton_test.dart +++ /dev/null @@ -1,79 +0,0 @@ -import "package:moxlib/automaton.dart"; - -import "package:test/test.dart"; - -enum States { - a, b, c, trap -} - -void main() { - test("Test a simple DFA", () { - final automaton = DeterministicFiniteAutomaton(States.a); - automaton.addTransition(States.a, 1, States.b); - automaton.addTransition(States.b, 2, States.c); - automaton.addTransition(States.c, 3, States.a); - - expect(automaton.state, States.a); - automaton.onInput(1); - expect(automaton.state, States.b); - automaton.onInput(2); - expect(automaton.state, States.c); - automaton.onInput(3); - expect(automaton.state, States.a); - }); - - test("Test a simple DFA with a trap state", () { - final automaton = DeterministicFiniteAutomaton(States.a, trapState: States.trap); - automaton.addTransition(States.a, 1, States.b); - automaton.addTransition(States.b, 2, States.c); - automaton.addTransition(States.c, 3, States.a); - - expect(automaton.state, States.a); - automaton.onInput(1); - expect(automaton.state, States.b); - automaton.onInput(2); - expect(automaton.state, States.c); - automaton.onInput(4); - expect(automaton.state, States.trap); - - // Transitioning away from the trap state should not be possible - automaton.onInput(5); - expect(automaton.state, States.trap); - }); - - test("Test a simple Mealy Automaton", () { - bool called = false; - final callback = (state, input) { - called = true; - }; - final automaton = MealyAutomaton(States.a); - - automaton.addTransition(States.a, 1, States.b, callback); - - automaton.onInput(1); - - expect(automaton.state, States.b); - expect(called, true); - }); - - test("Test a simple Mealy Automaton with a trap state", () { - bool called = false; - bool trapCalled = false; - final callback = (state, input) { - called = true; - }; - final trapCallback = (state, input) { - trapCalled = true; - }; - final automaton = MealyAutomaton(States.a, trapState: States.trap, trapCallback: trapCallback); - - automaton.addTransition(States.a, 1, States.b, callback); - - automaton.onInput(1); - expect(called, true); - - automaton.onInput(1); - expect(automaton.state, States.trap); - expect(trapCalled, true); - }); -}