4
\$\begingroup\$

I have reworked the code from my previous (linked) post.

For reference:

I have a string which contains JSON-like object flattened (stringify()ed). I need to JSON-ify it back. It's a custom format, so I wrote function to JSON-ify the string.

Please review it. Also one particular thing that I don't like is previousIsClosingBracket variable. I have added it for handling correct formatting of the last example. Is there a better way to detect such cases and have more beautiful solution?

#include <string>
#include <unordered_set>
#include <vector>
#include <iostream>
struct WordAtLevel
{
 std::string word;
 std::size_t level;
};
using tWordLevels = std::vector<WordAtLevel>;
class Formatter
{
public:
 explicit Formatter(std::string tclString);
 std::string formatted() const;
private:
 tWordLevels parse() const;
 static std::string build(tWordLevels wordAtLevels);
 std::string readUntilDelimiter() const;
 static bool isClosingBrackets(const std::string &str);
 static std::string indentation(const std::size_t tabCount);
 std::string m_tclString;
 std::size_t m_strLen = 0;
 mutable std::size_t m_idx = 0;
 static constexpr int tabWidth = 4;
 mutable bool m_bPrevWordReserved = false;
 const std::unordered_set<std::string> m_reservedWords{ "coords", "comment" };
};
Formatter::Formatter(std::string tclString)
 : m_tclString(std::move(tclString))
 , m_strLen(m_tclString.length())
{
}
std::string Formatter::formatted() const
{
 const tWordLevels wordLevels = parse();
 return build(wordLevels);
}
tWordLevels Formatter::parse() const
{
 tWordLevels wordAtLevels;
 int level = 0;
 for (m_idx = 0; m_idx < m_strLen; ++m_idx)
 {
 switch (auto ch = m_tclString[m_idx])
 {
 case '}':
 {
 if (m_bPrevWordReserved)
 m_bPrevWordReserved = false;
 else
 --level;
 wordAtLevels.emplace_back("}", level);
 break;
 }
 case '{':
 wordAtLevels.emplace_back("{", level);
 if (!m_bPrevWordReserved)
 ++level;
 [[fallthrough]];
 default:
 {
 std::string word = readUntilDelimiter();
 if (!word.empty())
 {
 if (m_reservedWords.contains(word))
 m_bPrevWordReserved = true;
 wordAtLevels.emplace_back(word, level);
 }
 break;
 }
 }
 }
 return wordAtLevels;
}
std::string Formatter::build(tWordLevels wordAtLevels)
{
 std::string result;
 const auto wordCount = wordAtLevels.size();
 bool previousIsClosingBracket = false;
 for (std::size_t idx = 0; idx < wordCount; ++idx)
 {
 const auto &[word, level] = wordAtLevels[idx];
 result += indentation(level);
 result += word;
 previousIsClosingBracket = isClosingBrackets(word);
 while (++idx < wordCount)
 {
 const auto &[nextWord, nextLevel] = wordAtLevels[idx];
 if (nextLevel != level)
 break;
 if (previousIsClosingBracket && !isClosingBrackets(nextWord))
 break;
 
 result += ' ';
 result += nextWord;
 }
 result += '\n';
 --idx;
 }
 return result;
}
std::string Formatter::readUntilDelimiter() const
{
 std::string word;
 ++m_idx;
 while (m_idx < m_strLen)
 {
 const auto ch = m_tclString[m_idx];
 if (ch == ' ' || ch == '{' || ch == '}')
 {
 --m_idx;
 break;
 }
 ++m_idx;
 word += ch;
 }
 return word;
}
bool Formatter::isClosingBrackets(const std::string& str)
{
 return str == std::string_view("}");
}
std::string Formatter::indentation(const std::size_t tabCount)
{
 return std::string(tabCount * tabWidth, ' ');
}
int main()
{
 const Formatter f1("{data {a {obj {coords {10 10} comment {} radius 260 type circle}}}}");
 std::cout << f1.formatted() << '\n';
 const Formatter f2("{data {b {obj {coords {-95 -85 -70 -85 -75 -95} comment {abc} type polygon}}}}");
 std::cout << f2.formatted() << '\n';
 const Formatter f3("{data {c {obj {coords {-55 -65 -70 -65 -25 -64} comment {abc def} type polygon}}}}");
 std::cout << f3.formatted() << '\n';
 const Formatter f4("{data {d {obj {coords {59 60} comment {} type point}} e {obj {coords {928 324} comment {} type point}}}}");
 std::cout << f4.formatted() << '\n';
}
asked Jan 30, 2022 at 18:00
\$\endgroup\$

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.