Switched to an RPN calculator since they're simpler to implement.

main
Zed A. Shaw 4 days ago
parent bd0ec9e095
commit 7847ffdcbd
  1. 197
      demos/calc.cpp

@ -5,6 +5,7 @@
#include "guecs.hpp" #include "guecs.hpp"
#include "constants.hpp" #include "constants.hpp"
#include <fmt/xchar.h> #include <fmt/xchar.h>
#include <deque>
#define FSM_DEBUG 1 #define FSM_DEBUG 1
#include "fsm.hpp" #include "fsm.hpp"
@ -14,94 +15,114 @@ constexpr const int WINDOW_HEIGHT=400;
using std::string, std::wstring; using std::string, std::wstring;
const std::unordered_map<string, wstring> LABELS {
{"readout", L""}, {"clear", L"CLR"}, {"btn0", L"0"}, {"btn1", L"1"},
{"btn2", L"2"}, {"btn3", L"3"}, {"btn4", L"4"},
{"btn5", L"5"}, {"btn6", L"6"}, {"btn7", L"7"},
{"btn8", L"8"}, {"btn9", L"9"}, {"mult", L"*"},
{"minus", L"-"}, {"plus", L"+"}, {"neg", L"±"},
{"div", L"/"}, {"eq", L"="} };
enum class Event { enum class Event {
NUMBER, ADD, SUB, MUL, NUMBER, ADD, SUB, MUL,
DIV, CLR, NOT, EQ, DIV, CLR, NEG, EQ, PUSH, POP
};
enum class State {
START,
CALCULATING,
CLEARED,
DISPLAYED
}; };
const std::unordered_map<string, wstring> LABELS {
struct Calculator : DeadSimpleFSM<State, Event> { {"readout", L""}, {"clear", L"CLR"}, {"btn0", L"0"}, {"btn1", L"1"},
{"btn2", L"2"}, {"btn3", L"3"}, {"btn4", L"4"},
{"btn5", L"5"}, {"btn6", L"6"}, {"btn7", L"7"},
{"btn8", L"8"}, {"btn9", L"9"}, {"mul", L"*"},
{"sub", L"-"}, {"add", L"+"}, {"neg", L"±"},
{"div", L"/"}, {"eq", L"="},
{"push", L"^"}, {"pop", L"v"} };
const std::unordered_map<wchar_t, Event> EVENTS {
{L'0', Event::NUMBER}, {L'1', Event::NUMBER},
{L'2', Event::NUMBER}, {L'3', Event::NUMBER},
{L'4', Event::NUMBER}, {L'5', Event::NUMBER},
{L'6', Event::NUMBER}, {L'7', Event::NUMBER},
{L'8', Event::NUMBER}, {L'9', Event::NUMBER},
{L'*', Event::MUL}, {L'-', Event::SUB},
{L'+', Event::ADD}, {L'/', Event::DIV},
{L'C', Event::CLR}, {L'^', Event::PUSH},
{L'±', Event::NEG}, {L'v', Event::POP},
{L'=', Event::EQ} };
struct Calculator {
wstring input; wstring input;
double value = 0.0; std::deque<double> stack;
double temp = 0.0;
double result = 0.0;
void event(Event ev, wchar_t op) { void dump() {
switch($state) { fmt::println("STACK: ");
FSM_STATE(State, START, ev, op); for(auto num : stack) {
FSM_STATE(State, CALCULATING, ev, op); fmt::println("{}", num);
FSM_STATE(State, CLEARED, ev, op);
FSM_STATE(State, DISPLAYED, ev, op);
} }
} }
void START(Event ev, wchar_t op) { void do_op(Event ev) {
if(ev == Event::NUMBER) { if(stack.size() < 2) return;
input += op;
state(State::CALCULATING);
}
}
void CALCULATING(Event ev, wchar_t op) {
using enum Event; using enum Event;
double left = stack.back();
stack.pop_back();
double right = stack.back();
stack.pop_back();
switch(ev) { switch(ev) {
case NUMBER: case MUL:
input += op; temp = left * right;
break; break;
case ADD: case ADD:
value += std::stof(input); temp = left + right;
input = L"";
break; break;
case SUB: case SUB:
value -= std::stof(input); temp = left - right;
input = L"";
break;
case MUL:
value *= std::stof(input);
input = L"";
break; break;
case DIV: case DIV:
value /= std::stof(input); temp = left / right;
input = L"";
break; break;
case NOT: default:
value = std::stof(input) * -1; dbc::sentinel("invalid op passed to op()");
input = L""; }
break;
case EQ: stack.push_back(temp);
value = std::stof(input); }
void event(Event ev, wchar_t op) {
using enum Event;
switch(ev) {
case NUMBER:
input += op;
break; break;
case MUL: do_op(MUL); break;
case ADD: do_op(ADD); break;
case SUB: do_op(SUB); break;
case DIV: do_op(DIV); break;
case CLR: case CLR:
input = L""; input = L"";
state(State::CLEARED); stack.clear();
break; break;
case POP:
if(!stack.empty()) {
temp = stack.back();
input = fmt::format(L"{}", temp);
stack.pop_back();
} }
break;
case PUSH:
if(input.size() > 0) {
temp = std::stof(input);
input = L"";
stack.push_back(temp);
} }
break;
void CLEARED(Event ev, wchar_t op) { case NEG:
if(ev == Event::NUMBER) { if(input.size() > 0) {
input += op; temp = std::stof(input);
state(State::CALCULATING); input = fmt::format(L"{}", temp * -1);
} }
break;
case EQ:
break;
} }
void DISPLAYED(Event ev, wchar_t op) { dump();
fmt::println(L"ev={}, op={}", (int)ev, op);
state(State::CALCULATING);
} }
}; };
@ -112,11 +133,13 @@ struct CalculatorUI {
CalculatorUI() { 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|_|_|_]"
"[btn7|btn8|btn9|mult]" "[push|pop|clear|eq]"
"[btn4|btn5|btn6|minus]" "[add|sub|mul|div]"
"[btn1|btn2|btn3|plus]" "[btn7|btn8|btn9]"
"[neg|btn0|eq|div]"); "[btn4|btn5|btn6]"
"[btn1|btn2|btn3]"
"[neg|btn0|_]");
} }
void init() { void init() {
@ -133,8 +156,9 @@ struct CalculatorUI {
$gui.set<guecs::Textual>(id, {L"", 40}); $gui.set<guecs::Textual>(id, {L"", 40});
} else { } else {
$gui.set<guecs::Label>(id, { label }); $gui.set<guecs::Label>(id, { label });
wchar_t op = label[0];
$gui.set<guecs::Clickable>(id, { $gui.set<guecs::Clickable>(id, {
[&, name](auto, auto) { handle_button(label[0]); } [&, op](auto, auto) { handle_button(op); }
}); });
} }
} }
@ -152,52 +176,13 @@ struct CalculatorUI {
} }
void handle_button(wchar_t op) { void handle_button(wchar_t op) {
using enum Event; $fsm.event(EVENTS.at(op), op);
switch(op) {
case L'0':
case L'1':
case L'2':
case L'3':
case L'4':
case L'5':
case L'6':
case L'7':
case L'8':
case L'9':
$fsm.event(NUMBER, op);
break;
case L'*':
$fsm.event(MUL, op);
break;
case L'-':
$fsm.event(SUB, op);
break;
case L'+':
$fsm.event(ADD, op);
break;
case L'/':
$fsm.event(DIV, op);
break;
case L'±':
$fsm.event(NOT, op);
break;
case L'=':
$fsm.event(EQ, op);
break;
case L'C':
$fsm.event(CLR, op);
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($fsm.input); label.update($fsm.input);
} }
}; };
int main() { int main() {
sound::init(); sound::init();
shaders::init(); shaders::init();

Loading…
Cancel
Save