|
|
@ -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(); |
|
|
|