Bash++
Bash++ compiler internal documentation
parse_arguments.h
Go to the documentation of this file.
1
7#pragma once
8
9#include <iostream>
10#include <string>
11#include <cstring>
12#include <vector>
13#include <optional>
14#include <filesystem>
15#include <memory>
16#include <unistd.h>
17#include <algorithm>
18
19#include <include/FixedString.h>
20#include <include/BashVersion.h>
21#include <include/xgetopt.h>
22
23#include <version.h>
24#include <updated_year.h>
25
26constexpr const char* program_name = "Bash++";
27
28constexpr const char* copyright = "Copyright (C) 2024-"
30 " Andrew S. Rightenburg\n\n"
31 "This program is free software; you can redistribute it and/or modify\n"
32 "it under the terms of the GNU General Public License as published by\n"
33 "the Free Software Foundation; either version 3 of the License, or\n"
34 "(at your option) any later version.\n\n"
35 "This program is distributed in the hope that it will be useful,\n"
36 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
37 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
38 "GNU General Public License for more details.\n\n"
39 "You should have received a copy of the GNU General Public License\n"
40 "along with this program. If not, see http://www.gnu.org/licenses/.\n";
41
42constexpr const char* help_intro =
43 "Usage: bpp [options] [file] ...\n"
44 "If no file is specified, read from stdin\n"
45 "All arguments after the file are passed to the compiled program\n"
46 "Options:\n";
47
48constexpr XGetOpt::OptionParser<
49 XGetOpt::Option<'o', "output", "Specify output file (default: run on exit)", XGetOpt::RequiredArgument>,
50 XGetOpt::Option<'b', "target-bash", "Target Bash version (default: 5.2)", XGetOpt::RequiredArgument>,
51 XGetOpt::Option<'s', "no-warnings", "Suppress warnings", XGetOpt::NoArgument>,
52 XGetOpt::Option<'I', "include", "Add directory to include path", XGetOpt::RequiredArgument>,
53 XGetOpt::Option<'t', "tokens", "Display tokens from lexer (do not compile program)", XGetOpt::NoArgument>,
54 XGetOpt::Option<'p', "parse-tree", "Display parse tree (do not compile program)", XGetOpt::NoArgument>,
55 XGetOpt::Option<'v', "version", "Display version information and exit", XGetOpt::NoArgument>,
56 XGetOpt::Option<'h', "help", "Display this help message and exit", XGetOpt::NoArgument>
58
68struct Arguments {
69 std::vector<char*> program_arguments;
70 std::optional<std::string> input_file;
71 std::optional<std::string> output_file;
72 BashVersion target_bash_version = {5, 2}; // Default to Bash 5.2
73 std::shared_ptr<std::vector<std::string>> include_paths = std::make_shared<std::vector<std::string>>();
74 bool suppress_warnings = false;
75 bool display_tokens = false;
76 bool display_parse_tree = false;
77
78 bool exit_early = false; // Exit early if the request is just -h/--help or -v/--version
79};
80
81inline Arguments parse_arguments(int argc, char* argv[]) {
82 Arguments args;
83
84 args.include_paths->push_back("/usr/lib/bpp/stdlib/");
85
86 // Will throw if invalid arguments are provided
87 auto [compiler_arguments, program_arguments]
89
90 args.program_arguments = std::vector<char*>{program_arguments.argv, program_arguments.argv + program_arguments.argc};
91
92 if (compiler_arguments.getNonOptionArguments().size() > 0) {
93 args.input_file = std::string(compiler_arguments.getNonOptionArguments()[0]);
94 }
95
96 bool received_output_filename = false;
97 for (const auto& arg : compiler_arguments) {
98 switch (arg.getShortOpt()) {
99 case 'b':
100 // Parse the target Bash version
101 {
102 std::istringstream version_stream(std::string(arg.getArgument()));
103 uint16_t major, minor;
104 char dot;
105 if (!(version_stream >> major >> dot >> minor) || dot != '.') {
106 throw std::runtime_error("Invalid Bash version format: " + std::string(arg.getArgument()) +
107 "\nExpected format: <major>.<minor> (e.g., 5.2)");
108 }
109 args.target_bash_version = {major, minor};
110 }
111 break;
112 case 'h':
113 std::cout << program_name << " " << bpp_compiler_version << std::endl
114 << help_intro << OptionParser.getHelpString();
115 args.exit_early = true;
116 return args;
117 break;
118 case 'I':
119 // Verify the given include path is a directory
120 if (!std::filesystem::exists(arg.getArgument()) || !std::filesystem::is_directory(arg.getArgument())) {
121 throw std::runtime_error("Include path '" + std::string(arg.getArgument()) + "' does not exist or is not a directory");
122 }
123 args.include_paths->push_back(std::string(arg.getArgument()));
124 break;
125 case 'o':
126 if (received_output_filename) {
127 throw std::runtime_error("Multiple output files specified");
128 }
129
130 if (std::string(arg.getArgument()) == "-") {
131 args.output_file = arg.getArgument();
132 break;
133 }
134
135 {
136 std::filesystem::path output_path(arg.getArgument());
137 if (output_path.is_absolute()) {
138 args.output_file = output_path.string();
139 } else {
140 args.output_file = std::filesystem::current_path() / output_path;
141 }
142 }
143
144 // Check if we have permission to write to the specified output file
145 // If the file exists, verify write access; if it doesn't, verify write access on its parent directory
146 try {
147 if (std::filesystem::exists(args.output_file.value())) {
148 if (!std::filesystem::is_regular_file(args.output_file.value())) {
149 throw std::runtime_error("Output file '" + args.output_file.value() + "' is not a regular file");
150 }
151 if (access(args.output_file.value().c_str(), W_OK) != 0) {
152 throw std::runtime_error("No write permission for output file '" + args.output_file.value() + "'");
153 }
154 } else {
155 std::filesystem::path parent_path = std::filesystem::path(args.output_file.value()).parent_path();
156 if (!std::filesystem::exists(parent_path) || !std::filesystem::is_directory(parent_path)) {
157 throw std::runtime_error("Parent directory of output file '" + args.output_file.value() + "' does not exist or is not a directory");
158 }
159 if (access(parent_path.c_str(), W_OK) != 0) {
160 throw std::runtime_error("No write permission for parent directory of output file '" + args.output_file.value() + "'");
161 }
162 }
163 } catch (const std::exception& e) {
164 throw std::runtime_error(std::string("Could not verify write permission for output file '") + args.output_file.value() + "': " + e.what());
165 }
166 break;
167 case 'p':
168 args.display_parse_tree = true;
169 break;
170 case 's':
171 args.suppress_warnings = true;
172 break;
173 case 't':
174 args.display_tokens = true;
175 break;
176 case 'v':
177 std::cout << program_name << " " << bpp_compiler_version << std::endl << copyright;
178 args.exit_early = true;
179 return args;
180 break;
181 }
182 }
183
184 return args;
185}
Parses command-line arguments based on a set of defined options.
Definition xgetopt.h:532
@ NoArgument
Definition xgetopt.h:179
@ RequiredArgument
Definition xgetopt.h:180
@ AfterFirstNonOptionArgument
Definition xgetopt.h:516
Arguments parse_arguments(int argc, char *argv[])
Definition parse_arguments.h:81
constexpr const char * help_intro
Definition parse_arguments.h:42
constexpr XGetOpt::OptionParser< XGetOpt::Option< 'o', "output", "Specify output file (default: run on exit)", XGetOpt::RequiredArgument >, XGetOpt::Option< 'b', "target-bash", "Target Bash version (default: 5.2)", XGetOpt::RequiredArgument >, XGetOpt::Option< 's', "no-warnings", "Suppress warnings", XGetOpt::NoArgument >, XGetOpt::Option< 'I', "include", "Add directory to include path", XGetOpt::RequiredArgument >, XGetOpt::Option< 't', "tokens", "Display tokens from lexer (do not compile program)", XGetOpt::NoArgument >, XGetOpt::Option< 'p', "parse-tree", "Display parse tree (do not compile program)", XGetOpt::NoArgument >, XGetOpt::Option< 'v', "version", "Display version information and exit", XGetOpt::NoArgument >, XGetOpt::Option< 'h', "help", "Display this help message and exit", XGetOpt::NoArgument > > OptionParser
Definition parse_arguments.h:57
constexpr const char * copyright
Definition parse_arguments.h:28
constexpr const char * program_name
Definition parse_arguments.h:26
Represents the parsed command-line arguments given to the compiler.
Definition parse_arguments.h:68
BashVersion target_bash_version
Definition parse_arguments.h:72
bool display_parse_tree
Definition parse_arguments.h:76
bool exit_early
Definition parse_arguments.h:78
bool display_tokens
Definition parse_arguments.h:75
std::optional< std::string > output_file
Definition parse_arguments.h:71
bool suppress_warnings
Definition parse_arguments.h:74
std::shared_ptr< std::vector< std::string > > include_paths
Definition parse_arguments.h:73
std::optional< std::string > input_file
Definition parse_arguments.h:70
std::vector< char * > program_arguments
Definition parse_arguments.h:69
Represents a Bash version to target for code generation.
Definition BashVersion.h:21
Compile-time representation of a command-line option.
Definition xgetopt.h:200
#define bpp_compiler_updated_year
Definition updated_year.h:1
#define bpp_compiler_version
Definition version.h:1