Bring over the FSM and then use it to make the calculator demo better.

main
Zed A. Shaw 4 days ago
parent 4b07ecac45
commit c0c63e5b2c
  1. 105
      demos/calc.cpp
  2. 28
      fsm.hpp

@ -6,6 +6,9 @@
#include "constants.hpp" #include "constants.hpp"
#include <fmt/xchar.h> #include <fmt/xchar.h>
#define FSM_DEBUG 1
#include "fsm.hpp"
constexpr const int WINDOW_WIDTH=300; constexpr const int WINDOW_WIDTH=300;
constexpr const int WINDOW_HEIGHT=400; constexpr const int WINDOW_HEIGHT=400;
@ -17,22 +20,85 @@ const std::unordered_map<string, wstring> LABELS {
{"btn5", L"5"}, {"btn6", L"6"}, {"btn7", L"7"}, {"btn5", L"5"}, {"btn6", L"6"}, {"btn7", L"7"},
{"btn8", L"8"}, {"btn9", L"9"}, {"mult", L"*"}, {"btn8", L"8"}, {"btn9", L"9"}, {"mult", L"*"},
{"minus", L"-"}, {"plus", L"+"}, {"neg", L"!"}, {"minus", L"-"}, {"plus", L"+"}, {"neg", L"!"},
{"dot", L"."}, {"eq", L"="} {"div", L"/"}, {"eq", L"="}
};
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 {
struct Calculator : DeadSimpleFSM<State, Event> {
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; guecs::UI $gui;
wstring $input; Calculator $fsm;
double $value = 0.0;
Calculator() { CalculatorUI() {
$gui.position(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); $gui.position(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
$gui.layout( $gui.layout(
"[*%(400)readout|_|_|_|clear]" "[*%(400)readout|_|_|_|clear]"
"[btn7|btn8|btn9|mult]" "[btn7|btn8|btn9|mult]"
"[btn4|btn5|btn6|minus]" "[btn4|btn5|btn6|minus]"
"[btn1|btn2|btn3|plus]" "[btn1|btn2|btn3|plus]"
"[neg|btn0|dot|eq]"); "[neg|btn0|eq|div]");
} }
void init() { void init() {
@ -79,39 +145,26 @@ struct Calculator {
case L'7': case L'7':
case L'8': case L'8':
case L'9': case L'9':
case L'.': $fsm.event(Event::NUMBER, op);
if($input.size() <= 10) {
$input += op;
}
break; break;
case L'*': case L'*':
$value = $value * std::stof($input);
$input = L"";
break;
case L'-': case L'-':
$value = $value - std::stof($input);
$input = L"";
break;
case L'+': case L'+':
$value = $value + std::stof($input); case L'/':
$input = L"";
break;
case L'!': case L'!':
$value = $value * -1.0; $fsm.event(Event::OP, op);
$input = L"";
break; break;
case L'=': case L'=':
$input = fmt::format(L"{}", $value); $fsm.event(Event::EQ, op);
$value = 0.0;
break; break;
case L'C': case L'C':
$input = L""; $fsm.event(Event::CLR, op);
break; break;
} }
auto readout = $gui.entity("readout"); auto readout = $gui.entity("readout");
auto& label = $gui.get<guecs::Textual>(readout); auto& label = $gui.get<guecs::Textual>(readout);
label.update($input); label.update($fsm.input);
} }
}; };
@ -126,7 +179,7 @@ int main() {
window.setFramerateLimit(FRAME_LIMIT); window.setFramerateLimit(FRAME_LIMIT);
window.setVerticalSyncEnabled(VSYNC); window.setVerticalSyncEnabled(VSYNC);
Calculator calc; CalculatorUI calc;
calc.init(); calc.init();
while(window.isOpen()) { while(window.isOpen()) {

@ -0,0 +1,28 @@
#pragma once
#include <fmt/core.h>
#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<typename S, typename E>
class DeadSimpleFSM {
protected:
// BUG: don't put this in your class because state() won't work
S $state = S::START;
public:
template<typename... Types>
void event(E event, Types... args);
void state(S next_state) {
$state = next_state;
}
bool in_state(S state) {
return $state == state;
}
};
Loading…
Cancel
Save