update shell with new function binding strategy and new help desk print-out
This commit is contained in:
parent
7073e4d082
commit
f8c5c1a117
|
@ -37,10 +37,11 @@ namespace minishell {
|
|||
* // Add execute function to the command 'read_arch'
|
||||
* // This is the function to be called when the command is used
|
||||
* // Assume that you have a function read_arch() which does the job of read architecture
|
||||
* // Note that the function read_arch() should return a void with only two arguments
|
||||
* // void read_arch(T, const CommandContext&);
|
||||
* // Note that the function read_arch() should return a void with only three arguments
|
||||
* // void read_arch(T, const Command&, const CommandContext&);
|
||||
* // The first argument is the template type you set when define the shell
|
||||
* // The second argument is a read-only object storing the parsing results for the command
|
||||
* // The second argument is a read-only object storing the options for the command
|
||||
* // The third argument is a read-only object storing the parsing results for the command
|
||||
* shell.set_command_execute_function(cmd_read_arch, &read_arch);
|
||||
*
|
||||
* // Run a shell
|
||||
|
@ -53,6 +54,15 @@ class Shell {
|
|||
typedef vtr::vector<ShellCommandId, ShellCommandId>::const_iterator shell_command_iterator;
|
||||
/* Create range */
|
||||
typedef vtr::Range<shell_command_iterator> shell_command_range;
|
||||
/* Enumeration of command types in a shell
|
||||
* Built-in commands have their own execute functions inside the shell
|
||||
*/
|
||||
enum e_exec_func_type {
|
||||
STANDARD,
|
||||
SHORT,
|
||||
BUILTIN,
|
||||
NUM_EXEC_FUNC_TYPES
|
||||
};
|
||||
public: /* Constructor */
|
||||
Shell<T>(const char* name);
|
||||
public: /* Public accessors */
|
||||
|
@ -61,26 +71,47 @@ class Shell {
|
|||
shell_command_range commands() const;
|
||||
ShellCommandId command(const std::string& name) const;
|
||||
std::string command_description(const ShellCommandId& cmd_id) const;
|
||||
ShellCommandClassId command_class(const ShellCommandId& cmd_id) const;
|
||||
std::string command_class_name(const ShellCommandClassId& cmd_class_id) const;
|
||||
/* We force a read-only return objects for command and command context
|
||||
* because users should NOT modify any of them!
|
||||
*/
|
||||
const Command& command(const ShellCommandId& cmd_id) const;
|
||||
const CommandContext& command_context(const ShellCommandId& cmd_id) const;
|
||||
std::vector<ShellCommandId> command_dependency(const ShellCommandId& cmd_id) const;
|
||||
std::vector<ShellCommandId> commands_by_class(const ShellCommandClassId& cmd_class_id) const;
|
||||
public: /* Public mutators */
|
||||
void add_title(const char* title);
|
||||
ShellCommandId add_command(const Command& cmd, const char* descr);
|
||||
void set_command_class(const ShellCommandId& cmd_id, const ShellCommandClassId& cmd_class_id);
|
||||
/* Link the execute function to a command
|
||||
* We support here three types of functions to be executed in the shell
|
||||
* 1. Standard function, including the data exchange <T> and commands
|
||||
* 2. Short function, including only the data exchange <T>
|
||||
* 3. Built-in function, including only the shell envoriment variables
|
||||
* Users just need to specify the function object and its type will be automatically inferred
|
||||
*/
|
||||
void set_command_execute_function(const ShellCommandId& cmd_id,
|
||||
std::function<void(T&, const Command&, const CommandContext&)> exec_func);
|
||||
void set_command_execute_function(const ShellCommandId& cmd_id,
|
||||
std::function<void(T&)> exec_func);
|
||||
void set_command_execute_function(const ShellCommandId& cmd_id,
|
||||
std::function<void()> exec_func);
|
||||
void set_command_dependency(const ShellCommandId& cmd_id,
|
||||
const std::vector<ShellCommandId> cmd_dependency);
|
||||
ShellCommandClassId add_command_class(const char* name);
|
||||
public: /* Public validators */
|
||||
bool valid_command_id(const ShellCommandId& cmd_id) const;
|
||||
bool valid_command_class_id(const ShellCommandClassId& cmd_class_id) const;
|
||||
public: /* Public executors */
|
||||
/* Start the interactive mode, where users will type-in command by command */
|
||||
void run_interactive_mode(T& context);
|
||||
/* Start the script mode, where users provide a file which includes all the commands to run */
|
||||
void run_script_mode(const char* script_file_name, T& context);
|
||||
/* Print all the commands by their classes. This is actually the help desk */
|
||||
void print_commands() const;
|
||||
/* Quit the shell */
|
||||
void exit() const;
|
||||
private: /* Private executors */
|
||||
/* Execute a command, the command line is the user's input to launch a command
|
||||
* The common_context is the data structure to exchange data between commands
|
||||
|
@ -93,6 +124,12 @@ class Shell {
|
|||
/* Title of the shell, this will appear in the interactive mode as an introduction */
|
||||
std::string title_;
|
||||
|
||||
/* Unique ids for each class of command */
|
||||
vtr::vector<ShellCommandClassId, ShellCommandClassId> command_class_ids_;
|
||||
|
||||
/* Names for each class of command */
|
||||
vtr::vector<ShellCommandClassId, std::string> command_class_names_;
|
||||
|
||||
/* Unique ids for each command */
|
||||
vtr::vector<ShellCommandId, ShellCommandId> command_ids_;
|
||||
|
||||
|
@ -105,8 +142,23 @@ class Shell {
|
|||
/* Description of the command, this is going to be printed out in the help desk */
|
||||
vtr::vector<ShellCommandId, std::string> command_description_;
|
||||
|
||||
/* Function pointers to execute each command */
|
||||
vtr::vector<ShellCommandId, std::function<void(T&, const Command&, const CommandContext&)>> command_execute_functions_;
|
||||
/* Class ids for each command */
|
||||
vtr::vector<ShellCommandId, ShellCommandClassId> command_classes_;
|
||||
|
||||
/* Function pointers to execute each command
|
||||
* We support here three types of functions to be executed in the shell
|
||||
* 1. Standard function, including the data exchange <T> and commands
|
||||
* 2. Short function, including only the data exchange <T>
|
||||
* 3. Built-in function, including only the shell envoriment variables
|
||||
*/
|
||||
vtr::vector<ShellCommandId, std::function<void(T&, const Command&, const CommandContext&)>> command_standard_execute_functions_;
|
||||
vtr::vector<ShellCommandId, std::function<void(T&)>> command_short_execute_functions_;
|
||||
vtr::vector<ShellCommandId, std::function<void()>> command_builtin_execute_functions_;
|
||||
|
||||
/* Type of execute functions for each command.
|
||||
* This is supposed to be an internal data ONLY
|
||||
*/
|
||||
vtr::vector<ShellCommandId, e_exec_func_type> command_execute_function_types_;
|
||||
|
||||
/* Dependency graph for different commands,
|
||||
* This helps the shell interface to check if a command need other commands to be run before its execution
|
||||
|
@ -115,11 +167,13 @@ class Shell {
|
|||
|
||||
/* Fast name look-up */
|
||||
std::map<std::string, ShellCommandId> command_name2ids_;
|
||||
std::map<std::string, ShellCommandClassId> command_class2ids_;
|
||||
vtr::vector<ShellCommandClassId, std::vector<ShellCommandId>> commands_by_classes_;
|
||||
};
|
||||
|
||||
} /* End namespace minshell */
|
||||
|
||||
/* Include the implementation functions in the header file */
|
||||
/* Include the template implementation functions in the header file */
|
||||
#include "shell.tpp"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Member functions for class Shell
|
||||
********************************************************************/
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_log.h"
|
||||
|
@ -63,6 +64,18 @@ std::string Shell<T>::command_description(const ShellCommandId& cmd_id) const {
|
|||
return command_description_[cmd_id];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
ShellCommandClassId Shell<T>::command_class(const ShellCommandId& cmd_id) const {
|
||||
VTR_ASSERT(true == valid_command_id(cmd_id));
|
||||
return command_classes_[cmd_id];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::string Shell<T>::command_class_name(const ShellCommandClassId& cmd_class_id) const {
|
||||
VTR_ASSERT(true == valid_command_class_id(cmd_class_id));
|
||||
return command_class_names_[cmd_class_id];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
const Command& Shell<T>::command(const ShellCommandId& cmd_id) const {
|
||||
VTR_ASSERT(true == valid_command_id(cmd_id));
|
||||
|
@ -81,6 +94,12 @@ std::vector<ShellCommandId> Shell<T>::command_dependency(const ShellCommandId& c
|
|||
return command_dependencies_[cmd_id];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::vector<ShellCommandId> Shell<T>::commands_by_class(const ShellCommandClassId& cmd_class_id) const {
|
||||
VTR_ASSERT(true == valid_command_class_id(cmd_class_id));
|
||||
return commands_by_classes_[cmd_class_id];
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Public mutators
|
||||
***********************************************************************/
|
||||
|
@ -104,7 +123,11 @@ ShellCommandId Shell<T>::add_command(const Command& cmd, const char* descr) {
|
|||
commands_.emplace_back(cmd);
|
||||
command_contexts_.push_back(CommandContext(cmd));
|
||||
command_description_.push_back(descr);
|
||||
command_execute_functions_.emplace_back();
|
||||
command_classes_.push_back(ShellCommandClassId::INVALID());
|
||||
command_execute_function_types_.emplace_back();
|
||||
command_standard_execute_functions_.emplace_back();
|
||||
command_short_execute_functions_.emplace_back();
|
||||
command_builtin_execute_functions_.emplace_back();
|
||||
command_dependencies_.emplace_back();
|
||||
|
||||
/* Register the name in the name2id map */
|
||||
|
@ -113,11 +136,41 @@ ShellCommandId Shell<T>::add_command(const Command& cmd, const char* descr) {
|
|||
return shell_cmd;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Shell<T>::set_command_class(const ShellCommandId& cmd_id, const ShellCommandClassId& cmd_class_id) {
|
||||
VTR_ASSERT(true == valid_command_id(cmd_id));
|
||||
VTR_ASSERT(true == valid_command_class_id(cmd_class_id));
|
||||
command_classes_[cmd_id] = cmd_class_id;
|
||||
/* Update the fast look-up to spot commands in a class */
|
||||
std::vector<ShellCommandId>::iterator it = std::find(commands_by_classes_[cmd_class_id].begin(), commands_by_classes_[cmd_class_id].end(), cmd_id);
|
||||
/* The command does not exist in the class, add it */
|
||||
if (it == commands_by_classes_[cmd_class_id].end()) {
|
||||
commands_by_classes_[cmd_class_id].push_back(cmd_id);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Shell<T>::set_command_execute_function(const ShellCommandId& cmd_id,
|
||||
std::function<void(T&, const Command&, const CommandContext&)> exec_func) {
|
||||
VTR_ASSERT(true == valid_command_id(cmd_id));
|
||||
command_execute_functions_[cmd_id] = exec_func;
|
||||
command_execute_function_types_[cmd_id] = STANDARD;
|
||||
command_standard_execute_functions_[cmd_id] = exec_func;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Shell<T>::set_command_execute_function(const ShellCommandId& cmd_id,
|
||||
std::function<void(T&)> exec_func) {
|
||||
VTR_ASSERT(true == valid_command_id(cmd_id));
|
||||
command_execute_function_types_[cmd_id] = SHORT;
|
||||
command_short_execute_functions_[cmd_id] = exec_func;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Shell<T>::set_command_execute_function(const ShellCommandId& cmd_id,
|
||||
std::function<void()> exec_func) {
|
||||
VTR_ASSERT(true == valid_command_id(cmd_id));
|
||||
command_execute_function_types_[cmd_id] = BUILTIN;
|
||||
command_builtin_execute_functions_[cmd_id] = exec_func;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
|
@ -131,6 +184,29 @@ void Shell<T>::set_command_dependency(const ShellCommandId& cmd_id,
|
|||
command_dependencies_[cmd_id] = dependent_cmds;
|
||||
}
|
||||
|
||||
/* Add a command with it description */
|
||||
template<class T>
|
||||
ShellCommandClassId Shell<T>::add_command_class(const char* name) {
|
||||
/* Ensure that the name is unique in the command list */
|
||||
std::map<std::string, ShellCommandClassId>::const_iterator name_it = command_class2ids_.find(std::string(name));
|
||||
if (name_it != command_class2ids_.end()) {
|
||||
return ShellCommandClassId::INVALID();
|
||||
}
|
||||
|
||||
/* This is a legal name. we can create a new id */
|
||||
ShellCommandClassId cmd_class = ShellCommandClassId(command_class_ids_.size());
|
||||
command_class_ids_.push_back(cmd_class);
|
||||
command_class_names_.push_back(std::string(name));
|
||||
|
||||
/* Register the name in the name2id map */
|
||||
command_class2ids_[std::string(name)] = cmd_class;
|
||||
|
||||
/* Register in the fast look-up for commands by classes */
|
||||
commands_by_classes_.emplace_back();
|
||||
|
||||
return cmd_class;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Public executors
|
||||
***********************************************************************/
|
||||
|
@ -196,12 +272,51 @@ void Shell<T>::run_script_mode(const char* script_file_name, T& context) {
|
|||
if (cmd_end_pos != std::string::npos) {
|
||||
cmd_part = line.substr(0, cmd_end_pos);
|
||||
}
|
||||
/* Process the command */
|
||||
/* Process the command only when the line is not empty */
|
||||
if (!cmd_part.empty()) {
|
||||
execute_command(cmd_part.c_str(), context);
|
||||
}
|
||||
}
|
||||
fp.close();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Shell<T>::print_commands() const {
|
||||
/* Print the commands by their classes */
|
||||
for (const ShellCommandClassId& cmd_class : command_class_ids_) {
|
||||
/* Print the class name */
|
||||
VTR_LOG("%s:\n", command_class_names_[cmd_class].c_str());
|
||||
|
||||
size_t cnt = 0;
|
||||
for (const ShellCommandId& cmd : commands_by_classes_[cmd_class]) {
|
||||
/* Print the command names in this class
|
||||
* but limited4 command per line for a clean layout
|
||||
*/
|
||||
VTR_LOG("%s", commands_[cmd].name().c_str());
|
||||
cnt++;
|
||||
if (4 == cnt) {
|
||||
VTR_LOG("\n");
|
||||
cnt = 0;
|
||||
} else {
|
||||
VTR_LOG("\t");
|
||||
}
|
||||
}
|
||||
|
||||
/* Put a new line in the end as a splitter */
|
||||
VTR_LOG("\n");
|
||||
}
|
||||
|
||||
/* Put a new line in the end as a splitter */
|
||||
VTR_LOG("\n");
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Shell<T>::exit() const {
|
||||
VTR_LOG("Thank you for using %s!\n",
|
||||
name().c_str());
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Private executors
|
||||
***********************************************************************/
|
||||
|
@ -220,6 +335,8 @@ void Shell<T>::execute_command(const char* cmd_line,
|
|||
return;
|
||||
}
|
||||
|
||||
/* TODO: Check the dependency graph to see if all the prequistics have been met */
|
||||
|
||||
/* Find the command! Parse the options */
|
||||
if (false == parse_command(tokens, commands_[cmd_id], command_contexts_[cmd_id])) {
|
||||
/* Echo the command */
|
||||
|
@ -230,8 +347,24 @@ void Shell<T>::execute_command(const char* cmd_line,
|
|||
/* Parse succeed. Let user to confirm selected options */
|
||||
print_command_context(commands_[cmd_id], command_contexts_[cmd_id]);
|
||||
|
||||
/* Execute the command! */
|
||||
command_execute_functions_[cmd_id](common_context, commands_[cmd_id], command_contexts_[cmd_id]);
|
||||
/* Execute the command depending on the type of function ! */
|
||||
switch (command_execute_function_types_[cmd_id]) {
|
||||
case STANDARD:
|
||||
command_standard_execute_functions_[cmd_id](common_context, commands_[cmd_id], command_contexts_[cmd_id]);
|
||||
break;
|
||||
case SHORT:
|
||||
command_short_execute_functions_[cmd_id](common_context);
|
||||
break;
|
||||
case BUILTIN:
|
||||
command_builtin_execute_functions_[cmd_id]();
|
||||
break;
|
||||
default:
|
||||
/* This is not allowed! Error out */
|
||||
VTR_LOG("Invalid type of execute function for command '%s'!\n",
|
||||
commands_[cmd_id].name().c_str());
|
||||
/* Exit the shell using the exit() function inside this class! */
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
|
@ -242,4 +375,9 @@ bool Shell<T>::valid_command_id(const ShellCommandId& cmd_id) const {
|
|||
return ( size_t(cmd_id) < command_ids_.size() ) && ( cmd_id == command_ids_[cmd_id] );
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool Shell<T>::valid_command_class_id(const ShellCommandClassId& cmd_class_id) const {
|
||||
return ( size_t(cmd_class_id) < command_class_ids_.size() ) && ( cmd_class_id == command_class_ids_[cmd_class_id] );
|
||||
}
|
||||
|
||||
} /* End namespace minshell */
|
||||
|
|
|
@ -10,8 +10,10 @@ namespace minishell {
|
|||
* A strong id for the options used by a command
|
||||
********************************************************************/
|
||||
struct shell_command_id_tag;
|
||||
struct shell_command_class_id_tag;
|
||||
|
||||
typedef vtr::StrongId<shell_command_id_tag> ShellCommandId;
|
||||
typedef vtr::StrongId<shell_command_class_id_tag> ShellCommandClassId;
|
||||
|
||||
} /* End namespace minshell */
|
||||
|
||||
|
|
|
@ -12,23 +12,6 @@ using namespace minishell;
|
|||
class ShellContext {
|
||||
};
|
||||
|
||||
static
|
||||
void shell_cmd_help_executor(ShellContext& context,
|
||||
const Command& cmd,
|
||||
const CommandContext& cmd_context) {
|
||||
VTR_LOG("Help desk:\n");
|
||||
VTR_LOG("Available commands:\n");
|
||||
VTR_LOG("help\texit\n");
|
||||
}
|
||||
|
||||
static
|
||||
void shell_cmd_exit_executor(ShellContext& context,
|
||||
const Command& cmd,
|
||||
const CommandContext& cmd_context) {
|
||||
VTR_LOG("Thank you for using!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
/* Create the command to launch shell in different modes */
|
||||
Command start_cmd("test_shell");
|
||||
|
@ -52,15 +35,45 @@ int main(int argc, char** argv) {
|
|||
* 2. exit
|
||||
*/
|
||||
Shell<ShellContext> shell("test_shell");
|
||||
shell.add_title("This is a simple test shell\nAuthor: Xifan Tang\n");
|
||||
std::string shell_title;
|
||||
|
||||
Command shell_cmd_help("help");
|
||||
ShellCommandId shell_cmd_help_id = shell.add_command(shell_cmd_help, "Launch help desk");
|
||||
shell.set_command_execute_function(shell_cmd_help_id, shell_cmd_help_executor);
|
||||
shell_title += std::string("The MIT License\n");
|
||||
shell_title += std::string("\n");
|
||||
shell_title += std::string("Copyright (c) 2018 LNIS - The University of Utah\n");
|
||||
shell_title += std::string("\n");
|
||||
shell_title += std::string("Permission is hereby granted, free of charge, to any person obtaining a copy\n");
|
||||
shell_title += std::string("of this software and associated documentation files (the \"Software\"), to deal\n");
|
||||
shell_title += std::string("in the Software without restriction, including without limitation the rights\n");
|
||||
shell_title += std::string("to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n");
|
||||
shell_title += std::string("copies of the Software, and to permit persons to whom the Software is\n");
|
||||
shell_title += std::string("furnished to do so, subject to the following conditions:\n");
|
||||
shell_title += std::string("\n");
|
||||
shell_title += std::string("The above copyright notice and this permission notice shall be included in\n");
|
||||
shell_title += std::string("all copies or substantial portions of the Software.\n");
|
||||
shell_title += std::string("\n");
|
||||
shell_title += std::string("THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n");
|
||||
shell_title += std::string("IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n");
|
||||
shell_title += std::string("FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n");
|
||||
shell_title += std::string("AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n");
|
||||
shell_title += std::string("LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n");
|
||||
shell_title += std::string("OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n");
|
||||
shell_title += std::string("THE SOFTWARE.\n");
|
||||
|
||||
shell.add_title(shell_title.c_str());
|
||||
|
||||
/* Add a new class of commands */
|
||||
ShellCommandClassId basic_cmd_class = shell.add_command_class("Basic");
|
||||
|
||||
Command shell_cmd_exit("exit");
|
||||
ShellCommandId shell_cmd_exit_id = shell.add_command(shell_cmd_exit, "Exit the shell");
|
||||
shell.set_command_execute_function(shell_cmd_exit_id, shell_cmd_exit_executor);
|
||||
shell.set_command_class(shell_cmd_exit_id, basic_cmd_class);
|
||||
shell.set_command_execute_function(shell_cmd_exit_id, [shell](){shell.exit();});
|
||||
|
||||
/* Note: help must be the last to add because the linking to execute function will do a snapshot on the shell */
|
||||
Command shell_cmd_help("help");
|
||||
ShellCommandId shell_cmd_help_id = shell.add_command(shell_cmd_help, "Launch help desk");
|
||||
shell.set_command_class(shell_cmd_help_id, basic_cmd_class);
|
||||
shell.set_command_execute_function(shell_cmd_help_id, [shell](){shell.print_commands();});
|
||||
|
||||
/* Create the data base for the shell */
|
||||
ShellContext shell_context;
|
||||
|
@ -88,6 +101,8 @@ int main(int argc, char** argv) {
|
|||
shell_context);
|
||||
return 0;
|
||||
}
|
||||
/* Reach here there is something wrong, show the help desk */
|
||||
print_command_options(start_cmd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue