-
-
Notifications
You must be signed in to change notification settings - Fork 72
Expand file tree
/
Copy pathApp.cpp
More file actions
446 lines (426 loc) · 15.4 KB
/
Copy pathApp.cpp
File metadata and controls
446 lines (426 loc) · 15.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
/*
* Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
* Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
* All rights reserved.
*
* SPDX-License-Identifier: MIT
*
* Licensed under the MIT License
*/
#include "qdmi/devices/na/Generator.hpp"
#include <cstddef>
#include <cstdint>
#include <exception>
#include <iostream>
#include <optional>
#include <span>
#include <spdlog/spdlog.h>
#include <stdexcept>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
namespace {
/**
* Prints the usage information for the command line tool.
* @param programName is the name of the program executable.
*/
auto printUsage(const std::string& programName) -> void {
std::cout
<< "Generator for turning neutral atom computer JSON specifications into "
"header files to be used as part of a neutral atom QDMI device "
"implementation.\n"
"\n"
"Usage: "
<< programName
<< " [OPTIONS] <command> [ARGS]\n"
"\n"
"Commands:\n"
" schema Generate a default JSON schema.\n"
" validate Validate a JSON specification.\n"
" generate Generate a header file from a JSON specification.\n"
"\n"
"Options:\n"
" -h, --help Show this help message and exit.\n"
" -v, --version Show version information and exit.\n";
}
/**
* Prints the usage information for the schema sub-command.
* @param programName is the name of the program executable.
*/
auto printSchemaUsage(const std::string& programName) -> void {
std::cout << "Generates a JSON schema with default values.\n"
"\n"
"Usage: "
<< programName
<< " schema [options]\n"
"\n"
"Options:\n"
" -h, --help Show this help message and exit.\n"
" -o, --output <file> Specify the output file. If not\n"
" specified, prints to stdout.\n";
}
/**
* Prints the usage information for the validate sub-command.
* @param programName is the name of the program executable.
*/
auto printValidateUsage(const std::string& programName) -> void {
std::cout << "Validates a JSON specification against the schema.\n"
"\n"
"Usage: "
<< programName
<< " validate [options] [<json_file>]\n"
"\n"
"Arguments:\n"
" json_file the path to the JSON file to validate. If\n"
" not specified, the JSON is read from stdin.\n"
"\n"
"Options:\n"
" -h, --help Show this help message and exit.\n";
}
/**
* Prints the usage information for the generate sub-command.
* @param programName is the name of the program executable.
*/
auto printGenerateUsage(const std::string& programName) -> void {
std::cout << "Generates a header file from a JSON specification.\n"
"\n"
"Usage: "
<< programName
<< " generate [options] <json_file>\n"
"\n"
"Arguments:\n"
" json_file the path to the JSON file to generate the\n"
" header file from. If not specified, the\n"
" JSON is read from stdin.\n"
"\n"
"Options:\n"
" -h, --help Show this help message and exit.\n"
" -o, --output <file> Specify the output file for the\n"
" generated header file. If no output\n"
" file is specified, the header file is\n"
" printed to stdout.\n";
}
/**
* Prints the version information for the command line tool.
*/
auto printVersion() -> void {
// NOLINTNEXTLINE(misc-include-cleaner)
std::cout << "MQT QDMI NA Device Generator (MQT Version " MQT_CORE_VERSION
")\n";
}
/// Enum to represent the different commands that can be executed.
enum class Command : uint8_t {
Schema, ///< Command to generate a JSON schema
Validate, ///< Command to validate a JSON specification
Generate ///< Command to generate a header file from a JSON specification
};
/// Struct to hold the parsed command line arguments.
struct Arguments {
std::string programName; ///< Name of the program executable
bool help = false; ///< Flag to indicate if help is requested
/// Flag to indicate if version information is requested
bool version = false;
std::optional<Command> command; ///< Command to execute
};
/// Struct to hold the parsed schema command line arguments.
struct SchemaArguments {
bool help = false; ///< Flag to indicate if help is requested
/// Optional output file for the schema
std::optional<std::string> outputFile;
};
/// Struct to hold the parsed validate command line arguments.
struct ValidateArguments {
bool help = false; ///< Flag to indicate if help is requested
/// Optional JSON file to validate
std::optional<std::string> jsonFile;
};
/// Struct to hold the parsed generate command line arguments.
struct GenerateArguments {
bool help = false; ///< Flag to indicate if help is requested
/// Optional output file for the generated header file
std::optional<std::string> outputFile;
/// Optional JSON file to parse the device configuration
std::optional<std::string> jsonFile;
};
/**
* Parses the command line arguments and returns an Arguments struct.
* @param args is the vector of command line arguments.
* @returns the parsed arguments as an Arguments struct and an index indicating
* the position of the first sub-command argument.
* @throws std::invalid_argument if the value after an option is missing.
*/
auto parseArguments(const std::vector<std::string>& args)
-> std::pair<Arguments, size_t> {
Arguments arguments;
arguments.programName =
args.empty() ? "mqt-core-na-device-gen" : args.front();
size_t i = 1;
while (i < args.size()) {
if (const std::string& arg = args.at(i); arg == "-h" || arg == "--help") {
arguments.help = true;
} else if (arg == "-v" || arg == "--version") {
arguments.version = true;
} else if (arg == "schema") {
arguments.command = Command::Schema;
break; // No more arguments for schema command
} else if (arg == "validate") {
arguments.command = Command::Validate;
break; // No more arguments for validate command
} else if (arg == "generate") {
arguments.command = Command::Generate;
break; // No more arguments for generate command
} else {
throw std::invalid_argument("Unknown argument: " + arg);
}
++i;
}
return {arguments, i + 1};
}
/**
* Parses the command line arguments for the schema command and returns a
* SchemaArguments struct.
* @param args is the vector of command line arguments.
* @param i is the index to the first sub-command argument within @p args
* @return Parsed schema arguments as a SchemaArguments struct.
*/
auto parseSchemaArguments(const std::vector<std::string>& args, size_t i)
-> SchemaArguments {
SchemaArguments schemaArgs;
while (i < args.size()) {
if (const std::string& arg = args.at(i); arg == "-h" || arg == "--help") {
schemaArgs.help = true;
} else if (arg == "-o" || arg == "--output") {
if (++i >= args.size()) {
throw std::invalid_argument("Missing value for output option.");
}
schemaArgs.outputFile = args.at(i);
} else {
throw std::invalid_argument("Unknown argument: " + arg);
}
++i;
}
return schemaArgs;
}
/**
* Parses the command line arguments for the validate command and returns a
* ValidateArguments struct.
* @param args is the vector of command line arguments.
* @param i is the index to the first sub-command argument within @p args
* @return Parsed validate arguments as a ValidateArguments struct.
*/
auto parseValidateArguments(const std::vector<std::string>& args, size_t i)
-> ValidateArguments {
ValidateArguments validateArgs;
while (i < args.size()) {
if (const std::string& arg = args.at(i); arg == "-h" || arg == "--help") {
validateArgs.help = true;
} else {
validateArgs.jsonFile = arg;
}
++i;
}
return validateArgs;
}
/**
* Parses the command line arguments for the generate command and returns a
* GenerateArguments struct.
* @param args is the vector of command line arguments.
* @param i is the index to the first sub-command argument within @p args
* @return Parsed generate arguments as a GenerateArguments struct.
*/
auto parseGenerateArguments(const std::vector<std::string>& args, size_t i)
-> GenerateArguments {
GenerateArguments generateArgs;
while (i < args.size()) {
if (const std::string& arg = args.at(i); arg == "-h" || arg == "--help") {
generateArgs.help = true;
} else if (arg == "-o" || arg == "--output") {
if (++i >= args.size()) {
throw std::invalid_argument("Missing value for output option.");
}
generateArgs.outputFile = args.at(i);
} else {
generateArgs.jsonFile = arg;
}
++i;
}
return generateArgs;
}
/**
* Executes the schema command, generating a JSON schema and writing it to the
* specified output file or stdout.
* @param progName is the name of the program executable.
* @param argVec is the vector of command line arguments.
* @param i is the index to the first sub-command argument within @p argVec
* @return 0 on success, 1 on error.
*/
auto executeSchemaCommand(const std::string& progName,
const std::vector<std::string>& argVec,
const size_t i) -> int {
SchemaArguments schemaArgs;
// parse the rest of the command line arguments for the schema command
try {
schemaArgs = parseSchemaArguments(argVec, i);
} catch (const std::exception& e) {
SPDLOG_ERROR("Error parsing schema arguments: {}", e.what());
printSchemaUsage(progName);
return 1;
}
// if the help flag is set, print the schema usage information and exit
if (schemaArgs.help) {
printSchemaUsage(progName);
return 0;
}
// generate the JSON schema and write it to the output file or stdout
try {
if (schemaArgs.outputFile.has_value()) {
na::writeJSONSchema(schemaArgs.outputFile.value());
} else {
na::writeJSONSchema(std::cout);
}
} catch (const std::exception& e) {
SPDLOG_ERROR("Error generating JSON schema: {}", e.what());
return 1;
}
return 0;
}
/**
* Executes the validate command, validating a JSON file or JSON string from
* stdin.
* @param progName is the name of the program executable.
* @param argVec is the vector of command line arguments.
* @param i is the index to the first sub-command argument within @p argVec
* @return 0 on success, 1 on error.
*/
auto executeValidateCommand(const std::string& progName,
const std::vector<std::string>& argVec,
const size_t i) -> int {
// parse the rest of the command line arguments for the validate command
const ValidateArguments validateArgs = parseValidateArguments(argVec, i);
//
if (validateArgs.help) {
printValidateUsage(progName);
return 0;
}
// validate the JSON file or the JSON string from stdin
try {
if (validateArgs.jsonFile.has_value()) {
std::ignore = na::readJSON(validateArgs.jsonFile.value());
} else {
std::ignore = na::readJSON(std::cin);
}
} catch (const std::exception& e) {
SPDLOG_ERROR("Error validating JSON: {}", e.what());
return 1;
}
return 0;
}
/**
* Executes the generate command, generating a header file from a JSON file or
* JSON string from stdin.
* @param progName is the name of the program executable.
* @param argVec is the vector of command line arguments.
* @param i is the index to the first sub-command argument within @p argVec
* @return 0 on success, 1 on error.
*/
auto executeGenerateCommand(const std::string& progName,
const std::vector<std::string>& argVec,
const size_t i) -> int {
GenerateArguments generateArgs;
// parse the rest of the command line arguments for the generate command
try {
generateArgs = parseGenerateArguments(argVec, i);
} catch (const std::exception& e) {
SPDLOG_ERROR("Error parsing generate arguments: {}", e.what());
printGenerateUsage(progName);
return 1;
}
// if the help flag is set, print the generate usage information and exit
if (generateArgs.help) {
printGenerateUsage(progName);
return 0;
}
// generate the header file from the JSON specification
try {
na::Device device;
// read the JSON file or the JSON string from stdin
if (generateArgs.jsonFile.has_value()) {
device = na::readJSON(generateArgs.jsonFile.value());
} else {
device = na::readJSON(std::cin);
}
// write the header file to the output file or stdout
if (generateArgs.outputFile.has_value()) {
na::writeHeader(device, generateArgs.outputFile.value());
} else {
na::writeHeader(device, std::cout);
}
} catch (const std::exception& e) {
SPDLOG_ERROR("Error generating header file: {}", e.what());
return 1;
}
return 0;
}
} // namespace
/**
* @brief Main function that parses command-line-arguments and processes the
* JSON.
* @details This function handles the command line arguments, checks for help
* and version flags, and processes the JSON file or schema file as specified by
* the user. Either a JSON file or a schema file must be provided. If no output
* file is specified, the JSON file is parsed but no header file is generated.
*
* @param argc is the number of command line arguments.
* @param argv is the array of command line arguments.
*/
int main(int argc, char* argv[]) {
std::vector<std::string> argVec;
std::pair<Arguments, size_t> parsedArgs;
// `main` functions should not throw exceptions. Apparently, the
// initialization of a vector can throw exceptions, so we catch them here.
try {
argVec.reserve(static_cast<size_t>(argc));
for (const auto& arg : std::span(argv, static_cast<size_t>(argc))) {
argVec.emplace_back(arg);
}
} catch (std::exception& e) {
SPDLOG_ERROR("Error parsing arguments into vector: {}", e.what());
return 1;
}
// parse the command line arguments up to the first sub-command
try {
parsedArgs = parseArguments(argVec);
} catch (const std::exception& e) {
SPDLOG_ERROR("Error parsing arguments: {}", e.what());
printUsage(argVec.empty() ? "mqt-core-na-device-gen" : argVec.front());
return 1;
}
// unpack the parsed arguments and the index of the first sub-command here
// because structured bindings only work with fresh variables
const auto& [args, i] = parsedArgs;
// print help or version information if requested
if (args.help) {
printUsage(args.programName);
return 0;
}
// if the version flag is set, print the version information and exit
if (args.version) {
printVersion();
return 0;
}
// if no command is specified, print the usage information
if (!args.command.has_value()) {
printUsage(args.programName);
return 1;
}
switch (*args.command) {
case Command::Schema:
return executeSchemaCommand(args.programName, argVec, i);
case Command::Validate:
return executeValidateCommand(args.programName, argVec, i);
case Command::Generate:
return executeGenerateCommand(args.programName, argVec, i);
}
return 0;
}