From c0c63e5b2c00a7821e9e125e65fba9107650654f Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 6 May 2025 01:05:28 -0400 Subject: [PATCH] Bring over the FSM and then use it to make the calculator demo better. --- demos/calc.cpp | 105 +++++++++++++++++++++++++++++++++++++------------ fsm.hpp | 28 +++++++++++++ 2 files changed, 107 insertions(+), 26 deletions(-) create mode 100644 fsm.hpp diff --git a/demos/calc.cpp b/demos/calc.cpp index 7e5b369..01c5ffc 100644 --- a/demos/calc.cpp +++ b/demos/calc.cpp @@ -6,6 +6,9 @@ #include "constants.hpp" #include +#define FSM_DEBUG 1 +#include "fsm.hpp" + constexpr const int WINDOW_WIDTH=300; constexpr const int WINDOW_HEIGHT=400; @@ -17,22 +20,85 @@ const std::unordered_map LABELS { {"btn5", L"5"}, {"btn6", L"6"}, {"btn7", L"7"}, {"btn8", L"8"}, {"btn9", L"9"}, {"mult", L"*"}, {"minus", L"-"}, {"plus", L"+"}, {"neg", L"!"}, - {"dot", L"."}, {"eq", L"="} + {"div", L"/"}, {"eq", L"="} }; -struct Calculator { +enum class Event { + NUMBER=0, + OP=1, + CLR=2, + NOT=3, + EQ=4 +}; + +enum class State { + START=0, + CALCULATING=1, + CLEARED=3, + DISPLAYED=4 +}; + + +struct Calculator : DeadSimpleFSM { + wstring input; + double value = 0.0; + + void event(Event ev, wchar_t op) { + switch($state) { + FSM_STATE(State, START, ev, op); + FSM_STATE(State, CALCULATING, ev, op); + FSM_STATE(State, CLEARED, ev, op); + FSM_STATE(State, DISPLAYED, ev, op); + } + } + + void START(Event ev, wchar_t op) { + if(ev == Event::NUMBER) { + input += op; + state(State::CALCULATING); + } + } + + void CALCULATING(Event ev, wchar_t op) { + if(ev == Event::NUMBER) { + input += op; + } else if(ev == Event::OP) { + + } else if(ev == Event::NOT) { + + } else if(ev == Event::EQ) { + + } else if(ev == Event::CLR) { + input = L""; + state(State::CLEARED); + } + } + + void CLEARED(Event ev, wchar_t op) { + if(ev == Event::NUMBER) { + input += op; + state(State::CALCULATING); + } + } + + void DISPLAYED(Event ev, wchar_t op) { + fmt::println(L"ev={}, op={}", (int)ev, op); + state(State::CALCULATING); + } +}; + +struct CalculatorUI { guecs::UI $gui; - wstring $input; - double $value = 0.0; + Calculator $fsm; - Calculator() { + CalculatorUI() { $gui.position(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); $gui.layout( "[*%(400)readout|_|_|_|clear]" "[btn7|btn8|btn9|mult]" "[btn4|btn5|btn6|minus]" "[btn1|btn2|btn3|plus]" - "[neg|btn0|dot|eq]"); + "[neg|btn0|eq|div]"); } void init() { @@ -79,39 +145,26 @@ struct Calculator { case L'7': case L'8': case L'9': - case L'.': - if($input.size() <= 10) { - $input += op; - } + $fsm.event(Event::NUMBER, op); break; case L'*': - $value = $value * std::stof($input); - $input = L""; - break; case L'-': - $value = $value - std::stof($input); - $input = L""; - break; case L'+': - $value = $value + std::stof($input); - $input = L""; - break; + case L'/': case L'!': - $value = $value * -1.0; - $input = L""; + $fsm.event(Event::OP, op); break; case L'=': - $input = fmt::format(L"{}", $value); - $value = 0.0; + $fsm.event(Event::EQ, op); break; case L'C': - $input = L""; + $fsm.event(Event::CLR, op); break; } auto readout = $gui.entity("readout"); auto& label = $gui.get(readout); - label.update($input); + label.update($fsm.input); } }; @@ -126,7 +179,7 @@ int main() { window.setFramerateLimit(FRAME_LIMIT); window.setVerticalSyncEnabled(VSYNC); - Calculator calc; + CalculatorUI calc; calc.init(); while(window.isOpen()) { diff --git a/fsm.hpp b/fsm.hpp new file mode 100644 index 0000000..8729dd8 --- /dev/null +++ b/fsm.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#ifndef FSM_DEBUG +#define FSM_STATE(C, S, E, ...) case C::S: S(E, ##__VA_ARGS__); break +#else +#define FSM_STATE(C, S, E, ...) case C::S: fmt::println(">> " #C " " #S " event={}, state={}", int(E), int($state)); S(E, ##__VA_ARGS__); fmt::println("<< " #C " state={}", int($state)); break +#endif + +template +class DeadSimpleFSM { +protected: + // BUG: don't put this in your class because state() won't work + S $state = S::START; + +public: + template + void event(E event, Types... args); + + void state(S next_state) { + $state = next_state; + } + + bool in_state(S state) { + return $state == state; + } +};