diff --git a/.vscode/settings.json b/.vscode/settings.json index 772265d..60739f6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -60,6 +60,7 @@ "cinttypes": "cpp", "typeinfo": "cpp", "valarray": "cpp", - "variant": "cpp" + "variant": "cpp", + "*.rh": "cpp" } } \ No newline at end of file diff --git a/README.md b/README.md index 1762b43..ad0f40f 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ I needed [xstate](https://github.com/davidkpiano/xstate) in C++. Here we are +## Features + +* Basic stuff (I dunno I'm new here I just wanted a state machine for myself ☃) +* Nested state machines just landed in by #5 - see [./xstate.cpp](./xstate.cpp) for an example. +* Others coming in real soon. Actually, even sooner, if you wanna contribute:P + ## Installation You only need the [`xstate.h`](./xstate.h) header file - that's it. @@ -83,13 +89,13 @@ int main() { compile with: ```sh -g++ -std=c++11 +g++ -std=c++17 ``` for example, ```sh -g++ -std=c++11 ./xstate.cpp -o xstate.out +g++ -std=c++17 ./xstate.cpp -o xstate.out ``` ## License diff --git a/go b/go index 9f13085..69702d3 100755 --- a/go +++ b/go @@ -2,4 +2,4 @@ PROGNAME="${1:-"xstate"}" -g++ -std=c++11 ./"$PROGNAME".cpp -o "$PROGNAME".out && ./"$PROGNAME".out +g++ -std=c++17 ./"$PROGNAME".cpp -o "$PROGNAME".out && ./"$PROGNAME".out diff --git a/xstate.cpp b/xstate.cpp index 47f7561..c1b2238 100644 --- a/xstate.cpp +++ b/xstate.cpp @@ -12,14 +12,219 @@ * compile with: * * ```sh - * g++ -std=c++11 ./xstate.cpp -o xstate.out + * g++ -std=c++17 ./xstate.cpp -o xstate.out * ``` * */ #include "xstate.h" -// using namespace xs; +#include +#include +#include + +std::vector splitStr(std::string str = "", const char delim = ' ') { + std::string buf; // Have a buffer string + std::stringstream ss(str); // Insert the string into a stream + + std::vector tokens; // Create vector to hold our words + + while (getline(ss, buf, delim)) { + tokens.push_back(buf); + } + + return tokens; +} + +namespace xs { + +const char *StateMachine::transition(const char *currentState, const char *event) +{ + std::vector stateAccessTokens = splitStr(currentState, '.'); + + // printf("num of tokens %d\n", stateAccessTokens.size()); + + // for (auto &token : stateAccessTokens) { + // printf("token %s\n", token.c_str()); + // } + + StateMachine *head = this; + + /** go deeper & traverse */ + for (size_t i = 0; i + 1 < stateAccessTokens.size(); ++i) { + const char *stateToken = stateAccessTokens[i].c_str(); + head = &head->states[stateToken].nested; + } + + const int lastIndex = stateAccessTokens.size() - 1; + std::string lastStateAccessToken = stateAccessTokens[lastIndex]; + + /** + * HALP FIXME + * + * lmfao I spent way too much time here + * + * don't use `const char *` for indexing for sure. + * + */ + const char *nextState = head->states[lastStateAccessToken].on[event]; + + // printf("nextState %s\n", nextState); + + return nextState; +} + +struct Interpreter { + private: + + InterpreterStatus status; + + InterpreterState *state; + StateMachine *stateMachine; + + public: + + Interpreter(StateMachine *stateMachine) + : + stateMachine(stateMachine), + state(new InterpreterState({ .value = stateMachine->initial})), + status(notStarted) + { + } + + const char *getStatusStr() const { + return InterpreterStatusStrings[this->status]; + } + + Interpreter *logInfo() { + printf("status = %d (%s) | state = %s \n", this->status, this->getStatusStr(), this->state->value); + return this; + } + + Interpreter *send(const char *event) { + if (this->status != started) { + fprintf( + stderr, + "\nERR interpreter.send was called when it was not started / stopped (%s).\n", + this->getStatusStr() + ); + + throw; + } + + const char *nextState = this->stateMachine->transition(this->state->value, event); + // printf("nextState %s\n", nextState); + + /** + * An event might've been fired at the wrong time / + * when the state didn't have any handlers for that specific event, + * thus we just skip it, since that's allowed. + */ + if (nextState != NULL) { + this->state->value = nextState; + } + + this->handleOnTransition(); + + return this; + } + + /** handlers (identical) */ + + + + // void onStart(std::function callback = [](const InterpreterState *state) {}) const { + // callback(this->state); + // } + + Interpreter *start() { + this->status = started; + this->handleOnStart(); + + return this; + } + + /** the one we call ourselves */ + private: std::function handleOnStart = []() {}; + + public: + Interpreter *onStart(const std::function callback = []( ) {}) { + this->handleOnStart = [&]() { callback( ); }; + return this; + } + + /** the one we expose to the consumer so he can inject the callback */ + Interpreter *onStart(const std::function callback = [](Interpreter *self) {}) { + this->handleOnStart = [&]() { callback(this); }; + return this; + } + + + + // std::function onTransition = []( ) {}; + // std::function onTransition = [](const InterpreterState *state) {}; + + // void onTransition(std::function callback = []() {}) const { + // callback(); + // } + + /** the one we call ourselves */ + private: std::function handleOnTransition = []() {}; + + /** the one we expose to the consumer so he can inject the callback */ + public: + Interpreter *onTransition(const std::function callback = []( ) {}) { + this->handleOnTransition = [&]() { callback( ); }; + return this; + } + + Interpreter *onTransition(const std::function callback = [](Interpreter *self) {}) { + this->handleOnTransition = [&]() { callback(this); }; + return this; + } + + + + // std::function onStop = []() {}; + // void onStop(std::function callback = [](const InterpreterState *state) {}) const { + // callback(this->state); + // } + + Interpreter *stop() { + this->status = stopped; + this->handleOnStop(); + return this; + } + + /** the one we call ourselves */ + private: std::function handleOnStop = []() {}; + + /** the one we expose to the consumer so he can inject the callback */ + public: + Interpreter *onStop(const std::function callback = []( ) {}) { + this->handleOnStop = [&]() { callback( ); }; + return this; + } + + Interpreter *onStop(const std::function callback = [](Interpreter *self) {}) { + this->handleOnStop = [&]() { callback(this); }; + return this; + } +}; + +Interpreter *interpret(StateMachine stateMachine) { + Interpreter *interpreter = new Interpreter(new StateMachine(stateMachine)); + + return interpreter; +} + +Interpreter *interpret(StateMachine *stateMachine) { + Interpreter *interpreter = new Interpreter( stateMachine ); + + return interpreter; +} + +} // namespace xs int main() { xs::StateMachine machine = { @@ -36,7 +241,23 @@ int main() { }, { "red" , - { .on = { { "TIMER", "green" } } } + { + .on = { { "TIMER", "red.red-100" } }, + .nested = { + .id = "red-brightness", + .initial = "red-100", + .states = { + { + "red-100", + { .on = { { "TIMER", "red.red-0" } } } + }, + { + "red-0", + { .on = { { "TIMER", "green" } } } + } + } + } + }, } } }; @@ -46,12 +267,11 @@ int main() { ->onStart([]() { printf("let's go!\n"); }) - ->onTransition([]() { - printf("yay we transitioned!\n"); + ->onTransition([](xs::Interpreter *self) { + self->logInfo(); }) - ->onStop([](xs::Interpreter *self) { + ->onStop([]() { printf("oh no we stopped c:\n"); - self->logInfo(); }) ->start(); @@ -61,6 +281,12 @@ int main() { toggleMachine->send("TIMER"); + toggleMachine->send("TIMER"); + + toggleMachine->send("TIMER"); + + toggleMachine->send("TIMER"); + toggleMachine->stop(); delete toggleMachine; diff --git a/xstate.h b/xstate.h index 1631ed2..b7bec52 100644 --- a/xstate.h +++ b/xstate.h @@ -6,81 +6,51 @@ * MIT Licensed * * - * compile with: - * - * ```sh - * g++ -std=c++11 ./xstate.cpp -o xstate.out - * ``` + * This file is pretty meh because you'd have to separate out + * the implementation into the `.cpp` file & that's *meh*, + * thus we don't currently use it, but maybe in the future. * */ -#include #include #include namespace xs { -/** - * { - * .on = { { "EVENT", "nextState" } } - * }; - * - */ -struct State { - std::map on; -}; +struct State; -/** - * { - * { - * "green" , - * { .on = { { "TIMER", "yellow" } } } - * }, - * { - * "yellow", - * { .on = { { "TIMER", "red" } } } - * }, - * { - * "red" , - * { .on = { { "TIMER", "green" } } } - * } - * }; - * - */ -typedef std::map States; +typedef std::map States; +// typedef std::map States; -/** - * { - * .id = "light", - * .initial = "green", - * .states = { - * { - * "green" , - * { .on = { { "TIMER", "yellow" } } } - * }, - * { - * "yellow", - * { .on = { { "TIMER", "red" } } } - * }, - * { - * "red" , - * { .on = { { "TIMER", "green" } } } - * } - * } - * }; - * - */ struct StateMachine { const char *id; const char *initial; States states; + // States *states; + + const char *transition(const char *currentState, const char *event); +}; - const char *transition(const char *currentState, const char *event) { - const char *nextState = this->states[currentState].on[event]; - return nextState; - } +// struct State : public StateMachine { +struct State { + std::map on; + StateMachine nested; }; +// struct StateMachine; +// // { +// // const char *id; +// // const char *initial; +// // States *states; + +// // const char *transition(const char *currentState, const char *event); +// // }; + +// struct State; +// { +// std::map on; +// }; + enum InterpreterStatus { notStarted, started, @@ -93,154 +63,41 @@ struct InterpreterState { const char *value; }; -struct Interpreter { - private: - - InterpreterStatus status; - - InterpreterState *state; - StateMachine *stateMachine; - - public: - - Interpreter(StateMachine *stateMachine) - : - stateMachine(stateMachine), - state(new InterpreterState({ .value = stateMachine->initial})), - status(notStarted) - { - } - - const char *getStatusStr() const { - return InterpreterStatusStrings[this->status]; - } - - Interpreter *logInfo() { - printf("status = %d (%s) | state = %s \n", this->status, this->getStatusStr(), this->state->value); - return this; - } - - Interpreter *send(const char *event) { - if (this->status != started) { - fprintf( - stderr, - "\nERR interpreter.send was called when it was not started / stopped (%s).\n", - this->getStatusStr() - ); +// struct Interpreter { +// private: +// InterpreterStatus status ; - throw; - } +// InterpreterState *state ; +// StateMachine *stateMachine; - const char *nextState = this->stateMachine->transition(this->state->value, event); - // printf("nextState %s\n", nextState); +// public: +// Interpreter(StateMachine *stateMachine); - /** - * An event might've been fired at the wrong time / - * when the state didn't have any handlers for that specific event, - * thus we just skip it, since that's allowed. - */ - if (nextState != NULL) { - this->state->value = nextState; - } +// const char *getStatusStr() const; +// Interpreter *logInfo () ; - this->handleOnTransition(); +// /** handlers */ +// Interpreter *start( ); +// Interpreter *send (const char *event); +// Interpreter *stop ( ); - return this; - } - - /** handlers (identical) */ - - - - // void onStart(std::function callback = [](const InterpreterState *state) {}) const { - // callback(this->state); - // } - - Interpreter *start() { - this->status = started; - this->handleOnStart(); - - return this; - } - - /** the one we call ourselves */ - private: std::function handleOnStart = []() {}; - - public: - Interpreter *onStart(const std::function callback = []( ) {}) { - this->handleOnStart = [&]() { callback( ); }; - return this; - } - - /** the one we expose to the consumer so he can inject the callback */ - Interpreter *onStart(const std::function callback = [](Interpreter *self) {}) { - this->handleOnStart = [&]() { callback(this); }; - return this; - } - - - - // std::function onTransition = []( ) {}; - // std::function onTransition = [](const InterpreterState *state) {}; - - // void onTransition(std::function callback = []() {}) const { - // callback(); - // } - - /** the one we call ourselves */ - private: std::function handleOnTransition = []() {}; - - /** the one we expose to the consumer so he can inject the callback */ - public: - Interpreter *onTransition(const std::function callback = []( ) {}) { - this->handleOnTransition = [&]() { callback( ); }; - return this; - } - - Interpreter *onTransition(const std::function callback = [](Interpreter *self) {}) { - this->handleOnTransition = [&]() { callback(this); }; - return this; - } - - - - // std::function onStop = []() {}; - // void onStop(std::function callback = [](const InterpreterState *state) {}) const { - // callback(this->state); - // } - - Interpreter *stop() { - this->status = stopped; - this->handleOnStop(); - return this; - } - - /** the one we call ourselves */ - private: std::function handleOnStop = []() {}; - - /** the one we expose to the consumer so he can inject the callback */ - public: - Interpreter *onStop(const std::function callback = []( ) {}) { - this->handleOnStop = [&]() { callback( ); }; - return this; - } - - Interpreter *onStop(const std::function callback = [](Interpreter *self) {}) { - this->handleOnStop = [&]() { callback(this); }; - return this; - } -}; +// private: +// std::function handleOnStart ; +// std::function handleOnTransition; +// std::function handleOnStop ; -Interpreter *interpret(StateMachine stateMachine) { - Interpreter *interpreter = new Interpreter(new StateMachine(stateMachine)); +// public: +// Interpreter *onStart( const std::function callback = []( ) {}); +// Interpreter *onStart( const std::function callback = [](Interpreter *self) {}); - return interpreter; -} +// Interpreter *onTransition(const std::function callback = []( ) {}); +// Interpreter *onTransition(const std::function callback = [](Interpreter *self) {}); -Interpreter *interpret(StateMachine *stateMachine) { - Interpreter *interpreter = new Interpreter( stateMachine ); +// Interpreter *onStop( const std::function callback = []( ) {}); +// Interpreter *onStop( const std::function callback = [](Interpreter *self) {}); +// }; - return interpreter; -} +// Interpreter *interpret(StateMachine stateMachine); +// Interpreter *interpret(StateMachine *stateMachine); } // namespace xs