Skip to content

Instantly share code, notes, and snippets.

@cjappl
Created January 1, 2023 01:11
Show Gist options
  • Save cjappl/493ee614995c1b9a80cc73b1fb3f7ca6 to your computer and use it in GitHub Desktop.
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
// 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