Switched to an RPN calculator since they're simpler to implement.
This commit is contained in:
parent
bd0ec9e095
commit
7847ffdcbd
1 changed files with 98 additions and 113 deletions
207
demos/calc.cpp
207
demos/calc.cpp
|
@ -5,6 +5,7 @@
|
|||
#include "guecs.hpp"
|
||||
#include "constants.hpp"
|
||||
#include <fmt/xchar.h>
|
||||
#include <deque>
|
||||
|
||||
#define FSM_DEBUG 1
|
||||
#include "fsm.hpp"
|
||||
|
@ -14,94 +15,114 @@ constexpr const int WINDOW_HEIGHT=400;
|
|||
|
||||
using std::string, std::wstring;
|
||||
|
||||
enum class Event {
|
||||
NUMBER, ADD, SUB, MUL,
|
||||
DIV, CLR, NEG, EQ, PUSH, POP
|
||||
};
|
||||
|
||||
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"="} };
|
||||
{"btn8", L"8"}, {"btn9", L"9"}, {"mul", L"*"},
|
||||
{"sub", L"-"}, {"add", L"+"}, {"neg", L"±"},
|
||||
{"div", L"/"}, {"eq", L"="},
|
||||
{"push", L"^"}, {"pop", L"v"} };
|
||||
|
||||
enum class Event {
|
||||
NUMBER, ADD, SUB, MUL,
|
||||
DIV, CLR, NOT, EQ,
|
||||
};
|
||||
|
||||
enum class State {
|
||||
START,
|
||||
CALCULATING,
|
||||
CLEARED,
|
||||
DISPLAYED
|
||||
};
|
||||
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 : DeadSimpleFSM<State, Event> {
|
||||
struct Calculator {
|
||||
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) {
|
||||
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 dump() {
|
||||
fmt::println("STACK: ");
|
||||
for(auto num : stack) {
|
||||
fmt::println("{}", num);
|
||||
}
|
||||
}
|
||||
|
||||
void START(Event ev, wchar_t op) {
|
||||
if(ev == Event::NUMBER) {
|
||||
input += op;
|
||||
state(State::CALCULATING);
|
||||
}
|
||||
}
|
||||
|
||||
void CALCULATING(Event ev, wchar_t op) {
|
||||
void do_op(Event ev) {
|
||||
if(stack.size() < 2) return;
|
||||
using enum Event;
|
||||
|
||||
double left = stack.back();
|
||||
stack.pop_back();
|
||||
double right = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
switch(ev) {
|
||||
case MUL:
|
||||
temp = left * right;
|
||||
break;
|
||||
case ADD:
|
||||
temp = left + right;
|
||||
break;
|
||||
case SUB:
|
||||
temp = left - right;
|
||||
break;
|
||||
case DIV:
|
||||
temp = left / right;
|
||||
break;
|
||||
default:
|
||||
dbc::sentinel("invalid op passed to op()");
|
||||
}
|
||||
|
||||
stack.push_back(temp);
|
||||
}
|
||||
|
||||
void event(Event ev, wchar_t op) {
|
||||
using enum Event;
|
||||
switch(ev) {
|
||||
case NUMBER:
|
||||
input += op;
|
||||
break;
|
||||
case ADD:
|
||||
value += std::stof(input);
|
||||
input = L"";
|
||||
break;
|
||||
case SUB:
|
||||
value -= std::stof(input);
|
||||
input = L"";
|
||||
break;
|
||||
case MUL:
|
||||
value *= std::stof(input);
|
||||
input = L"";
|
||||
break;
|
||||
case DIV:
|
||||
value /= std::stof(input);
|
||||
input = L"";
|
||||
break;
|
||||
case NOT:
|
||||
value = std::stof(input) * -1;
|
||||
input = L"";
|
||||
break;
|
||||
case EQ:
|
||||
value = std::stof(input);
|
||||
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:
|
||||
input = L"";
|
||||
state(State::CLEARED);
|
||||
stack.clear();
|
||||
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;
|
||||
case NEG:
|
||||
if(input.size() > 0) {
|
||||
temp = std::stof(input);
|
||||
input = fmt::format(L"{}", temp * -1);
|
||||
}
|
||||
break;
|
||||
case EQ:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
dump();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -112,11 +133,13 @@ struct CalculatorUI {
|
|||
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|eq|div]");
|
||||
"[*%(400)readout|_|_|_]"
|
||||
"[push|pop|clear|eq]"
|
||||
"[add|sub|mul|div]"
|
||||
"[btn7|btn8|btn9]"
|
||||
"[btn4|btn5|btn6]"
|
||||
"[btn1|btn2|btn3]"
|
||||
"[neg|btn0|_]");
|
||||
}
|
||||
|
||||
void init() {
|
||||
|
@ -133,8 +156,9 @@ struct CalculatorUI {
|
|||
$gui.set<guecs::Textual>(id, {L"", 40});
|
||||
} else {
|
||||
$gui.set<guecs::Label>(id, { label });
|
||||
wchar_t op = label[0];
|
||||
$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) {
|
||||
using enum Event;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$fsm.event(EVENTS.at(op), op);
|
||||
auto readout = $gui.entity("readout");
|
||||
auto& label = $gui.get<guecs::Textual>(readout);
|
||||
label.update($fsm.input);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
int main() {
|
||||
sound::init();
|
||||
shaders::init();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue