stamenStatic Menu Generator |
git clone git://git.dimitrijedobrota.com/stamen.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | |
commit | 69b2fecb3c6afe1358abb309dd8a3a95cdf417e7 |
parent | 8af1469e5448a18f63b83bf7b144d18d87940f5b |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Thu, 23 Nov 2023 19:34:45 +0000 |
Consistency Improvement * Move generation code to generate.cpp * Improve exception safety * Improve decomposition
Diffstat:M | CMakeLists.txt | | | +- |
M | include/stamen.h | | | +++++++++++++++++++++------------------------------------------------------------- |
M | src/generate.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
3 files changed, 78 insertions(+), 69 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(
Stamen
VERSION 0.0.10
VERSION 0.0.11
DESCRIPTION "Static menu generator"
LANGUAGES CXX
)
diff --git a/include/stamen.h b/include/stamen.h
@@ -15,6 +15,7 @@ namespace stamen {
class Menu {
public:
friend class Generator;
typedef int (*callback_f)(void);
Menu(const Menu &) = delete;
@@ -22,35 +23,27 @@ public:
struct private_ctor_t {};
// Tag type dispatch
Menu(private_ctor_t, const std::string &code, const std::string &prompt)
: Menu(code, prompt) {}
Menu(private_ctor_t, const std::string &code, const callback_f callback)
: Menu(code, callback) {}
class EMenu : std::exception {
virtual const char *what() const noexcept override {
return "Trying to access an unknown menu";
}
};
struct item_t {
friend class Menu;
item_t(const callback_f func, const std::string &prompt)
: callback(func), prompt(prompt) {}
item_t(const std::string &code, const std::string &prompt)
: code(code), prompt(prompt) {}
const std::string getCode(void) const { return code; }
const std::string getPrompt(void) const { return prompt; }
const callback_f getCallback(void) const { return callback; }
int operator()(void) const {
return callback ? callback() : getMenu(code)();
}
int operator()(void) const { return callback ? callback() : start(code); }
private:
item_t(const std::string &code, const std::string &prompt)
: code(code), prompt(prompt) {}
const std::string prompt, code;
const callback_f callback = nullptr;
};
@@ -61,11 +54,12 @@ public:
static void read(const std::string &s);
static void insert(const std::string &code, const callback_f callback);
static int start(const std::string &entry) { return getMenu(entry)(); }
static void print(const std::string &entry) { print(entry, 1); }
static void generateSource(std::ostream &os);
static void generateInclude(std::ostream &os);
static int start(const std::string &entry) {
const Menu *menu = getMenu(entry);
if (!menu) return 1;
return menu->operator()();
}
int operator()() const {
return callback ? callback() : display(title, items.data(), items.size());
@@ -86,11 +80,11 @@ private:
static void print(const std::string &entry, const int depth);
static const Menu &getMenu(const std::string &code) {
static const Menu *getMenu(const std::string &code) {
static lookup_t &lookup = getLookup();
const auto it = lookup.find(code);
if (it == lookup.end()) throw EMenu();
return it->second;
if (it == lookup.end()) return nullptr;
return &it->second;
}
const std::string code, title;
@@ -126,55 +120,17 @@ inline void Menu::insert(const std::string &code, const callback_f callback) {
std::forward_as_tuple(private_ctor_t{}, code, callback));
}
inline void Menu::generateInclude(std::ostream &os) {
os << "#ifndef STAMEN_MENU_H\n";
os << "#define STAMEN_MENU_H\n\n";
os << "#include <stamen.h>\n\n";
os << "namespace stamen {\n\n";
for (const auto &[code, _] : getLookup()) {
const Menu &menu = getMenu(code);
if (menu.callback) continue;
os << std::format("int {}(void);\n", menu.code);
}
os << "\n}\n";
os << "#endif\n";
}
inline void Menu::generateSource(std::ostream &os) {
os << "#include <stamen.h>\n";
os << "#include \"shared.h\"\n";
os << "\nnamespace stamen {\n";
for (const auto &[code, _] : getLookup()) {
const Menu &menu = getMenu(code);
if (menu.callback) continue;
os << std::format("int {}(void) {{\n", menu.code);
os << std::format("\tstatic const Menu::item_t items[] = {{\n");
for (const auto &item : menu.items) {
os << std::format("\t\t{{{}, \"{}\"}},\n", item.code, item.prompt);
}
os << std::format("\t}};\n");
os << std::format("\treturn Menu::display(\"{}\", items, "
"sizeof(items) / sizeof(Menu::item_t));\n",
menu.title);
os << std::format("}}\n\n");
}
os << "\n}\n";
}
inline void Menu::print(const std::string &code, const int depth) {
static lookup_t &lookup = getLookup();
const auto it = lookup.find(code);
if (it == lookup.end()) return;
const Menu &menu = it->second;
const Menu *menu = getMenu(code);
if (!menu) return;
if (depth == 1) std::cout << std::format("{}({})\n", menu.title, code);
if (depth == 1) std::cout << std::format("{}({})\n", menu->title, code);
if (!menu.callback) {
for (const auto &item : menu.items) {
if (!menu->callback) {
for (const auto &item : menu->items) {
std::cout << std::format("{}{} ({})\n", std::string(depth << 1, ' '),
item.prompt, item.code);
menu.print(item.code, depth + 1);
item.getPrompt(), item.getCode());
menu->print(item.getCode(), depth + 1);
}
}
}
diff --git a/src/generate.cpp b/src/generate.cpp
@@ -3,6 +3,58 @@
using namespace stamen;
namespace stamen {
class Generator {
public:
class EGenerate : std::exception {
virtual const char *what() const noexcept override {
return "Trying to access unknown code";
}
};
static void generateInclude(std::ostream &os) {
os << "#ifndef STAMEN_MENU_H\n";
os << "#define STAMEN_MENU_H\n\n";
os << "#include <stamen.h>\n\n";
os << "namespace stamen {\n\n";
for (const auto &[code, _] : Menu::getLookup()) {
const Menu *menu = Menu::getMenu(code);
if (!menu) throw EGenerate();
if (menu->callback) continue;
os << std::format("int {}(void);\n", menu->code);
}
os << "\n}\n";
os << "#endif\n";
}
static void generateSource(std::ostream &os) {
os << "#include <stamen.h>\n";
os << "#include \"shared.h\"\n";
os << "\nnamespace stamen {\n";
for (const auto &[code, _] : Menu::getLookup()) {
const Menu *menu = Menu::getMenu(code);
if (!menu) throw EGenerate();
if (menu->callback) continue;
os << std::format("int {}(void) {{\n", menu->code);
os << std::format("\tstatic const Menu::item_t items[] = {{\n");
for (const auto &item : menu->items) {
os << std::format("\t\t{{{}, \"{}\"}},\n", item.getCode(),
item.getPrompt());
}
os << std::format("\t}};\n");
os << std::format("\treturn Menu::display(\"{}\", items, "
"sizeof(items) / sizeof(Menu::item_t));\n",
menu->title);
os << std::format("}}\n\n");
}
os << "\n}\n";
}
};
} // namespace stamen
int main(const int argc, const char *argv[]) {
if (argc != 2) {
std::cout << "please enter exaclty one config file" << std::endl;
@@ -15,8 +67,9 @@ int main(const int argc, const char *argv[]) {
std::string::size_type pos = path.rfind('.');
std::string base = pos != std::string::npos ? path.substr(0, pos) : path;
std::ofstream cpp(base + ".cpp"), h(base + ".h");
Menu::generateSource(cpp);
Menu::generateInclude(h);
Generator::generateSource(cpp);
Generator::generateInclude(h);
return 0;
}