27constexpr const char*
copyright =
"Copyright (C) 2024-"
29 " Andrew S. Rightenburg\n\n"
30 "This program is free software; you can redistribute it and/or modify\n"
31 "it under the terms of the GNU General Public License as published by\n"
32 "the Free Software Foundation; either version 3 of the License, or\n"
33 "(at your option) any later version.\n\n"
34 "This program is distributed in the hope that it will be useful,\n"
35 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
36 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
37 "GNU General Public License for more details.\n\n"
38 "You should have received a copy of the GNU General Public License\n"
39 "along with this program. If not, see http://www.gnu.org/licenses/.\n";
68 {
'o',
"output",
"Specify output file (default: run on exit)",
true},
69 {
'b',
"target-bash",
"Target Bash version (default: 5.2)",
true},
70 {
's',
"no-warnings",
"Suppress warnings",
false},
71 {
'I',
"include",
"Add directory to include path",
true},
72 {
'p',
"parse-tree",
"Display parse tree (do not compile program)",
false},
73 {
't',
"tokens",
"Display tokens (do not compile program)",
false},
74 {
'v',
"version",
"Display version information",
false},
75 {
'h',
"help",
"Display this help message",
false},
76 {0,
nullptr,
nullptr,
false}
101 size_t max_length = 0;
104 if (length > max_length) {
113 "Usage: bpp [options] [file] ...\n"
114 "If no file is specified, read from stdin\n"
115 "All arguments after the file are passed to the compiled program\n"
131 if (opt.shortopt && opt.longopt) {
139 if (opt.takes_argument) {
145 size_t padding_amount = max_label_length - label_length;
146 total_length += padding_amount + 1 +
string_length(opt.description) + 1;
149 return total_length + 1;
168 result.
append(opt.shortopt);
171 if (opt.shortopt && opt.longopt) {
177 result.
append(opt.longopt);
180 if (opt.takes_argument) {
186 size_t padding_amount = max_label_length - label_length;
187 result.
append(
' ', padding_amount + 1);
188 result.
append(opt.description);
212 std::shared_ptr<std::vector<std::string>>
include_paths = std::make_shared<std::vector<std::string>>();
221 std::vector<char*> program_arguments;
222 std::vector<char*> compiler_arguments;
224 program_arguments.reserve(
static_cast<std::vector<char*>::size_type
>(argc));
225 compiler_arguments.reserve(
static_cast<std::vector<char*>::size_type
>(argc));
226 compiler_arguments.push_back(argv[0]);
233 std::vector<std::pair<char, const char*>> options_with_arguments;
235 for (
const auto& opt :
options) {
236 if (opt.takes_argument) {
237 options_with_arguments.emplace_back(opt.shortopt, opt.longopt);
241 bool expecting_argument =
false;
242 bool received_filename =
false;
243 for (
int i = 1; i < argc; i++) {
245 if (received_filename) {
246 program_arguments.push_back(argv[i]);
252 if (expecting_argument) {
253 compiler_arguments.push_back(argv[i]);
254 expecting_argument =
false;
260 if (argv[i][0] ==
'-') {
261 for (
const auto& opt : options_with_arguments) {
263 if (argv[i][1] == opt.first && argv[i][2] ==
'\0') {
265 expecting_argument =
true;
266 }
else if (opt.second
267 && strcmp(argv[i] + 2, opt.second) == 0
268 && argv[i][2 + std::char_traits<char>::length(opt.second)] ==
'\0'
271 expecting_argument =
true;
277 compiler_arguments.push_back(argv[i]);
280 if (!received_filename && argv[i][0] !=
'-') {
282 received_filename =
true;
286 return {compiler_arguments, program_arguments};
300 int option_index = 0;
302 std::vector<char> options_with_arguments;
304 std::string short_options;
305 for (
const auto& opt :
options) {
307 short_options += opt.shortopt;
308 if (opt.takes_argument) {
309 short_options +=
":";
310 options_with_arguments.push_back(opt.shortopt);
314 short_options +=
'\0';
317 static struct option long_options[(sizeof(
options) / sizeof(
options[0])) + 1];
319 for (
const auto& opt :
options) {
321 long_options[option_index++] = {opt.longopt, opt.takes_argument ? required_argument : no_argument,
nullptr, opt.shortopt};
324 long_options[option_index] = {
nullptr, 0,
nullptr, 0};
328 bool received_output_filename =
false;
332 static_cast<int>(compiler_arguments.size()),
333 compiler_arguments.data(),
334 short_options.c_str(),
343 std::istringstream version_stream(optarg);
344 uint16_t major, minor;
346 if (!(version_stream >> major >> dot >> minor) || dot !=
'.') {
347 throw std::runtime_error(
"Invalid Bash version format: " + std::string(optarg) +
348 "\nExpected format: <major>.<minor> (e.g., 5.2)");
360 if (!std::filesystem::exists(optarg) || !std::filesystem::is_directory(optarg)) {
361 throw std::runtime_error(
"Include path '" + std::string(optarg) +
"' does not exist or is not a directory");
366 if (received_output_filename) {
367 throw std::runtime_error(
"Multiple output files specified");
370 if (std::string(optarg) ==
"-") {
376 std::filesystem::path output_path(optarg);
377 if (output_path.is_absolute()) {
380 args.
output_file = std::filesystem::current_path() / output_path;
387 if (std::filesystem::exists(args.
output_file.value())) {
388 if (!std::filesystem::is_regular_file(args.
output_file.value())) {
389 throw std::runtime_error(
"Output file '" + args.
output_file.value() +
"' is not a regular file");
391 if (access(args.
output_file.value().c_str(), W_OK) != 0) {
392 throw std::runtime_error(
"No write permission for output file '" + args.
output_file.value() +
"'");
395 std::filesystem::path parent_path = std::filesystem::path(args.
output_file.value()).parent_path();
396 if (!std::filesystem::exists(parent_path) || !std::filesystem::is_directory(parent_path)) {
397 throw std::runtime_error(
"Parent directory of output file '" + args.
output_file.value() +
"' does not exist or is not a directory");
399 if (access(parent_path.c_str(), W_OK) != 0) {
400 throw std::runtime_error(
"No write permission for parent directory of output file '" + args.
output_file.value() +
"'");
403 }
catch (
const std::exception& e) {
404 throw std::runtime_error(std::string(
"Could not verify write permission for output file '") + args.
output_file.value() +
"': " + e.what());
422 if (std::find(options_with_arguments.begin(), options_with_arguments.end(), optopt) != options_with_arguments.end()) {
423 throw std::runtime_error(std::string(
"Option -") +
static_cast<char>(optopt) +
" requires an argument\nUse -h for help");
425 throw std::runtime_error(
"Unknown option: -" + std::string(1, optopt));
consteval size_t string_length(const char *str)
Definition FixedString.h:34
consteval size_t max_option_label_length()
Definition parse_arguments.h:100
consteval size_t option_label_length(const Option &opt)
Definition parse_arguments.h:79
constexpr auto help_string
Definition parse_arguments.h:196
Arguments parse_arguments(int argc, char *argv[])
Definition parse_arguments.h:289
consteval size_t calculate_help_string_length()
Definition parse_arguments.h:118
constexpr const char * help_intro
Definition parse_arguments.h:112
constexpr Option options[]
Definition parse_arguments.h:67
std::pair< std::vector< char * >, std::vector< char * > > separate_compiler_and_program_options(Arguments *args, int argc, char *argv[])
Definition parse_arguments.h:220
constexpr const char * copyright
Definition parse_arguments.h:27
consteval auto generate_help_string()
Definition parse_arguments.h:153
constexpr const char * program_name
Definition parse_arguments.h:25
Represents the parsed command-line arguments given to the compiler.
Definition parse_arguments.h:207
BashVersion target_bash_version
Definition parse_arguments.h:211
bool display_parse_tree
Definition parse_arguments.h:215
bool exit_early
Definition parse_arguments.h:217
bool display_tokens
Definition parse_arguments.h:214
std::optional< std::string > output_file
Definition parse_arguments.h:210
bool suppress_warnings
Definition parse_arguments.h:213
std::shared_ptr< std::vector< std::string > > include_paths
Definition parse_arguments.h:212
std::optional< std::string > input_file
Definition parse_arguments.h:209
std::vector< char * > program_arguments
Definition parse_arguments.h:208
Represents a Bash version to target for code generation.
Definition BashVersion.h:20
Definition FixedString.h:8
consteval void append(std::string_view str)
Definition FixedString.h:12
Represents a command-line option for the Bash++ compiler.
Definition parse_arguments.h:50
const char shortopt
Definition parse_arguments.h:51
const char * description
Definition parse_arguments.h:53
const bool takes_argument
Definition parse_arguments.h:54
const char * longopt
Definition parse_arguments.h:52
constexpr Option(char shortopt, const char *longopt, const char *description, bool takes_argument)
Definition parse_arguments.h:56
#define bpp_compiler_updated_year
Definition updated_year.h:1
#define bpp_compiler_version
Definition version.h:1