From 4b1e037fb0f763cd3e3401d71b66269440d75dbf Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sun, 27 Sep 2009 18:20:49 +0200 Subject: Generate parsing constructors and parsing code Also generate some runtime support code such exceptions, value parsers, etc. --- cli/generator.cxx | 6 ++ cli/header.cxx | 64 +++++++++++- cli/makefile | 2 + cli/runtime-header.cxx | 177 ++++++++++++++++++++++++++++++++ cli/runtime-header.hxx | 17 ++++ cli/runtime-source.cxx | 177 ++++++++++++++++++++++++++++++++ cli/runtime-source.hxx | 14 +++ cli/source.cxx | 272 ++++++++++++++++++++++++++++++++++++++++++++++++- 8 files changed, 725 insertions(+), 4 deletions(-) create mode 100644 cli/runtime-header.cxx create mode 100644 cli/runtime-header.hxx create mode 100644 cli/runtime-source.cxx create mode 100644 cli/runtime-source.hxx (limited to 'cli') diff --git a/cli/generator.cxx b/cli/generator.cxx index b864dfb..79401e0 100644 --- a/cli/generator.cxx +++ b/cli/generator.cxx @@ -17,6 +17,9 @@ #include "source.hxx" #include "inline.hxx" +#include "runtime-header.hxx" +#include "runtime-source.hxx" + #include "context.hxx" #include "generator.hxx" #include "name-processor.hxx" @@ -174,7 +177,9 @@ generate (semantics::cli_unit& unit, path const& p) << "#define " << guard << endl << endl; + generate_runtime_header_decl (ctx); generate_header (ctx); + generate_runtime_header_impl (ctx); if (inl) { @@ -206,6 +211,7 @@ generate (semantics::cli_unit& unit, path const& p) if (!inl) generate_inline (ctx); + generate_runtime_source (ctx); generate_source (ctx); } diff --git a/cli/header.cxx b/cli/header.cxx index c248690..8033b7f 100644 --- a/cli/header.cxx +++ b/cli/header.cxx @@ -25,6 +25,8 @@ namespace } }; + // + // struct option_data: traversal::option, context { option_data (context& c) : context (c) {} @@ -62,9 +64,61 @@ namespace << "public:" << endl << endl; + // c-tors + // + string um ("::cli::unknown_mode"); + + os << name << " (int argc," << endl + << "char** argv," << endl + << um << " option = " << um << "::fail," << endl + << um << " argument = " << um << "::stop);" + << endl; + + os << name << " (int start," << endl + << "int argc," << endl + << "char** argv," << endl + << um << " option = " << um << "::fail," << endl + << um << " argument = " << um << "::stop);" + << endl; + + os << name << " (int argc," << endl + << "char** argv," << endl + << "int& end," << endl + << um << " option = " << um << "::fail," << endl + << um << " argument = " << um << "::stop);" + << endl; + + os << name << " (int start," << endl + << "int argc," << endl + << "char** argv," << endl + << "int& end," << endl + << um << " option = " << um << "::fail," << endl + << um << " argument = " << um << "::stop);" + << endl; + + // + // + os << "// Option accessors." << endl + << "//" << endl + << "public:" << endl + << endl; + names (c, names_option_); - os << "private:" << endl; + // _parse() + // + os << "private:" << endl + << "int" << endl + << "_parse (int start," << endl + << "int argc," << endl + << "char** argv," << endl + << um << " option," << endl + << um << " argument);" + << endl; + + // Data members. + // + os << "public:" << endl; //@@ tmp names (c, names_option_data_); @@ -101,11 +155,15 @@ generate_header (context& ctx) includes includes (ctx); traversal::names unit_names; namespace_ ns (ctx); - traversal::names ns_names; class_ cl (ctx); unit >> includes; - unit >> unit_names >> ns >> ns_names >> ns; + unit >> unit_names >> ns; + unit_names >> cl; + + traversal::names ns_names; + + ns >> ns_names >> ns; ns_names >> cl; unit.dispatch (ctx.unit); diff --git a/cli/makefile b/cli/makefile index 488d758..f281167 100644 --- a/cli/makefile +++ b/cli/makefile @@ -12,6 +12,8 @@ context.cxx \ header.cxx \ inline.cxx \ source.cxx \ +runtime-header.cxx \ +runtime-source.cxx \ generator.cxx \ name-processor.cxx diff --git a/cli/runtime-header.cxx b/cli/runtime-header.cxx new file mode 100644 index 0000000..87cdf11 --- /dev/null +++ b/cli/runtime-header.cxx @@ -0,0 +1,177 @@ +// file : cli/runtime-header.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include "runtime-header.hxx" + +using namespace std; + +void +generate_runtime_header_decl (context& ctx) +{ + ostream& os (ctx.os); + + os << "#include " << endl + << "#include " << endl + << "#include " << endl + << endl; + + os << "namespace cli" + << "{"; + + // unknown_mode + // + os << "class unknown_mode" + << "{" + << "public:" + << "enum value" + << "{" + << "skip," << endl + << "stop," << endl + << "fail" << endl + << "};" + << "unknown_mode (value v)" << endl + << ": v_ (v) {}" + << "operator value () const {return v_;}" + << "private:" << endl + << "value v_;" + << "};"; + + // Exceptions. + // + + os << "// Exceptions." << endl + << "//" << endl + << endl; + + os << "class exception: public std::exception" + << "{" + << "public:" << endl + << "virtual void" << endl + << "print (std::ostream&) const = 0;" + << "};"; + + os << "inline std::ostream&" << endl + << "operator<< (std::ostream& os, const exception& e)" + << "{" + << "e.print (os);" + << "return os;" + << "}"; + + os << "class unknown_option: public exception" + << "{" + << "public:" << endl + << "virtual" << endl + << "~unknown_option () throw ();" + << endl + << "unknown_option (const std::string& option)" << endl + << ": option_ (option)" + << "{" + << "}" + << "const std::string&" << endl + << "option () const" + << "{" + << "return option_;" + << "}" + << "virtual void" << endl + << "print (std::ostream&) const;" + << endl + << "virtual const char*" << endl + << "what () const throw ();" + << endl + << "private:" << endl + << "std::string option_;" + << "};"; + + os << "class unknown_argument: public exception" + << "{" + << "public:" << endl + << "virtual" << endl + << "~unknown_argument () throw ();" + << endl + << "unknown_argument (const std::string& argument)" << endl + << ": argument_ (argument)" + << "{" + << "}" + << "const std::string&" << endl + << "argument () const" + << "{" + << "return argument_;" + << "}" + << "virtual void" << endl + << "print (std::ostream&) const;" + << endl + << "virtual const char*" << endl + << "what () const throw ();" + << endl + << "private:" << endl + << "std::string argument_;" + << "};"; + + os << "class missing_value: public exception" + << "{" + << "public:" << endl + << "virtual" << endl + << "~missing_value () throw ();" + << endl + << "missing_value (const std::string& option)" << endl + << ": option_ (option)" + << "{" + << "}" + << "const std::string&" << endl + << "option () const" + << "{" + << "return option_;" + << "}" + << "virtual void" << endl + << "print (std::ostream&) const;" + << endl + << "virtual const char*" << endl + << "what () const throw ();" + << endl + << "private:" << endl + << "std::string option_;" + << "};"; + + os << "class invalid_value: public exception" + << "{" + << "public:" << endl + << "virtual" << endl + << "~invalid_value () throw ();" + << endl + << "invalid_value (const std::string& option," << endl + << "const std::string& value)" << endl + << ": option_ (option)," + << " value_ (value)" + << "{" + << "}" + << "const std::string&" << endl + << "option () const" + << "{" + << "return option_;" + << "}" + << "const std::string&" << endl + << "value () const" + << "{" + << "return value_;" + << "}" + << "virtual void" << endl + << "print (std::ostream&) const;" + << endl + << "virtual const char*" << endl + << "what () const throw ();" + << endl + << "private:" << endl + << "std::string option_;" + << "std::string value_;" + << "};"; + + os << "}"; // namespace cli +} + +void +generate_runtime_header_impl (context& ctx) +{ + ostream& os (ctx.os); +} diff --git a/cli/runtime-header.hxx b/cli/runtime-header.hxx new file mode 100644 index 0000000..5f78e9d --- /dev/null +++ b/cli/runtime-header.hxx @@ -0,0 +1,17 @@ +// file : cli/runtime-header.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifndef CLI_RUNTIME_HEADER_HXX +#define CLI_RUNTIME_HEADER_HXX + +#include "context.hxx" + +void +generate_runtime_header_decl (context&); + +void +generate_runtime_header_impl (context&); + +#endif // CLI_RUNTIME_HEADER_HXX diff --git a/cli/runtime-source.cxx b/cli/runtime-source.cxx new file mode 100644 index 0000000..3cb5f2e --- /dev/null +++ b/cli/runtime-source.cxx @@ -0,0 +1,177 @@ +// file : cli/runtime-source.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include "runtime-source.hxx" + +using namespace std; + +void +generate_runtime_source (context& ctx) +{ + ostream& os (ctx.os); + + os << "#include " << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl + << endl; + + os << "namespace cli" + << "{"; + + // 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\";" + << "}"; + + // parser class template & its specializations + // + os << "template " << endl + << "struct parser" + << "{" + << "static int" << endl + << "parse (X& x, char** argv, int n)" + << "{" + << "if (n > 1)" + << "{" + << "std::istringstream is (argv[1]);" + << "if (!(is >> x && is.eof ()))" << endl + << "throw invalid_value (argv[0], argv[1]);" + << "return 2;" + << "}" + << "else" << endl + << "throw missing_value (argv[0]);" + << "}" + << "};"; + + // parser + // + os << "template <>" << endl + << "struct parser" + << "{" + << "static int" << endl + << "parse (bool& x, char**, int)" + << "{" + << "x = true;" + << "return 1;" + << "}" + << "};"; + + // parser + // + os << "template <>" << endl + << "struct parser" + << "{" + << "static int" << endl + << "parse (std::string& x, char** argv, int n)" + << "{" + << "if (n > 1)" + << "{" + << "x = argv[1];" + << "return 2;" + << "}" + << "else" << endl + << "throw missing_value (argv[0]);" + << "}" + << "};"; + + // parser> + // + os << "template " << endl + << "struct parser >" + << "{" + << "static int" << endl + << "parse (std::vector& v, char** argv, int n)" + << "{" + << "X x;" + << "int i (parser::parse (x, argv, n));" + << "v.push_back (x);" + << "return i;" + << "}" + << "};"; + + // Parser thunk. + // + os << "template " << endl + << "int" << endl + << "thunk (X& x, char** argv, int n)" + << "{" + << "return parser::parse (x.*P, argv, n);" + << "}"; + + os << "}"; // namespace cli +} diff --git a/cli/runtime-source.hxx b/cli/runtime-source.hxx new file mode 100644 index 0000000..efcba29 --- /dev/null +++ b/cli/runtime-source.hxx @@ -0,0 +1,14 @@ +// file : cli/runtime-source.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifndef CLI_RUNTIME_SOURCE_HXX +#define CLI_RUNTIME_SOURCE_HXX + +#include "context.hxx" + +void +generate_runtime_source (context&); + +#endif // CLI_RUNTIME_SOURCE_HXX diff --git a/cli/source.cxx b/cli/source.cxx index 75be8a3..e9e4365 100644 --- a/cli/source.cxx +++ b/cli/source.cxx @@ -7,18 +7,288 @@ namespace { + // + // + struct option_init: traversal::option, context + { + option_init (context& c) : context (c), comma_ (false) {} + virtual void + traverse (type& o) + { + if (comma_) + os << "," << endl + << " "; + else + comma_ = true; + + os << emember (o); + + if (o.initialized_p ()) + { + using semantics::expression; + expression const& i (o.initializer ()); + + switch (i.type ()) + { + case expression::string_lit: + case expression::char_lit: + case expression::bool_lit: + case expression::int_lit: + case expression::float_lit: + case expression::identifier: + { + os << " (" << i.value () << ")"; + break; + } + case expression::call_expr: + { + os << " " << i.value (); + break; + } + } + } + else + os << " ()"; + } + + private: + bool comma_; + }; + + // + // + struct option_map: traversal::option, context + { + option_map (context& c) : context (c) {} + + virtual void + traverse (type& o) + { + string name (ename (o)); + string type (o.type ().name ()); + string scope (escape (o.scope ().name ())); + string map ("_cli_" + scope + "_map_"); + + os << "_cli_" << scope << "_map_[\"" << o.name () << "\"] = " << endl + << "&::cli::thunk<" << scope << ", " << type << ", " << + "&" << scope << "::" << emember (o) << ">;"; + } + }; + + // + // + struct class_: traversal::class_, context + { + class_ (context& c) + : context (c), option_map_ (c) + { + names_option_map_ >> option_map_; + } + + virtual void + traverse (type& c) + { + string name (escape (c.name ())); + + os << "// " << name << endl + << "//" << endl + << endl; + + // c-tors + // + string um ("::cli::unknown_mode"); + + os << name << "::" << endl + << name << " (int argc," << endl + << "char** argv," << endl + << um << " opt," << endl + << um << " arg)" << endl + << ": "; + { + option_init init (*this); + traversal::names names_init (init); + names (c, names_init); + } + os << "{" + << "_parse (1, argc, argv, opt, arg);" + << "}"; + + os << name << "::" << endl + << name << " (int start," << endl + << "int argc," << endl + << "char** argv," << endl + << um << " opt," << endl + << um << " arg)" << endl + << ": "; + { + option_init init (*this); + traversal::names names_init (init); + names (c, names_init); + } + os << "{" + << "_parse (start, argc, argv, opt, arg);" + << "}"; + + os << name << "::" << endl + << name << " (int argc," << endl + << "char** argv," << endl + << "int& end," << endl + << um << " opt," << endl + << um << " arg)" << endl + << ": "; + { + option_init init (*this); + traversal::names names_init (init); + names (c, names_init); + } + os << "{" + << "end = _parse (1, argc, argv, opt, arg);" + << "}"; + + os << name << "::" << endl + << name << " (int start," << endl + << "int argc," << endl + << "char** argv," << endl + << "int& end," << endl + << um << " opt," << endl + << um << " arg)" << endl + << ": "; + { + option_init init (*this); + traversal::names names_init (init); + names (c, names_init); + } + os << "{" + << "end = _parse (start, argc, argv, opt, arg);" + << "}"; + + // _parse() + // + string map ("_cli_" + name + "_map"); + + os << "typedef" << endl + << "std::map" << endl + << map << ";" + << endl + << "static " << map << " " << map << "_;" + << endl; + + os << "struct " << map << "_init" + << "{" + << map << "_init ()" + << "{"; + + names (c, names_option_map_); + + os << "}" + << "} " << map << "_init_;" + << endl; + + os << "int " << name << "::" << endl + << "_parse (int start," << endl + << "int argc," << endl + << "char** argv," << endl + << um << " opt_mode," << endl + << um << " arg_mode)" + << "{" + << "bool opt (true);" // Still recognizing options. + << endl + << "for (; start < argc;)" + << "{" + << "const char* s (argv[start]);" + << endl + << "if (std::strcmp (s, \"--\") == 0)" + << "{" + << "start++;" + << "opt = false;" + << "continue;" + << "}" + << map << "::const_iterator i (" << endl + << "opt ? " << map << "_.find (s) : " << map << "_.end ());" + << endl + << "if (i != " << map << "_.end ())" + << "{" + << "start += (*(i->second)) (*this, argv + start, argc - start);" + << "}" + << "else if (opt && s[0] == '-' && s[1] != '\\0')" + << "{" + + // Unknown option. + // + << "switch (opt_mode)" + << "{" + << "case ::cli::unknown_mode::skip:" << endl + << "{" + << "start++;" + << "continue;" + << "}" + << "case ::cli::unknown_mode::stop:" << endl + << "{" + << "break;" + << "}" + << "case ::cli::unknown_mode::fail:" << endl + << "{" + << "throw ::cli::unknown_option (s);" + << "}" + << "}" // switch + << "break;" // The stop case. + << "}" + << "else" + << "{" + + // Unknown argument. + // + << "switch (arg_mode)" + << "{" + << "case ::cli::unknown_mode::skip:" << endl + << "{" + << "start++;" + << "continue;" + << "}" + << "case ::cli::unknown_mode::stop:" << endl + << "{" + << "break;" + << "}" + << "case ::cli::unknown_mode::fail:" << endl + << "{" + << "throw ::cli::unknown_argument (s);" + << "}" + << "}" // switch + << "break;" // The stop case. + << "}" + + << "}" // for + << "return start;" + << "}"; + } + + private: + option_map option_map_; + traversal::names names_option_map_; + }; } void generate_source (context& ctx) { + ctx.os << "#include " << endl + << "#include " << endl + << endl; + traversal::cli_unit unit; traversal::names unit_names; namespace_ ns (ctx); + class_ cl (ctx); + + unit >> unit_names >> ns; + unit_names >> cl; + traversal::names ns_names; - unit >> unit_names >> ns >> ns_names >> ns; + ns >> ns_names >> ns; + ns_names >> cl; unit.dispatch (ctx.unit); } -- cgit v1.1