| stamenStatic Menu Generator | 
| git clone git://git.dimitrijedobrota.com/stamen.git | 
| Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | 
| 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++
| M | CMakeLists.txt | | | + - | 
| M | demo/CMakeLists.txt | | | + - | 
| M | demo/main.c | | | +++ ------ | 
| M | demo/main.cpp | | | +++ --- | 
| A | include/menu.h | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| D | include/shared.h | | | --------------- | 
| M | include/stamen.h | | | ++++++++++ ------------------------------------------------------------------------ | 
| D | include/stamenc.h | | | ----------------- | 
| M | src/CMakeLists.txt | | | ++++ ------------- | 
| M | src/generate.cpp | | | +++++++++++++++++ ------------------------------------ | 
| A | src/menu.cpp | | | ++++++++++++++++++++++++++++++++++++++++++++++ | 
| M | src/stamen.cpp | | | ++ ------ | 
| D | src/stamenc.cpp | | | -------------- | 
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);
          }