Created
January 1, 2023 01:11
-
-
Save cjappl/493ee614995c1b9a80cc73b1fb3f7ca6 to your computer and use it in GitHub Desktop.
Output Quoted Strings Finite State Machine (FSM) in C++ - Topic 29 of Pragmatic Programmer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Based on general design of this FSM: https://www.aleksandrhovhannisyan.com/blog/implementing-a-finite-state-machine-in-cpp/ | |
/* | |
* stateDiagram | |
direction LR | |
LookForString --> LookForString: * | |
LookForString --> InString: ch == ", InitResult() | |
InString --> InString: ch == anything else, AddToResult() | |
InString --> LookForString: ch == ", OutputResult() | |
InString --> CopyNextChar: ch == \, AddToResult() | |
CopyNextChar --> InString: ch == *, AddToResult() | |
*/ | |
#include <iostream> | |
class StringSearchState; | |
class StringSearchFSM; | |
// Define an abstract state class | |
// we need enter actions, exit actions, as well as what to do when we get something that may change | |
// the state | |
class StringSearchState | |
{ | |
public: | |
// Its important that you pass in the thing that HAS the states to each | |
// of these functions. These classes are extremely tightly interwoven, so they can affect | |
// each other's private states. | |
virtual void enter(StringSearchFSM *s) = 0; | |
virtual void processChar(StringSearchFSM *s, char inputChar) = 0; | |
virtual void exit(StringSearchFSM *s) = 0; | |
virtual ~StringSearchState() = default; | |
}; | |
class InStringState : public StringSearchState | |
{ | |
public: | |
static StringSearchState& GetInstance(); | |
void enter(StringSearchFSM *s) override; | |
void processChar(StringSearchFSM *s, char inputChar) override; | |
void exit(StringSearchFSM *s) override; | |
}; | |
class CopyNextCharState : public StringSearchState | |
{ | |
public: | |
static StringSearchState& GetInstance(); | |
void enter(StringSearchFSM *s) override; | |
void processChar(StringSearchFSM *s, char inputChar) override; | |
void exit(StringSearchFSM *s) override; | |
}; | |
class LookForStringState : public StringSearchState | |
{ | |
public: | |
static StringSearchState& GetInstance(); | |
void enter(StringSearchFSM *s) override; | |
void processChar(StringSearchFSM *s, char inputChar) override; | |
void exit(StringSearchFSM *s) override; | |
}; | |
class StringSearchFSM | |
{ | |
public: | |
void SetState(StringSearchState* newState) | |
{ | |
mState->exit(this); | |
mState = newState; | |
mState->enter(this); | |
}; | |
[[ nodiscard ]] const StringSearchState& GetCurrentState() | |
{ | |
return *mState; | |
} | |
void processChar(char c) | |
{ | |
mState->processChar(this, c); | |
} | |
friend InStringState; | |
friend LookForStringState; | |
friend CopyNextCharState; | |
private: | |
void InitializeResult() | |
{ | |
mResult.clear(); | |
}; | |
void AddToResult(char c) | |
{ | |
mResult += c; | |
} | |
void OutputResult() const | |
{ | |
std::cout << mResult << "\n"; | |
} | |
StringSearchState* mState { &LookForStringState::GetInstance() }; | |
std::string mResult {}; | |
}; | |
StringSearchState &CopyNextCharState :: GetInstance() | |
{ | |
static CopyNextCharState singleton; | |
return singleton; | |
} | |
void CopyNextCharState::enter(StringSearchFSM *s) | |
{ | |
} | |
void CopyNextCharState::processChar(StringSearchFSM *s, char inputChar) | |
{ | |
s->AddToResult(inputChar); | |
s->SetState(&InStringState::GetInstance()); | |
} | |
void CopyNextCharState::exit(StringSearchFSM *s) | |
{ | |
} | |
StringSearchState &InStringState :: GetInstance() | |
{ | |
static InStringState singleton; | |
return singleton; | |
} | |
void InStringState::enter(StringSearchFSM *s) | |
{ | |
} | |
void InStringState::processChar(StringSearchFSM *s, char inputChar) | |
{ | |
if (inputChar == '"') | |
{ | |
s->OutputResult(); | |
s->SetState(&LookForStringState::GetInstance()); | |
} | |
else if (inputChar == '\\') | |
{ | |
s->AddToResult(inputChar); | |
s->SetState(&CopyNextCharState::GetInstance()); | |
} | |
else | |
{ | |
s->AddToResult(inputChar); | |
} | |
} | |
void InStringState::exit(StringSearchFSM *s) | |
{ | |
} | |
StringSearchState &LookForStringState :: GetInstance() | |
{ | |
static LookForStringState singleton; | |
return singleton; | |
} | |
void LookForStringState::enter(StringSearchFSM *s) | |
{ | |
} | |
void LookForStringState::processChar(StringSearchFSM *s, char inputChar) | |
{ | |
if (inputChar == '"') | |
{ | |
s->InitializeResult(); | |
s->SetState(&InStringState::GetInstance()); | |
} | |
} | |
void LookForStringState::exit(StringSearchFSM *s) | |
{ | |
} | |
int main() | |
{ | |
StringSearchFSM machine; | |
machine.SetState(&LookForStringState::GetInstance()); | |
for (auto& ch : R"(Hi there "test string" aloha "second test")") | |
{ | |
machine.processChar(ch); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment