stamen

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

commit ad4b84a3bae704a23f2a417764b1bf62270adfc6
parent 0ca7539acca2a05ac12c268598ee3cd1881515fc
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Sat,  2 Dec 2023 23:59:56 +0000

Restructure and improvements

* Restructure the project
* Remove redundancy
* Improve consistency
* Improve decomposition
* Target C and C++

Diffstat:
MCMakeLists.txt | 2+-
Mdemo/CMakeLists.txt | 2+-
Mdemo/main.c | 9+++------
Mdemo/main.cpp | 6+++---
Ainclude/menu.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dinclude/shared.h | 15---------------
Minclude/stamen.h | 127++++++++++---------------------------------------------------------------------
Dinclude/stamenc.h | 17-----------------
Msrc/CMakeLists.txt | 17++++-------------
Msrc/generate.cpp | 53+++++++++++++++++------------------------------------
Asrc/menu.cpp | 46++++++++++++++++++++++++++++++++++++++++++++++
Msrc/stamen.cpp | 8++------
Dsrc/stamenc.cpp | 14--------------
13 files changed, 147 insertions(+), 224 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) project( Stamen - VERSION 0.0.15 + VERSION 0.0.16 DESCRIPTION "Static menu generator" LANGUAGES C CXX ) diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt @@ -35,7 +35,7 @@ add_executable(cdemo ) target_link_libraries(cdemo - stamenc + stamen ) add_custom_command( diff --git a/demo/main.c b/demo/main.c @@ -1,9 +1,11 @@ #include "demo_menu.h" -#include "stamenc.h" +#include "stamen.h" #include <stdio.h> #include <stdlib.h> +// need to link against stamen library +// in order to use stamen_builtin_display const stamen_display_f stamen_display = stamen_builtin_display; int operation1(void) { @@ -29,11 +31,6 @@ int finish(void) { exit(0); } -int menu_dynamic_print(void) { - stamen_print("menu_main"); - return 1; -} - int main(void) { menu_main(); return 0; diff --git a/demo/main.cpp b/demo/main.cpp @@ -4,8 +4,8 @@ #include <iostream> // need to link against stamen library -// in order to use stamen::BuiltinDisplay -const stamen_display_f stamen_display = stamen::builtinDisplay; +// in order to use stamen_builtin_display +const stamen_display_f stamen_display = stamen_builtin_display; int operation1() { std::cout << "operation 1" << std::endl; @@ -31,6 +31,6 @@ int finish() { } int main() { - stamen::menu_main(); + menu_main(); return 0; } diff --git a/include/menu.h b/include/menu.h @@ -0,0 +1,55 @@ +#ifndef STAMEN_MENU_H +#define STAMEN_MENU_H + +#include "stamen.h" + +#include <string> +#include <unordered_map> +#include <vector> + +class Menu { + struct private_ctor_t {}; + +public: + using lookup_t = std::unordered_map<std::string, Menu>; + static lookup_t lookup; + + static void read(const std::string &s); + static void print(const std::string &entry) { internal_print(entry, 1); } + static lookup_t &getLookup() { return lookup; } + + Menu(const Menu &) = delete; + Menu &operator=(const Menu &) = delete; + + // Tag type dispatch + Menu(private_ctor_t, const std::string &code, const std::string &prompt) + : Menu(code, prompt) {} + + struct lookup_item_t { + const std::string code; + const std::string prompt; + }; + + [[nodiscard]] const std::string &getCode() const { return code; } + [[nodiscard]] const std::string &getTitle() const { return title; } + [[nodiscard]] const std::vector<lookup_item_t> &getItems() const { + return lookup_items; + } + +private: + Menu(std::string code, std::string prompt) + : code(std::move(code)), title(std::move(prompt)) {} + + static const Menu *getMenu(const std::string &code) { + const auto it = lookup.find(code); + if (it == lookup.end()) return nullptr; + return &it->second; + } + + static void internal_print(const std::string &entry, const int depth); + + std::vector<lookup_item_t> lookup_items; + const std::string code, title; +}; + +#endif diff --git a/include/shared.h b/include/shared.h @@ -1,15 +0,0 @@ -#ifndef STAMEN_SHARED_H -#define STAMEN_SHARED_H - -typedef int (*stamen_callback_f)(void); - -typedef struct item_t item_t; -struct item_t { - stamen_callback_f callback; - const char *prompt; -}; - -typedef int (*stamen_display_f)(const char *, const item_t[], int); -extern const stamen_display_f stamen_display; - -#endif diff --git a/include/stamen.h b/include/stamen.h @@ -1,120 +1,23 @@ -#ifndef STAMEN_H -#define STAMEN_H +#ifndef STAMEN_STAMEN_H +#define STAMEN_STAMEN_H -#include "shared.h" - -#include <exception> -#include <format> -#include <fstream> -#include <iostream> -#include <sstream> -#include <string> -#include <tuple> -#include <unordered_map> -#include <utility> -#include <vector> - -namespace stamen { - -class Menu { - struct private_ctor_t {}; - -public: - friend class Generator; - - using callback_f = stamen_callback_f; - using display_f = stamen_display_f; - - Menu(const Menu &) = delete; - Menu(Menu &&) = delete; - Menu &operator=(const Menu &) = delete; - Menu &operator=(Menu &&) = delete; - - // 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) {} - - static void read(const std::string &s); - static void print(const std::string &entry) { internal_print(entry, 1); } - static void insert(const std::string &code, const callback_f callback); - -private: - Menu(std::string code, std::string prompt) - : code(std::move(code)), title(std::move(prompt)) {} - - Menu(const std::string &code, const callback_f callback) - : code(code), title(code), callback(callback) {} - - using lookup_t = std::unordered_map<std::string, Menu>; - static lookup_t &getLookup() { - static lookup_t lookup; - return lookup; - } - - static void internal_print(const std::string &entry, const int depth); - - static const Menu *getMenu(const std::string &code) { - const static lookup_t &lookup = getLookup(); - const auto it = lookup.find(code); - if (it == lookup.end()) return nullptr; - return &it->second; - } +#ifdef __cplusplus +#define EXTERNC extern "C" +#else +#define EXTERNC extern +#endif - const std::string code, title; - const callback_f callback = nullptr; +typedef int (*stamen_callback_f)(void); - using lookup_item_t = std::pair<std::string, std::string>; - std::vector<lookup_item_t> items; +typedef struct item_t item_t; +struct item_t { + stamen_callback_f callback; + const char *prompt; }; -inline void Menu::read(const std::string &s) { - std::string line, delim, code, prompt; - std::fstream fs(s); - char tmp = 0; - - lookup_t &lookup = getLookup(); - auto last = lookup.end(); - while (std::getline(fs, line)) { - if (line.empty()) continue; - std::istringstream ss(line); - ss >> delim >> code; - ss.ignore(1, ' '), std::getline(ss, prompt); - if (delim == "+") { - const auto [iter, succ] = - lookup.emplace(std::piecewise_construct, std::forward_as_tuple(code), - std::forward_as_tuple(private_ctor_t{}, code, prompt)); - last = iter; - } else { - last->second.items.emplace_back(code, prompt); - } - } -} - -inline void Menu::insert(const std::string &code, const callback_f callback) { - getLookup().emplace(std::piecewise_construct, std::forward_as_tuple(code), - std::forward_as_tuple(private_ctor_t{}, code, callback)); -} - -inline void Menu::internal_print(const std::string &code, const int depth) { - const Menu *menu = getMenu(code); - if (!menu) return; - - if (depth == 1) std::cout << std::format("{}({})\n", menu->title, code); - - if (!menu->callback) { - for (const auto &[code, prompt] : menu->items) { - std::cout << std::format("{}{} ({})\n", std::string(depth << 1, ' '), - prompt, code); - menu->internal_print(code, depth + 1); - } - } -} - -int builtinDisplay(const char *title, const ::item_t itemv[], int size); +typedef int (*stamen_display_f)(const char *, const item_t[], int); +extern const stamen_display_f stamen_display; -} // namespace stamen +EXTERNC int stamen_builtin_display(const char *title, const item_t itemv[], int size); #endif diff --git a/include/stamenc.h b/include/stamenc.h @@ -1,17 +0,0 @@ -#ifndef CSTAMEN_H -#define CSTAMEN_H - -#include "shared.h" - -#ifdef __cplusplus -#define EXTERNC extern "C" -#else -#define EXTERNC extern -#endif - -EXTERNC void stamen_read(const char *file); -EXTERNC void stamen_print(const char *entry); -EXTERNC void stamen_insert(const char *code, stamen_callback_f callback); -EXTERNC int stamen_builtin_display(const char *title, const item_t itemv[], int size); - -#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt @@ -9,15 +9,12 @@ set_target_properties(stamen PROPERTIES PUBLIC_HEADER ../include/stamen.h ) -add_library(stamenc stamenc.cpp stamen.cpp) -target_link_libraries(stamenc PUBLIC stamen-include) -set_target_properties(stamenc PROPERTIES - VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} - PUBLIC_HEADER ../include/stamenc.h +install(TARGETS stamen + LIBRARY DESTINATION lib + PUBLIC_HEADER DESTINATION include ) -add_executable(stamen-generate generate.cpp) +add_executable(stamen-generate generate.cpp menu.cpp) target_link_libraries(stamen-generate PRIVATE stamen-include) target_include_directories(stamen-generate PUBLIC ../include) @@ -27,10 +24,4 @@ set_target_properties(stamen-generate PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" ) - install(TARGETS stamen-generate DESTINATION bin) -install(TARGETS stamen - LIBRARY DESTINATION lib - PUBLIC_HEADER DESTINATION include -) - diff --git a/src/generate.cpp b/src/generate.cpp @@ -1,60 +1,41 @@ -#include "stamen.h" +#include "menu.h" +#include <format> +#include <fstream> +#include <iostream> #include <string> -using namespace stamen; - -namespace stamen { - class Generator { public: - class EGenerate : std::exception { - [[nodiscard]] const char *what() const noexcept override { - return "Trying to access unknown code"; - } - }; - - static void generateInclude(std::ostream &os, bool cpp) { + static void generateInclude(std::ostream &os) { os << "#ifndef STAMEN_MENU_H\n"; os << "#define STAMEN_MENU_H\n\n"; - if (cpp) 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 << "#include <stamen.h>\n\n"; + for (const auto &[code, menu] : Menu::getLookup()) { + os << std::format("int {}(void);\n", menu.getCode()); } - if (cpp) os << "\n}\n"; os << "\n#endif\n"; } - static void generateSource(std::ostream &os, bool cpp) { - os << "#include <stamenc.h>\n"; + static void generateSource(std::ostream &os) { + os << "#include <stamen.h>\n"; os << "#include \"shared.h\"\n\n"; - if (cpp) 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); + for (const auto &[code, menu] : Menu::getLookup()) { + os << std::format("int {}(void) {{\n", menu.getCode()); os << std::format("\tstatic const item_t items[] = {{\n"); - for (const auto &[code, prompt] : menu->items) { + for (const auto &[code, prompt] : menu.getItems()) { os << std::format("\t\t{{ {}, \"{}\" }},\n", code, prompt); } os << std::format("\t}};\n"); os << std::format("\treturn stamen_display(\"{}\", items, " - "sizeof(items) / sizeof(item_t));\n", - menu->title); + "sizeof(items) / sizeof(items[0]));\n", + menu.getTitle()); os << std::format("}}\n\n"); } - if (cpp) os << "}\n"; } }; -} // namespace stamen - int main(const int argc, const char *argv[]) { const auto args = std::span(argv, size_t(argc)); @@ -73,8 +54,8 @@ int main(const int argc, const char *argv[]) { std::string ext = cpp ? "pp" : ""; std::ofstream source(base + ".c" + ext), include(base + ".h" + ext); - Generator::generateSource(source, cpp); - Generator::generateInclude(include, cpp); + Generator::generateSource(source); + Generator::generateInclude(include); return 0; } diff --git a/src/menu.cpp b/src/menu.cpp @@ -0,0 +1,46 @@ +#include "menu.h" + +#include <format> +#include <fstream> +#include <iostream> +#include <sstream> +#include <tuple> +#include <utility> + +Menu::lookup_t Menu::lookup; + +void Menu::read(const std::string &s) { + std::string line, delim, code, prompt; + std::fstream fs(s); + char tmp = 0; + + lookup_t &lookup = getLookup(); + auto last = lookup.end(); + while (std::getline(fs, line)) { + if (line.empty()) continue; + std::istringstream ss(line); + ss >> delim >> code; + ss.ignore(1, ' '), std::getline(ss, prompt); + if (delim == "+") { + const auto [iter, succ] = + lookup.emplace(std::piecewise_construct, std::forward_as_tuple(code), + std::forward_as_tuple(private_ctor_t{}, code, prompt)); + last = iter; + } else { + last->second.lookup_items.emplace_back(code, prompt); + } + } +} + +void Menu::internal_print(const std::string &code, const int depth) { + const Menu *menu = getMenu(code); + if (!menu) return; + + if (depth == 1) std::cout << std::format("{}({})\n", menu->title, code); + + for (const auto &[code, prompt] : menu->lookup_items) { + std::cout << std::format("{}{} ({})\n", std::string(depth << 1, ' '), + prompt, code); + menu->internal_print(code, depth + 1); + } +} diff --git a/src/stamen.cpp b/src/stamen.cpp @@ -1,13 +1,11 @@ -#include "../include/stamen.h" +#include "stamen.h" #include <cmath> #include <format> #include <iostream> #include <ostream> #include <variant> -namespace stamen { - -int builtinDisplay(const char *title, const ::item_t itemv[], int size) { +int stamen_builtin_display(const char *title, const ::item_t itemv[], int size) { const size_t digits = size_t(std::log10(size)) + 1; const auto items = std::span(itemv, size_t(size)); int choice = 0; @@ -47,5 +45,3 @@ int builtinDisplay(const char *title, const ::item_t itemv[], int size) { return 1; } - -} // namespace stamen diff --git a/src/stamenc.cpp b/src/stamenc.cpp @@ -1,14 +0,0 @@ -#include "../include/stamenc.h" -#include "../include/stamen.h" - -using namespace stamen; - -void stamen_read(const char *file) { stamen::Menu::read(file); } -void stamen_print(const char *entry) { stamen::Menu::print(entry); } -void stamen_insert(const char *code, stamen_callback_f callback) { - Menu::insert(code, callback); -} - -int stamen_builtin_display(const char *title, const item_t items[], int size) { - return builtinDisplay(title, items, size); -}