diff options
-rw-r--r-- | cli/generator.cxx | 13 | ||||
-rw-r--r-- | cli/runtime-source.cxx | 755 | ||||
-rw-r--r-- | cli/runtime-source.hxx | 2 |
3 files changed, 389 insertions, 381 deletions
diff --git a/cli/generator.cxx b/cli/generator.cxx index 8b4b244..1ebde25 100644 --- a/cli/generator.cxx +++ b/cli/generator.cxx @@ -160,7 +160,9 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p) // Check if we need to generate the runtime code. If we include // another options file, then we assume the runtime is generated - // there. + // there. However, to reduce the number of standard headers we + // have to include in the generated header file, we will still + // need to generate some template code in the source file. // bool runtime (true); for (semantics::cli_unit::includes_iterator i (unit.includes_begin ()); @@ -288,13 +290,10 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p) (br ? '>' : '"') << endl << endl; - if (runtime) - { - if (!inl) - generate_runtime_inline (ctx); + if (runtime && !inl) + generate_runtime_inline (ctx); - generate_runtime_source (ctx); - } + generate_runtime_source (ctx, runtime); if (!inl) generate_inline (ctx); diff --git a/cli/runtime-source.cxx b/cli/runtime-source.cxx index 37dc1ac..5964fdd 100644 --- a/cli/runtime-source.cxx +++ b/cli/runtime-source.cxx @@ -8,7 +8,7 @@ using namespace std; void -generate_runtime_source (context& ctx) +generate_runtime_source (context& ctx, bool complete) { ostream& os (ctx.os); @@ -19,7 +19,7 @@ generate_runtime_source (context& ctx) << "#include <ostream>" << endl << "#include <sstream>" << endl; - if (ctx.options.generate_file_scanner ()) + if (complete && ctx.options.generate_file_scanner ()) os << "#include <cstring>" << endl << "#include <fstream>" << endl; @@ -27,443 +27,452 @@ generate_runtime_source (context& ctx) ctx.cli_open (); - // unknown_option - // - os << "// unknown_option" << endl - << "//" << endl - << "unknown_option::" << endl - << "~unknown_option () throw ()" - << "{" - << "}" - - << "void unknown_option::" << endl - << "print (std::ostream& os) const" - << "{" - << "os << \"unknown option '\" << option () << \"'\";" - << "}" - - << "const char* unknown_option::" << endl - << "what () const throw ()" - << "{" - << "return \"unknown option\";" - << "}"; - - // unknown_argument - // - os << "// unknown_argument" << endl - << "//" << endl - << "unknown_argument::" << endl - << "~unknown_argument () throw ()" - << "{" - << "}" - - << "void unknown_argument::" << endl - << "print (std::ostream& os) const" - << "{" - << "os << \"unknown argument '\" << argument () << \"'\";" - << "}" - - << "const char* unknown_argument::" << endl - << "what () const throw ()" - << "{" - << "return \"unknown argument\";" - << "}"; - - // missing_value - // - os << "// missing_value" << endl - << "//" << endl - << "missing_value::" << endl - << "~missing_value () throw ()" - << "{" - << "}" - - << "void missing_value::" << endl - << "print (std::ostream& os) const" - << "{" - << "os << \"missing value for option '\" << option () << \"'\";" - << "}" - - << "const char* missing_value::" << endl - << "what () const throw ()" - << "{" - << "return \"missing option value\";" - << "}"; - - // invalid_value - // - os << "// invalid_value" << endl - << "//" << endl - << "invalid_value::" << endl - << "~invalid_value () throw ()" - << "{" - << "}" - - << "void invalid_value::" << endl - << "print (std::ostream& os) const" - << "{" - << "os << \"invalid value '\" << value () << \"' for option '\"" << endl - << " << option () << \"'\";" - << "}" - - << "const char* invalid_value::" << endl - << "what () const throw ()" - << "{" - << "return \"invalid option value\";" - << "}"; - - // eos_reached - // - os << "// eos_reached" << endl - << "//" << endl - << "void eos_reached::" << endl - << "print (std::ostream& os) const" - << "{" - << "os << what ();" - << "}" - - << "const char* eos_reached::" << endl - << "what () const throw ()" - << "{" - << "return \"end of argument stream reached\";" - << "}"; - - if (ctx.options.generate_file_scanner ()) + if (complete) { - // file_io_failure + // unknown_option // - os << "// file_io_failure" << endl + os << "// unknown_option" << endl << "//" << endl - << "file_io_failure::" << endl - << "~file_io_failure () throw ()" + << "unknown_option::" << endl + << "~unknown_option () throw ()" << "{" << "}" - << "void file_io_failure::" << endl + << "void unknown_option::" << endl << "print (std::ostream& os) const" << "{" - << "os << \"unable to open file '\" << file () << \"' or read failure\";" + << "os << \"unknown option '\" << option () << \"'\";" << "}" - << "const char* file_io_failure::" << endl + << "const char* unknown_option::" << endl << "what () const throw ()" << "{" - << "return \"unable to open file or read failure\";" + << "return \"unknown option\";" << "}"; - // unmatched_argument + // unknown_argument // - os << "// unmatched_quote" << endl + os << "// unknown_argument" << endl << "//" << endl - << "unmatched_quote::" << endl - << "~unmatched_quote () throw ()" + << "unknown_argument::" << endl + << "~unknown_argument () throw ()" << "{" << "}" - << "void unmatched_quote::" << endl + << "void unknown_argument::" << endl << "print (std::ostream& os) const" << "{" - << "os << \"unmatched quote in argument '\" << argument () << \"'\";" + << "os << \"unknown argument '\" << argument () << \"'\";" << "}" - << "const char* unmatched_quote::" << endl + << "const char* unknown_argument::" << endl << "what () const throw ()" << "{" - << "return \"unmatched quote\";" + << "return \"unknown argument\";" << "}"; - } - - // scanner - // - os << "// scanner" << endl - << "//" << endl - << "scanner::" << endl - << "~scanner ()" - << "{" - << "}"; - - // argv_scanner - // - os << "// argv_scanner" << endl - << "//" << endl - - << "bool argv_scanner::" << endl - << "more ()" - << "{" - << "return i_ < argc_;" - << "}" - - << "const char* argv_scanner::" << endl - << "peek ()" - << "{" - << "if (i_ < argc_)" << endl - << "return argv_[i_];" - << "else" << endl - << "throw eos_reached ();" - << "}" - - << "const char* argv_scanner::" << endl - << "next ()" - << "{" - << "if (i_ < argc_)" - << "{" - << "const char* r (argv_[i_]);" - << endl - << "if (erase_)" - << "{" - << "for (int i (i_ + 1); i < argc_; ++i)" << endl - << "argv_[i - 1] = argv_[i];" - << endl - << "--argc_;" - << "argv_[argc_] = 0;" - << "}" - << "else" << endl - << "++i_;" - << endl - << "return r;" - << "}" - << "else" << endl - << "throw eos_reached ();" - << "}" - - << "void argv_scanner::" << endl - << "skip ()" - << "{" - << "if (i_ < argc_)" << endl - << "++i_;" - << "else" << endl - << "throw eos_reached ();" - << "}"; - - // argv_file_scanner - // - if (ctx.options.generate_file_scanner ()) - { - bool sep (!ctx.opt_sep.empty ()); - - os << "// argv_file_scanner" << endl - << "//" << endl - << "bool argv_file_scanner::" << endl - << "more ()" - << "{" - << "if (!args_.empty ())" << endl - << "return true;" - << endl - << "while (base::more ())" - << "{" - << "// See if the next argument is the file option." << endl + // missing_value + // + os << "// missing_value" << endl << "//" << endl - << "const char* a (base::peek ());" - << "const option_info* oi;" - << endl - << "if (" << (sep ? "!skip_ && " : "") << "(oi = find (a)))" - << "{" - << "base::next ();" - << endl - << "if (!base::more ())" << endl - << "throw missing_value (oi->option);" - << endl - << "if (oi->search_func != 0)" + << "missing_value::" << endl + << "~missing_value () throw ()" << "{" - << "std::string f (oi->search_func (base::next (), oi->arg));" - << endl - << "if (!f.empty ())" << endl - << "load (f);" - << "}" - << "else" << endl - << "load (base::next ());" - << endl - << "if (!args_.empty ())" << endl - << "return true;" - << "}" - << "else" - << "{"; - if (sep) - os << "if (!skip_)" << endl - << "skip_ = (std::strcmp (a, \"" << ctx.opt_sep << "\") == 0);" - << endl; - os << "return true;" - << "}" - << "}" // while - << "return false;" << "}" - << "const char* argv_file_scanner::" << endl - << "peek ()" + << "void missing_value::" << endl + << "print (std::ostream& os) const" << "{" - << "if (!more ())" << endl - << "throw eos_reached ();" - << endl - << "return args_.empty () ? base::peek () : args_.front ().c_str ();" + << "os << \"missing value for option '\" << option () << \"'\";" << "}" - << "const char* argv_file_scanner::" << endl - << "next ()" - << "{" - << "if (!more ())" << endl - << "throw eos_reached ();" - << endl - << "if (args_.empty ())" << endl - << "return base::next ();" - << "else" + << "const char* missing_value::" << endl + << "what () const throw ()" << "{" - << "hold_.swap (args_.front ());" - << "args_.pop_front ();" - << "return hold_.c_str ();" - << "}" - << "}" + << "return \"missing option value\";" + << "}"; - << "void argv_file_scanner::" << endl - << "skip ()" + // invalid_value + // + os << "// invalid_value" << endl + << "//" << endl + << "invalid_value::" << endl + << "~invalid_value () throw ()" << "{" - << "if (!more ())" << endl - << "throw eos_reached ();" - << endl - << "if (args_.empty ())" << endl - << "return base::skip ();" - << "else" << endl - << "args_.pop_front ();" << "}" - << "const argv_file_scanner::option_info* argv_file_scanner::" << endl - << "find (const char* a) const" + << "void invalid_value::" << endl + << "print (std::ostream& os) const" << "{" - << "for (std::size_t i (0); i < options_count_; ++i)" << endl - << "if (std::strcmp (a, options_[i].option) == 0)" << endl - << "return &options_[i];" - << endl - << "return 0;" + << "os << \"invalid value '\" << value () << \"' for option '\"" << endl + << " << option () << \"'\";" << "}" - << "void argv_file_scanner::" << endl - << "load (const std::string& file)" - << "{" - << "using namespace std;" - << endl - << "ifstream is (file.c_str ());" - << endl - << "if (!is.is_open ())" << endl - << "throw file_io_failure (file);" - << endl - << "while (!is.eof ())" + << "const char* invalid_value::" << endl + << "what () const throw ()" << "{" - << "string line;" - << "getline (is, line);" - << endl - << "if (is.fail () && !is.eof ())" << endl - << "throw file_io_failure (file);" - << endl - << "string::size_type n (line.size ());" - << endl - << "// Trim the line from leading and trailing whitespaces." << endl + << "return \"invalid option value\";" + << "}"; + + // eos_reached + // + os << "// eos_reached" << endl << "//" << endl - << "if (n != 0)" + << "void eos_reached::" << endl + << "print (std::ostream& os) const" << "{" - << "const char* f (line.c_str ());" - << "const char* l (f + n);" - << endl - << "const char* of (f);" - << "while (f < l && (*f == ' ' || *f == '\\t' || *f == '\\r'))" << endl - << "++f;" - << endl - << "--l;" - << endl - << "const char* ol (l);" - << "while (l > f && (*l == ' ' || *l == '\\t' || *l == '\\r'))" << endl - << "--l;" - << endl - << "if (f != of || l != ol)" << endl - << "line = f <= l ? string (f, l - f + 1) : string ();" + << "os << what ();" << "}" - << "// Ignore empty lines, those that start with #." << endl + + << "const char* eos_reached::" << endl + << "what () const throw ()" + << "{" + << "return \"end of argument stream reached\";" + << "}"; + + if (ctx.options.generate_file_scanner ()) + { + // file_io_failure + // + os << "// file_io_failure" << endl + << "//" << endl + << "file_io_failure::" << endl + << "~file_io_failure () throw ()" + << "{" + << "}" + + << "void file_io_failure::" << endl + << "print (std::ostream& os) const" + << "{" + << "os << \"unable to open file '\" << file () << \"' or read " << + "failure\";" + << "}" + + << "const char* file_io_failure::" << endl + << "what () const throw ()" + << "{" + << "return \"unable to open file or read failure\";" + << "}"; + + // unmatched_argument + // + os << "// unmatched_quote" << endl + << "//" << endl + << "unmatched_quote::" << endl + << "~unmatched_quote () throw ()" + << "{" + << "}" + + << "void unmatched_quote::" << endl + << "print (std::ostream& os) const" + << "{" + << "os << \"unmatched quote in argument '\" << argument () << \"'\";" + << "}" + + << "const char* unmatched_quote::" << endl + << "what () const throw ()" + << "{" + << "return \"unmatched quote\";" + << "}"; + } + + // scanner + // + os << "// scanner" << endl << "//" << endl - << "if (line.empty () || line[0] == '#')" << endl - << "continue;" - << endl - << "string::size_type p (line.find (' '));" - << endl - << "if (p == string::npos)" - << "{"; - if (sep) - os << "if (!skip_)" << endl - << "skip_ = (line == \"" << ctx.opt_sep << "\");" - << endl; - os << "args_.push_back (line);" - << "}" - << "else" + << "scanner::" << endl + << "~scanner ()" << "{" - << "string s1 (line, 0, p);" - << endl - << "// Skip leading whitespaces in the argument." << endl + << "}"; + + // argv_scanner + // + os << "// argv_scanner" << endl << "//" << endl - << "n = line.size ();" - << "for (++p; p < n; ++p)" + + << "bool argv_scanner::" << endl + << "more ()" << "{" - << "char c (line[p]);" - << endl - << "if (c != ' ' && c != '\\t' && c != '\\r')" << endl - << "break;" + << "return i_ < argc_;" << "}" - << "string s2 (line, p);" - << endl - << "// If the string is wrapped in quotes, remove them." << endl - << "//" << endl - << "n = s2.size ();" - << "char cf (s2[0]), cl (s2[n - 1]);" - << endl - << "if (cf == '\"' || cf == '\\'' || cl == '\"' || cl == '\\'')" + + << "const char* argv_scanner::" << endl + << "peek ()" << "{" - << "if (n == 1 || cf != cl)" << endl - << "throw unmatched_quote (s2);" - << endl - << "s2 = string (s2, 1, n - 2);" + << "if (i_ < argc_)" << endl + << "return argv_[i_];" + << "else" << endl + << "throw eos_reached ();" << "}" - << "const option_info* oi;" - << "if (" << (sep ? "!skip_ && " : "") << "(oi = find (s1.c_str ())))" << endl + + << "const char* argv_scanner::" << endl + << "next ()" << "{" - << "if (s2.empty ())" << endl - << "throw missing_value (oi->option);" + << "if (i_ < argc_)" + << "{" + << "const char* r (argv_[i_]);" << endl - << "if (oi->search_func != 0)" + << "if (erase_)" << "{" - << "std::string f (oi->search_func (s2.c_str (), oi->arg));" + << "for (int i (i_ + 1); i < argc_; ++i)" << endl + << "argv_[i - 1] = argv_[i];" << endl - << "if (!f.empty ())" << endl - << "load (f);" + << "--argc_;" + << "argv_[argc_] = 0;" << "}" << "else" << endl - << "load (s2);" - << "}" - << "else" - << "{" - << "args_.push_back (s1);" - << "args_.push_back (s2);" + << "++i_;" + << endl + << "return r;" << "}" + << "else" << endl + << "throw eos_reached ();" << "}" - << "}" // while - << "}"; - } - // Option description. - // - if (ctx.options.generate_description ()) - { - // options - // - os << "void options::" << endl - << "push_back (const option& o)" + << "void argv_scanner::" << endl + << "skip ()" << "{" - << "container_type::size_type n (size ());" - << "container_type::push_back (o);" - << "map_[o.name ()] = n;" - << endl - << "for (option_names::const_iterator i (o.aliases ().begin ());" << endl - << "i != o.aliases ().end (); ++i)" << endl - << "map_[*i] = n;" + << "if (i_ < argc_)" << endl + << "++i_;" + << "else" << endl + << "throw eos_reached ();" << "}"; + + // argv_file_scanner + // + if (ctx.options.generate_file_scanner ()) + { + bool sep (!ctx.opt_sep.empty ()); + + os << "// argv_file_scanner" << endl + << "//" << endl + + << "bool argv_file_scanner::" << endl + << "more ()" + << "{" + << "if (!args_.empty ())" << endl + << "return true;" + << endl + << "while (base::more ())" + << "{" + << "// See if the next argument is the file option." << endl + << "//" << endl + << "const char* a (base::peek ());" + << "const option_info* oi;" + << endl + << "if (" << (sep ? "!skip_ && " : "") << "(oi = find (a)))" + << "{" + << "base::next ();" + << endl + << "if (!base::more ())" << endl + << "throw missing_value (oi->option);" + << endl + << "if (oi->search_func != 0)" + << "{" + << "std::string f (oi->search_func (base::next (), oi->arg));" + << endl + << "if (!f.empty ())" << endl + << "load (f);" + << "}" + << "else" << endl + << "load (base::next ());" + << endl + << "if (!args_.empty ())" << endl + << "return true;" + << "}" + << "else" + << "{"; + if (sep) + os << "if (!skip_)" << endl + << "skip_ = (std::strcmp (a, \"" << ctx.opt_sep << "\") == 0);" + << endl; + os << "return true;" + << "}" + << "}" // while + << "return false;" + << "}" + + << "const char* argv_file_scanner::" << endl + << "peek ()" + << "{" + << "if (!more ())" << endl + << "throw eos_reached ();" + << endl + << "return args_.empty () ? base::peek () : args_.front ().c_str ();" + << "}" + + << "const char* argv_file_scanner::" << endl + << "next ()" + << "{" + << "if (!more ())" << endl + << "throw eos_reached ();" + << endl + << "if (args_.empty ())" << endl + << "return base::next ();" + << "else" + << "{" + << "hold_.swap (args_.front ());" + << "args_.pop_front ();" + << "return hold_.c_str ();" + << "}" + << "}" + + << "void argv_file_scanner::" << endl + << "skip ()" + << "{" + << "if (!more ())" << endl + << "throw eos_reached ();" + << endl + << "if (args_.empty ())" << endl + << "return base::skip ();" + << "else" << endl + << "args_.pop_front ();" + << "}" + + << "const argv_file_scanner::option_info* argv_file_scanner::" << endl + << "find (const char* a) const" + << "{" + << "for (std::size_t i (0); i < options_count_; ++i)" << endl + << "if (std::strcmp (a, options_[i].option) == 0)" << endl + << "return &options_[i];" + << endl + << "return 0;" + << "}" + + << "void argv_file_scanner::" << endl + << "load (const std::string& file)" + << "{" + << "using namespace std;" + << endl + << "ifstream is (file.c_str ());" + << endl + << "if (!is.is_open ())" << endl + << "throw file_io_failure (file);" + << endl + << "while (!is.eof ())" + << "{" + << "string line;" + << "getline (is, line);" + << endl + << "if (is.fail () && !is.eof ())" << endl + << "throw file_io_failure (file);" + << endl + << "string::size_type n (line.size ());" + << endl + << "// Trim the line from leading and trailing whitespaces." << endl + << "//" << endl + << "if (n != 0)" + << "{" + << "const char* f (line.c_str ());" + << "const char* l (f + n);" + << endl + << "const char* of (f);" + << "while (f < l && (*f == ' ' || *f == '\\t' || *f == '\\r'))" << endl + << "++f;" + << endl + << "--l;" + << endl + << "const char* ol (l);" + << "while (l > f && (*l == ' ' || *l == '\\t' || *l == '\\r'))" << endl + << "--l;" + << endl + << "if (f != of || l != ol)" << endl + << "line = f <= l ? string (f, l - f + 1) : string ();" + << "}" + << "// Ignore empty lines, those that start with #." << endl + << "//" << endl + << "if (line.empty () || line[0] == '#')" << endl + << "continue;" + << endl + << "string::size_type p (line.find (' '));" + << endl + << "if (p == string::npos)" + << "{"; + if (sep) + os << "if (!skip_)" << endl + << "skip_ = (line == \"" << ctx.opt_sep << "\");" + << endl; + os << "args_.push_back (line);" + << "}" + << "else" + << "{" + << "string s1 (line, 0, p);" + << endl + << "// Skip leading whitespaces in the argument." << endl + << "//" << endl + << "n = line.size ();" + << "for (++p; p < n; ++p)" + << "{" + << "char c (line[p]);" + << endl + << "if (c != ' ' && c != '\\t' && c != '\\r')" << endl + << "break;" + << "}" + << "string s2 (line, p);" + << endl + << "// If the string is wrapped in quotes, remove them." << endl + << "//" << endl + << "n = s2.size ();" + << "char cf (s2[0]), cl (s2[n - 1]);" + << endl + << "if (cf == '\"' || cf == '\\'' || cl == '\"' || cl == '\\'')" + << "{" + << "if (n == 1 || cf != cl)" << endl + << "throw unmatched_quote (s2);" + << endl + << "s2 = string (s2, 1, n - 2);" + << "}" + << "const option_info* oi;" + << "if (" << (sep ? "!skip_ && " : "") << + "(oi = find (s1.c_str ())))" << endl + << "{" + << "if (s2.empty ())" << endl + << "throw missing_value (oi->option);" + << endl + << "if (oi->search_func != 0)" + << "{" + << "std::string f (oi->search_func (s2.c_str (), oi->arg));" + << endl + << "if (!f.empty ())" << endl + << "load (f);" + << "}" + << "else" << endl + << "load (s2);" + << "}" + << "else" + << "{" + << "args_.push_back (s1);" + << "args_.push_back (s2);" + << "}" + << "}" + << "}" // while + << "}"; + } + + // Option description. + // + if (ctx.options.generate_description ()) + { + // options + // + os << "void options::" << endl + << "push_back (const option& o)" + << "{" + << "container_type::size_type n (size ());" + << "container_type::push_back (o);" + << "map_[o.name ()] = n;" + << endl + << "for (option_names::const_iterator i (o.aliases ().begin ());" << endl + << "i != o.aliases ().end (); ++i)" << endl + << "map_[*i] = n;" + << "}"; + } } + // To reduce the number of standard headers we have to include in the + // generated header file, we always generate the following templates + // in the source file. + // bool sp (ctx.specifier); // parser class template & its specializations diff --git a/cli/runtime-source.hxx b/cli/runtime-source.hxx index 79e0cef..8300c9a 100644 --- a/cli/runtime-source.hxx +++ b/cli/runtime-source.hxx @@ -9,6 +9,6 @@ #include "context.hxx" void -generate_runtime_source (context&); +generate_runtime_source (context&, bool complete); #endif // CLI_RUNTIME_SOURCE_HXX |