stamen

Stamen - static menu generator
git clone git://git.dimitrijedobrota.com/stamen.git
Log | Files | Refs | README | LICENSE

commit c8ced3bdeaf1fe72949185cdc566b5fc69f11ddc
parent b833ffadb09c2e07d744ac2e30ecb0562d8b89be
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Thu,  9 Nov 2023 20:26:34 +0000

Add ability to generate c++ code from config

Diffstat:
Minclude/menu.h | 37+++++++++++++++++++++----------------
Asrc/generate.cpp | 22++++++++++++++++++++++
Msrc/main.cpp | 14++++++++------
Msrc/menu.cpp | 63++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
4 files changed, 105 insertions(+), 31 deletions(-)

diff --git a/include/menu.h b/include/menu.h @@ -20,40 +20,45 @@ public: } }; -private: struct item_t { - const std::string prompt; - const std::string callback; + const std::string prompt, callback; + const callback_f func; - item_t(const std::string &p, const std::string &c) - : prompt(p), callback(c) {} + item_t(const std::string &p, const std::string &c, + const callback_f func = nullptr) + : prompt(p), callback(c), func(func) {} item_t(const json &j) : item_t(j["prompt"], j["callback"]) {} }; - Menu(const json &json_data) - : name(json_data["name"]), code(json_data["code"]), - items({json_data["items"].begin(), json_data["items"].end()}) {} - - Menu(const std::string code, const callback_f callback) - : name(code), code(code), items(), callback(callback) {} - -public: static void read(const std::string &s) { for (const auto &json_data : json::parse(std::fstream(s))) { lookup.insert({json_data["code"], Menu(json_data)}); } } - static int start() { return getMenu("main")(); } + static int start() { return getMenu(entry)(); } static void insert(const std::string code, const callback_f callback) { lookup.insert({code, Menu(code, callback)}); } - static void print(const std::string code = "main", const int depth = 1); - int operator()() const; + static void print(const std::string &code = entry, const int depth = 1); + static void generate(const std::string &code = entry); + static int display(const std::string &name, const item_t items[], int size); + + int operator()() const { + return callback ? callback() : display(name, items.data(), items.size()); + } private: + Menu(const json &json_data) + : name(json_data["name"]), code(json_data["code"]), + items({json_data["items"].begin(), json_data["items"].end()}) {} + + Menu(const std::string code, const callback_f callback) + : name(code), code(code), items(), callback(callback) {} + static std::unordered_map<std::string, Menu> lookup; + static const std::string entry; static const Menu &getMenu(const std::string &code) { const auto it = lookup.find(code); diff --git a/src/generate.cpp b/src/generate.cpp @@ -0,0 +1,22 @@ +#include "json.hpp" +#include <format> +#include <fstream> +#include <iostream> +#include <string> + +using json = nlohmann::json; + +int main(const int argc, const char *argv[]) { + for (int i = 1; i < argc; i++) { + const char *filename = argv[i]; + for (const auto &menu : json::parse(std::fstream(filename))) { + std::cout << std::format("{}({}):\n", (std::string)menu["name"], + (std::string)menu["code"]); + for (const auto &item : menu["items"]) { + std::cout << std::format("* {}({})\n", (std::string)item["prompt"], + (std::string)item["callback"]); + } + } + } + return 0; +} diff --git a/src/main.cpp b/src/main.cpp @@ -18,14 +18,16 @@ int finish(void) { exit(0); } +int foo(void) { exit(1); } + int main(void) { Menu::read("menu.json"); - Menu::insert("algorithms", algorithms); - Menu::insert("settings", settings); - Menu::insert("finish", finish); - - Menu::print("main"); - Menu::start(); + // Menu::insert("algorithms", algorithms); + // Menu::insert("settings", settings); + // Menu::insert("finish", finish); + Menu::generate(); + // Menu::print(); + // Menu::start(); return 0; } diff --git a/src/menu.cpp b/src/menu.cpp @@ -2,30 +2,32 @@ #include <cmath> #include <format> #include <iostream> +#include <stack> +#include <unordered_set> std::unordered_map<std::string, Menu> Menu::lookup; +const std::string Menu::entry = "menu_main"; -int Menu::operator()() const { - if (callback) return callback(); - +int Menu::display(const std::string &name, const item_t items[], int size) { int choice; - const int n = items.size(), digits = std::log10(n) + 1; + const int digits = std::log10(size) + 1; while (true) { std::cout << std::format("{}:\n", name); - for (auto i = 0ul; i < n; i++) { + for (auto i = 0ul; i < size; i++) { std::cout << std::format(" {:{}}. {}\n", i, digits, items[i].prompt); } while (true) { std::cout << "Choose an option: "; - if (std::cin >> choice && choice >= -1 && choice < n) { + if (std::cin >> choice && choice >= -1 && choice < size) { if (choice == -1) { std::cout << "choice: back\n"; return 1; } - std::cout << std::format("Choice: {}\n\n", items[choice].prompt); - int res = getMenu(items[choice].callback)(); + const item_t &chosen = items[choice]; + std::cout << std::format("Choice: {}\n\n", chosen.prompt); + int res = chosen.func ? chosen.func() : getMenu(chosen.callback)(); if (--res) return res; break; @@ -44,7 +46,50 @@ int Menu::operator()() const { return 1; } -void Menu::print(const std::string code, const int depth) { +void Menu::generate(const std::string &code) { + std::unordered_set<std::string> seen; + std::vector<std::string> order; + std::stack<std::string> st; + + st.push(code); + while (!st.empty()) { + if (!st.top().empty()) { + const std::string &code = st.top(); + st.push(""); + for (const auto &[_, code, func] : getMenu(code).items) { + if (lookup.count(code)) st.push(code); + } + continue; + } + + st.pop(); + const std::string &code = st.top(); + st.pop(); + + if (seen.count(code)) continue; + seen.insert(code); + order.push_back(code); + } + + for (const auto &code : order) { + const Menu menu = getMenu(code); + if (menu.callback) continue; + + std::cout << std::format("int {}(void) {{\n", menu.code); + std::cout << std::format("\tstatic const Menu::item_t items[] = {{\n"); + for (const auto &item : menu.items) { + std::cout << std::format("\t\t{{ \"{}\", \"{}\", {} }},\n", item.prompt, + item.callback, item.callback); + } + std::cout << std::format("\t}};\n"); + std::cout << std::format("\treturn Menu::display(\"{}\", items, " + "sizeof(items) / sizeof(Menu::item_t));\n", + menu.name); + std::cout << std::format("}}\n\n"); + } +} + +void Menu::print(const std::string &code, const int depth) { const auto it = lookup.find(code); if (it == lookup.end()) return; const Menu &menu = it->second;