Add echo command and unit test for command parser
This commit is contained in:
parent
3ae80a192f
commit
7a5b36fe52
|
@ -2,13 +2,13 @@ cmake_minimum_required(VERSION 3.9)
|
||||||
|
|
||||||
project("libminishell")
|
project("libminishell")
|
||||||
|
|
||||||
#file(GLOB_RECURSE EXEC_SOURCES test/main.cpp)
|
file(GLOB_RECURSE EXEC_SOURCES test/test_command_parser.cpp)
|
||||||
file(GLOB_RECURSE LIB_SOURCES src/*.cpp)
|
file(GLOB_RECURSE LIB_SOURCES src/*.cpp)
|
||||||
file(GLOB_RECURSE LIB_HEADERS src/*.h)
|
file(GLOB_RECURSE LIB_HEADERS src/*.h)
|
||||||
files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
|
files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
|
||||||
|
|
||||||
#Remove test executable from library
|
#Remove test executable from library
|
||||||
#list(REMOVE_ITEM LIB_SOURCES ${EXEC_SOURCES})
|
list(REMOVE_ITEM LIB_SOURCES ${EXEC_SOURCES})
|
||||||
|
|
||||||
#Create the library
|
#Create the library
|
||||||
add_library(libminishell STATIC
|
add_library(libminishell STATIC
|
||||||
|
@ -22,8 +22,8 @@ target_link_libraries(libminishell
|
||||||
libvtrutil)
|
libvtrutil)
|
||||||
|
|
||||||
#Create the test executable
|
#Create the test executable
|
||||||
#add_executable(read_arch_openfpga ${EXEC_SOURCES})
|
add_executable(test_command_parser ${EXEC_SOURCES})
|
||||||
#target_link_libraries(read_arch_openfpga libarchopenfpga)
|
target_link_libraries(test_command_parser libminishell)
|
||||||
|
|
||||||
#Supress IPO link warnings if IPO is enabled
|
#Supress IPO link warnings if IPO is enabled
|
||||||
#get_target_property(READ_ARCH_USES_IPO read_arch_openfpga INTERPROCEDURAL_OPTIMIZATION)
|
#get_target_property(READ_ARCH_USES_IPO read_arch_openfpga INTERPROCEDURAL_OPTIMIZATION)
|
||||||
|
|
|
@ -10,8 +10,8 @@ namespace minishell {
|
||||||
/*********************************************************************
|
/*********************************************************************
|
||||||
* Public constructors
|
* Public constructors
|
||||||
********************************************************************/
|
********************************************************************/
|
||||||
Command::Command(const std::string& name) {
|
Command::Command(const char* name) {
|
||||||
name_ = name;
|
name_ = std::string(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************************
|
/************************************************************************
|
||||||
|
@ -48,7 +48,7 @@ std::vector<CommandOptionId> Command::require_value_options() const {
|
||||||
CommandOptionId Command::option(const std::string& name) const {
|
CommandOptionId Command::option(const std::string& name) const {
|
||||||
/* Ensure that the name is unique in the option list */
|
/* Ensure that the name is unique in the option list */
|
||||||
std::map<std::string, CommandOptionId>::const_iterator name_it = option_name2ids_.find(name);
|
std::map<std::string, CommandOptionId>::const_iterator name_it = option_name2ids_.find(name);
|
||||||
if (name_it != option_name2ids_.end()) {
|
if (name_it == option_name2ids_.end()) {
|
||||||
return CommandOptionId::INVALID();
|
return CommandOptionId::INVALID();
|
||||||
}
|
}
|
||||||
return option_name2ids_.at(name);
|
return option_name2ids_.at(name);
|
||||||
|
@ -57,7 +57,7 @@ CommandOptionId Command::option(const std::string& name) const {
|
||||||
CommandOptionId Command::short_option(const std::string& name) const {
|
CommandOptionId Command::short_option(const std::string& name) const {
|
||||||
/* Ensure that the name is unique in the option list */
|
/* Ensure that the name is unique in the option list */
|
||||||
std::map<std::string, CommandOptionId>::const_iterator name_it = option_short_name2ids_.find(name);
|
std::map<std::string, CommandOptionId>::const_iterator name_it = option_short_name2ids_.find(name);
|
||||||
if (name_it != option_short_name2ids_.end()) {
|
if (name_it == option_short_name2ids_.end()) {
|
||||||
return CommandOptionId::INVALID();
|
return CommandOptionId::INVALID();
|
||||||
}
|
}
|
||||||
return option_short_name2ids_.at(name);
|
return option_short_name2ids_.at(name);
|
||||||
|
@ -104,11 +104,11 @@ std::string Command::option_description(const CommandOptionId& option_id) const
|
||||||
***********************************************************************/
|
***********************************************************************/
|
||||||
|
|
||||||
/* Add an option without required values */
|
/* Add an option without required values */
|
||||||
CommandOptionId Command::add_option(const std::string& name,
|
CommandOptionId Command::add_option(const char* name,
|
||||||
const bool& option_required,
|
const bool& option_required,
|
||||||
const std::string& description) {
|
const char* description) {
|
||||||
/* Ensure that the name is unique in the option list */
|
/* Ensure that the name is unique in the option list */
|
||||||
std::map<std::string, CommandOptionId>::const_iterator name_it = option_name2ids_.find(name);
|
std::map<std::string, CommandOptionId>::const_iterator name_it = option_name2ids_.find(std::string(name));
|
||||||
if (name_it != option_name2ids_.end()) {
|
if (name_it != option_name2ids_.end()) {
|
||||||
return CommandOptionId::INVALID();
|
return CommandOptionId::INVALID();
|
||||||
}
|
}
|
||||||
|
@ -116,40 +116,40 @@ CommandOptionId Command::add_option(const std::string& name,
|
||||||
/* This is a legal name. we can create a new id */
|
/* This is a legal name. we can create a new id */
|
||||||
CommandOptionId option = CommandOptionId(option_ids_.size());
|
CommandOptionId option = CommandOptionId(option_ids_.size());
|
||||||
option_ids_.push_back(option);
|
option_ids_.push_back(option);
|
||||||
option_names_.push_back(name);
|
option_names_.push_back(std::string(name));
|
||||||
option_short_names_.emplace_back();
|
option_short_names_.emplace_back();
|
||||||
option_required_.push_back(option_required);
|
option_required_.push_back(option_required);
|
||||||
option_require_value_types_.push_back(NUM_OPT_VALUE_TYPES);
|
option_require_value_types_.push_back(NUM_OPT_VALUE_TYPES);
|
||||||
option_description_.push_back(description);
|
option_description_.push_back(std::string(description));
|
||||||
|
|
||||||
/* Register the name and short name in the name2id map */
|
/* Register the name and short name in the name2id map */
|
||||||
option_name2ids_[name] = option;
|
option_name2ids_[std::string(name)] = option;
|
||||||
|
|
||||||
return option;
|
return option;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add a short name to an option */
|
/* Add a short name to an option */
|
||||||
bool Command::set_option_short_name(const CommandOptionId& option_id,
|
bool Command::set_option_short_name(const CommandOptionId& option_id,
|
||||||
const std::string& short_name) {
|
const char* short_name) {
|
||||||
/* Validate the option id */
|
/* Validate the option id */
|
||||||
VTR_ASSERT(true == valid_option_id(option_id));
|
VTR_ASSERT(true == valid_option_id(option_id));
|
||||||
|
|
||||||
/* Short name is optional, so do the following only when it is not empty
|
/* Short name is optional, so do the following only when it is not empty
|
||||||
* Ensure that the short name is unique in the option list
|
* Ensure that the short name is unique in the option list
|
||||||
*/
|
*/
|
||||||
if (true == short_name.empty()) {
|
if (true == std::string(short_name).empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, CommandOptionId>::const_iterator short_name_it = option_short_name2ids_.find(short_name);
|
std::map<std::string, CommandOptionId>::const_iterator short_name_it = option_short_name2ids_.find(std::string(short_name));
|
||||||
if (short_name_it != option_short_name2ids_.end()) {
|
if (short_name_it != option_short_name2ids_.end()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
option_short_names_[option_id] = short_name;
|
option_short_names_[option_id] = std::string(short_name);
|
||||||
|
|
||||||
/* Short name is optional, so register it only when it is not empty */
|
/* Short name is optional, so register it only when it is not empty */
|
||||||
option_short_name2ids_[short_name] = option_id;
|
option_short_name2ids_[std::string(short_name)] = option_id;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ class Command {
|
||||||
/* Create range */
|
/* Create range */
|
||||||
typedef vtr::Range<command_option_iterator> command_option_range;
|
typedef vtr::Range<command_option_iterator> command_option_range;
|
||||||
public: /* Constructor */
|
public: /* Constructor */
|
||||||
Command(const std::string& name);
|
Command(const char* name);
|
||||||
public: /* Public accessors */
|
public: /* Public accessors */
|
||||||
std::string name() const;
|
std::string name() const;
|
||||||
command_option_range options() const;
|
command_option_range options() const;
|
||||||
|
@ -68,10 +68,10 @@ class Command {
|
||||||
e_option_value_type option_require_value_type(const CommandOptionId& option_id) const;
|
e_option_value_type option_require_value_type(const CommandOptionId& option_id) const;
|
||||||
std::string option_description(const CommandOptionId& option_id) const;
|
std::string option_description(const CommandOptionId& option_id) const;
|
||||||
public: /* Public mutators */
|
public: /* Public mutators */
|
||||||
CommandOptionId add_option(const std::string& name,
|
CommandOptionId add_option(const char* name,
|
||||||
const bool& option_required,
|
const bool& option_required,
|
||||||
const std::string& description);
|
const char* description);
|
||||||
bool set_option_short_name(const CommandOptionId& option_id, const std::string& short_name);
|
bool set_option_short_name(const CommandOptionId& option_id, const char* short_name);
|
||||||
void set_option_require_value(const CommandOptionId& option_id,
|
void set_option_require_value(const CommandOptionId& option_id,
|
||||||
const e_option_value_type& option_require_value_type);
|
const e_option_value_type& option_require_value_type);
|
||||||
public: /* Public validators */
|
public: /* Public validators */
|
||||||
|
|
|
@ -14,13 +14,13 @@ namespace minishell {
|
||||||
* This function is mainly used to create help desk for a command
|
* This function is mainly used to create help desk for a command
|
||||||
********************************************************************/
|
********************************************************************/
|
||||||
void print_command_options(const Command& cmd) {
|
void print_command_options(const Command& cmd) {
|
||||||
VTR_LOG("Command '%s' usage:",
|
VTR_LOG("Command '%s' usage:\n",
|
||||||
cmd.name().c_str());
|
cmd.name().c_str());
|
||||||
for (const CommandOptionId& opt : cmd.options()) {
|
for (const CommandOptionId& opt : cmd.options()) {
|
||||||
VTR_LOG("%s, %s : %s",
|
VTR_LOG("--%s, -%s : %s\n",
|
||||||
cmd.option_name(opt).c_str(),
|
cmd.option_name(opt).c_str(),
|
||||||
cmd.option_short_name(opt).c_str(),
|
cmd.option_short_name(opt).c_str(),
|
||||||
cmd.option_description(opt));
|
cmd.option_description(opt).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,23 +31,29 @@ void print_command_options(const Command& cmd) {
|
||||||
********************************************************************/
|
********************************************************************/
|
||||||
void print_command_context(const Command& cmd,
|
void print_command_context(const Command& cmd,
|
||||||
const CommandContext& cmd_context) {
|
const CommandContext& cmd_context) {
|
||||||
VTR_LOG("Confirm selected options when call command '%s':",
|
VTR_LOG("Confirm selected options when call command '%s':\n",
|
||||||
cmd.name().c_str());
|
cmd.name().c_str());
|
||||||
for (const CommandOptionId& opt : cmd.options()) {
|
for (const CommandOptionId& opt : cmd.options()) {
|
||||||
if (false == cmd.option_require_value(opt)) {
|
if (false == cmd.option_require_value(opt)) {
|
||||||
VTR_LOG("%s, %s : %s",
|
VTR_LOG("--%s, -%s : %s\n",
|
||||||
cmd.option_name(opt).c_str(),
|
cmd.option_name(opt).c_str(),
|
||||||
cmd.option_short_name(opt).c_str(),
|
cmd.option_short_name(opt).c_str(),
|
||||||
cmd_context.option_enable(cmd, opt) ? "on" : "off");
|
cmd_context.option_enable(cmd, opt) ? "on" : "off");
|
||||||
} else {
|
} else {
|
||||||
VTR_ASSERT_SAFE (true == cmd.option_require_value(opt));
|
VTR_ASSERT_SAFE (true == cmd.option_require_value(opt));
|
||||||
VTR_LOG("%s, %s : %s",
|
/* If the option is disabled, we output an off */
|
||||||
|
std::string opt_value;
|
||||||
|
if (false == cmd_context.option_enable(cmd, opt)) {
|
||||||
|
opt_value = std::string("off");
|
||||||
|
} else {
|
||||||
|
opt_value = cmd_context.option_value(cmd, opt);
|
||||||
|
}
|
||||||
|
VTR_LOG("--%s, -%s : %s\n",
|
||||||
cmd.option_name(opt).c_str(),
|
cmd.option_name(opt).c_str(),
|
||||||
cmd.option_short_name(opt).c_str(),
|
cmd.option_short_name(opt).c_str(),
|
||||||
cmd_context.option_value(cmd, opt).c_str());
|
opt_value.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} /* End namespace minshell */
|
} /* End namespace minshell */
|
||||||
|
|
|
@ -23,7 +23,7 @@ CommandOptionId parse_option(const std::string& argv,
|
||||||
CommandOptionId option_id = cmd.option(argv);
|
CommandOptionId option_id = cmd.option(argv);
|
||||||
/* Not found, error out */
|
/* Not found, error out */
|
||||||
if (CommandOptionId::INVALID() == option_id) {
|
if (CommandOptionId::INVALID() == option_id) {
|
||||||
VTR_LOG("Detect unknown option '%s'!\n",
|
VTR_LOG("Detect unknown option '--%s'!\n",
|
||||||
argv.c_str());
|
argv.c_str());
|
||||||
}
|
}
|
||||||
/* Found, update the CommandContext */
|
/* Found, update the CommandContext */
|
||||||
|
@ -43,7 +43,7 @@ CommandOptionId parse_short_option(const std::string& argv,
|
||||||
CommandOptionId option_id = cmd.short_option(argv);
|
CommandOptionId option_id = cmd.short_option(argv);
|
||||||
/* Not found, error out */
|
/* Not found, error out */
|
||||||
if (CommandOptionId::INVALID() == option_id) {
|
if (CommandOptionId::INVALID() == option_id) {
|
||||||
VTR_LOG("Detect unknown option '%s'!\n",
|
VTR_LOG("Detect unknown short option '-%s'!\n",
|
||||||
argv.c_str());
|
argv.c_str());
|
||||||
}
|
}
|
||||||
/* Found, update the CommandContext */
|
/* Found, update the CommandContext */
|
||||||
|
@ -63,8 +63,8 @@ bool parse_command(const std::vector<std::string>& argv,
|
||||||
VTR_ASSERT(1 <= argv.size());
|
VTR_ASSERT(1 <= argv.size());
|
||||||
|
|
||||||
/* Validate that the command name matches argv[0] */
|
/* Validate that the command name matches argv[0] */
|
||||||
if (argv[0] == cmd.name()) {
|
if (argv[0] != cmd.name()) {
|
||||||
VTR_LOG("Unexpected command name '%s'!",
|
VTR_LOG("Unexpected command name '%s'!\n",
|
||||||
argv[0]);
|
argv[0]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ bool parse_command(const std::vector<std::string>& argv,
|
||||||
for (size_t iarg = 1; iarg < argv.size(); ++iarg) {
|
for (size_t iarg = 1; iarg < argv.size(); ++iarg) {
|
||||||
/* Option must start with dash */
|
/* Option must start with dash */
|
||||||
if (0 != strncmp("-", argv[iarg].c_str(), 1)) {
|
if (0 != strncmp("-", argv[iarg].c_str(), 1)) {
|
||||||
VTR_LOG("Invalid option '%s'!",
|
VTR_LOG("Invalid option '%s'!\n",
|
||||||
argv[iarg].c_str());
|
argv[iarg].c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -136,19 +136,25 @@ bool parse_command(const std::vector<std::string>& argv,
|
||||||
std::vector<CommandOptionId> missing_options = cmd_context.check_required_options(cmd);
|
std::vector<CommandOptionId> missing_options = cmd_context.check_required_options(cmd);
|
||||||
if (!missing_options.empty()) {
|
if (!missing_options.empty()) {
|
||||||
for (const CommandOptionId& missing_opt : missing_options) {
|
for (const CommandOptionId& missing_opt : missing_options) {
|
||||||
VTR_LOG("Required option '%s' is missing!",
|
VTR_LOG("Required option '%s' is missing!\n",
|
||||||
cmd.option_name(missing_opt));
|
cmd.option_name(missing_opt).c_str());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CommandOptionId> missing_value_options = cmd_context.check_required_option_values(cmd);
|
std::vector<CommandOptionId> missing_value_options = cmd_context.check_required_option_values(cmd);
|
||||||
if (!missing_value_options.empty()) {
|
if (!missing_value_options.empty()) {
|
||||||
|
bool parse_fail = false;
|
||||||
for (const CommandOptionId& missing_opt : missing_value_options) {
|
for (const CommandOptionId& missing_opt : missing_value_options) {
|
||||||
VTR_LOG("Require value for option '%s' is missing!",
|
/* If the missing option is not enabled, we can skip this option */
|
||||||
cmd.option_name(missing_opt));
|
if (false == cmd_context.option_enable(cmd, missing_opt)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
VTR_LOG("Require value for option '%s' is missing!\n",
|
||||||
|
cmd.option_name(missing_opt).c_str());
|
||||||
|
parse_fail = true;
|
||||||
}
|
}
|
||||||
return false;
|
return !parse_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/********************************************************************
|
||||||
|
* Test the command parser by pre-defining a simple command
|
||||||
|
*******************************************************************/
|
||||||
|
#include "command_parser.h"
|
||||||
|
#include "command_echo.h"
|
||||||
|
|
||||||
|
using namespace minishell;
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
/* Create a new command */
|
||||||
|
Command cmd("read_openfpga_arch");
|
||||||
|
/* Add options to the command */
|
||||||
|
/* Option to specify architecture file */
|
||||||
|
CommandOptionId opt_arch_file = cmd.add_option("file", true, "Specify the OpenFPGA architecture file");
|
||||||
|
cmd.set_option_require_value(opt_arch_file, OPT_STRING);
|
||||||
|
cmd.set_option_short_name(opt_arch_file, "f");
|
||||||
|
|
||||||
|
CommandOptionId opt_echo_arch = cmd.add_option("echo", false, "Echo the parsed result to file");
|
||||||
|
cmd.set_option_require_value(opt_echo_arch, OPT_STRING);
|
||||||
|
cmd.set_option_short_name(opt_echo_arch, "e");
|
||||||
|
|
||||||
|
CommandOptionId opt_help = cmd.add_option("help", false, "Help desk");
|
||||||
|
cmd.set_option_short_name(opt_help, "h");
|
||||||
|
|
||||||
|
/* Parse the option, to avoid issues, we use the command name to replace the argv[0] */
|
||||||
|
std::vector<std::string> cmd_opts;
|
||||||
|
cmd_opts.push_back(cmd.name());
|
||||||
|
for (int iarg = 1; iarg < argc; ++iarg) {
|
||||||
|
cmd_opts.push_back(std::string(argv[iarg]));
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandContext cmd_context(cmd);
|
||||||
|
if (false == parse_command(cmd_opts, cmd, cmd_context)) {
|
||||||
|
/* Echo the command */
|
||||||
|
print_command_options(cmd);
|
||||||
|
} else {
|
||||||
|
/* Let user to confirm selected options */
|
||||||
|
print_command_context(cmd, cmd_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue