stamen

Static Menu Generator
git clone git://git.dimitrijedobrota.com/stamen.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |

generate.cpp (6795B)


1 #include <format> 2 #include <fstream> 3 #include <iostream> 4 #include <string> 5 6 #include "poafloc/poafloc.hpp" 7 #include "stamen/menu.hpp" 8 9 struct arguments_t 10 { 11 std::string config; 12 std::string display; 13 std::string nspace = "stamen"; 14 bool cpp = false; 15 bool user = false; 16 }; 17 18 namespace { 19 20 void generate_include_c(std::ostream& ost, const arguments_t& args) 21 { 22 ost << "#ifndef STAMEN_MENU_H\n"; 23 ost << "#define STAMEN_MENU_H\n\n"; 24 25 for (const auto& [code, menu] : stamen::menu::menu_lookup) 26 { 27 ost << std::format("int {}(size_t /* unused */);\n", menu.get_code()); 28 } 29 30 ost << "\n#endif\n"; 31 (void)args; 32 } 33 34 void generate_source_c(std::ostream& ost, const arguments_t& args) 35 { 36 if (args.user) 37 { 38 ost << "#include \"stamen.h\"\n\n"; 39 } 40 else 41 { 42 ost << "#include <stamen/stamen.h>\n\n"; 43 } 44 45 ost << std::format("extern int {}(const char *title, ", args.display); 46 ost << "const stamen_item_t itemv[], size_t size);\n\n"; 47 48 for (const auto& [code, _] : stamen::menu::free_lookup) 49 { 50 ost << std::format("extern int {}(size_t);\n", code); 51 } 52 ost << '\n'; 53 54 for (const auto& [code, menu] : stamen::menu::menu_lookup) 55 { 56 ost << std::format("int {}(size_t /* unused */) {{\n", menu.get_code()); 57 58 ost << "\tstatic const stamen_item_t items[] = "; 59 60 ost << "{\n"; 61 for (auto i = 0UL; i < menu.get_size(); i++) 62 { 63 ost << "\t\t{ " << menu.get_code(i); 64 ost << ", \"" << menu.get_prompt(i) << "\" },\n"; 65 } 66 ost << "\t};\n"; 67 68 ost << std::format("\treturn {}(\"{}\"", args.display, menu.get_title()); 69 ost << ", items, sizeof(items) / sizeof(items[0]));\n"; 70 ost << "}\n\n"; 71 } 72 } 73 74 void generate_include_cpp(std::ostream& ost, const arguments_t& args) 75 { 76 ost << "#pragma once\n\n"; 77 78 ost << "#include <cstddef>\n"; 79 ost << "#include <string>\n"; 80 ost << "#include <vector>\n\n"; 81 82 ost << (args.user ? "#include \"stamen.hpp\"\n\n" 83 : "#include <stamen/stamen.hpp>\n\n"); 84 85 ost << std::format("namespace {}\n{{\n", args.nspace); 86 87 ost << R"( 88 struct menu_t { 89 std::string title; 90 stamen::callback_f callback; 91 std::vector<stamen::item_t> items; 92 93 static int visit(const menu_t& menu); 94 }; 95 96 )"; 97 98 for (const auto& [code, menu] : stamen::menu::menu_lookup) 99 { 100 ost << std::format("int {}(std::size_t /* unused */);\n", menu.get_code()); 101 } 102 103 ost << std::format("\n}} // namespace {}\n", args.nspace); 104 } 105 106 void generate_source_cpp(std::ostream& ost, 107 const arguments_t& args, 108 const std::string& include) 109 { 110 ost << "#include <cstddef>\n\n"; 111 112 ost << std::format("#include \"{}\"\n\n", include); 113 114 ost << std::format("namespace {}\n{{\n\n", args.nspace); 115 116 ost << std::format("extern int {}(const char *title, ", args.display); 117 ost << "const stamen::item_t itemv[], size_t size);\n\n"; 118 119 for (const auto& [code, _] : stamen::menu::free_lookup) 120 { 121 ost << std::format("extern int {}(std::size_t);\n", code); 122 } 123 ost << '\n'; 124 125 for (const auto& [code, menu] : stamen::menu::menu_lookup) 126 { 127 ost << std::format("int {}(size_t /* unused */) // NOLINT\n{{\n", 128 menu.get_code()); 129 130 ost << "\tstatic const menu_t menu = {\n"; 131 ost << std::format("\t\t.title = \"{}\",\n", menu.get_title()); 132 ost << std::format("\t\t.callback = {},\n", menu.get_code()); 133 ost << std::format("\t\t.items = {{\n"); 134 for (auto i = 0UL; i < menu.get_size(); i++) 135 { 136 ost << std::format("\t\t\t{{.callback = {}, .prompt = \"{}\"}},\n", 137 menu.get_code(i), 138 menu.get_prompt(i)); 139 } 140 ost << "\t\t}\n\t};\n\n"; 141 142 ost << "\treturn menu_t::visit(menu);\n"; 143 144 ost << "}\n\n"; 145 } 146 147 ost << std::format("\n}} // namespace {}\n", args.nspace); 148 } 149 150 int parse_opt(int key, const char* arg, poafloc::Parser* parser) 151 { 152 auto* arguments = static_cast<arguments_t*>(parser->input()); 153 switch (key) 154 { 155 case 'd': 156 arguments->display = arg; 157 break; 158 case 'n': 159 if (!arguments->cpp) 160 { 161 poafloc::failure(parser, 0, 0, "Namespace only available in C++ mode"); 162 poafloc::help(parser, stderr, poafloc::STD_USAGE); 163 break; 164 } 165 arguments->nspace = arg; 166 break; 167 case 'u': 168 arguments->user = true; 169 break; 170 case 666: 171 arguments->cpp = false; 172 break; 173 case 777: 174 arguments->cpp = true; 175 break; 176 case poafloc::ARG: 177 if (!arguments->config.empty()) 178 { 179 poafloc::failure(parser, 0, 0, "Too many arguments"); 180 poafloc::help(parser, stderr, poafloc::STD_USAGE); 181 } 182 arguments->config = arg; 183 break; 184 case poafloc::NO_ARGS: 185 poafloc::failure(parser, 0, 0, "Missing an argument"); 186 poafloc::help(parser, stderr, poafloc::STD_USAGE); 187 break; 188 case poafloc::END: 189 if (arguments->display.empty()) 190 { 191 if (arguments->cpp) arguments->display = "stamen::builtin_display"; 192 else arguments->display = "stamen_builtin_display"; 193 } 194 break; 195 default: 196 break; 197 } 198 return 0; 199 } 200 201 } // namespace 202 203 // clang-format off 204 static const poafloc::option_t options[] { 205 {nullptr, 0, nullptr, 0, "Output mode", 1}, 206 {"c", 666, nullptr, 0, "Generate files for C"}, 207 {"cpp", 777, nullptr, 0, "Generate files for C++"}, 208 {nullptr, 0, nullptr, 0, "Output settings", 2}, 209 {"display", 'd', "name", 0, "Set display function to be called"}, 210 {"namespace", 'n', "name", 0, "Namespace, C++ only"}, 211 {"user", 'u', nullptr, 0, "Include user stamen headers"}, 212 {nullptr, 0, nullptr, 0, "Informational Options", -1}, 213 {nullptr}, 214 }; 215 // clang-format on 216 217 static const poafloc::arg_t arg { 218 options, 219 parse_opt, 220 "config_file", 221 "", 222 }; 223 224 int main(int argc, char* argv[]) 225 { 226 arguments_t args; 227 228 if (poafloc::parse(&arg, argc, argv, 0, &args) != 0) 229 { 230 std::cerr << "There was an error while parsing arguments"; 231 return 1; 232 } 233 234 const auto& config = args.config; 235 stamen::menu::read(config.c_str()); 236 237 const std::string::size_type pos = args.config.rfind('.'); 238 const std::string base = 239 pos != std::string::npos ? config.substr(0, pos) : config; 240 241 if (args.cpp) 242 { 243 const auto include_filename = base + ".hpp"; 244 std::ofstream include(include_filename); 245 generate_include_cpp(include, args); 246 247 const auto source_filename = base + ".cpp"; 248 std::ofstream source(source_filename); 249 generate_source_cpp(source, args, include_filename); 250 } 251 else 252 { 253 const auto include_filename = base + ".h"; 254 std::ofstream include(include_filename); 255 generate_include_c(include, args); 256 257 const auto source_filename = base + ".c"; 258 std::ofstream source(source_filename); 259 generate_source_c(source, args); 260 } 261 262 return 0; 263 }