summaryrefslogtreecommitdiff
path: root/cli/runtime-source.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2009-12-10 09:47:29 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2009-12-10 09:47:29 +0200
commitf8edfd22cb45b554a573d2722900196758e9e958 (patch)
treeb2c800c8793f08be287a67ed72517f9cc2831fda /cli/runtime-source.cxx
parenteddefea6ea39e64e9eb5adf74a279a230a63cf5b (diff)
Scanner-based parsing with support for element erasing
Also implement argv_file_scanner which provides support for reading command line arguments from the argv array as well as files specified with command line options. New examples: file. New tests: ctor, erase, file.
Diffstat (limited to 'cli/runtime-source.cxx')
-rw-r--r--cli/runtime-source.cxx228
1 files changed, 223 insertions, 5 deletions
diff --git a/cli/runtime-source.cxx b/cli/runtime-source.cxx
index 3003b6b..323b604 100644
--- a/cli/runtime-source.cxx
+++ b/cli/runtime-source.cxx
@@ -17,8 +17,13 @@ generate_runtime_source (context& ctx)
<< "#include <string>" << endl
<< "#include <vector>" << endl
<< "#include <ostream>" << endl
- << "#include <sstream>" << endl
- << endl;
+ << "#include <sstream>" << endl;
+
+ if (ctx.options.generate_file_scanner ())
+ os << "#include <cstring>" << endl
+ << "#include <fstream>" << endl;
+
+ os << endl;
os << "namespace cli"
<< "{";
@@ -31,11 +36,13 @@ generate_runtime_source (context& ctx)
<< "~unknown_option () throw ()"
<< "{"
<< "}"
+
<< "void unknown_option::" << endl
<< "print (std::ostream& os) const"
<< "{"
<< "os << \"unknown option '\" << option () << \"'\";"
<< "}"
+
<< "const char* unknown_option::" << endl
<< "what () const throw ()"
<< "{"
@@ -50,11 +57,13 @@ generate_runtime_source (context& ctx)
<< "~unknown_argument () throw ()"
<< "{"
<< "}"
+
<< "void unknown_argument::" << endl
<< "print (std::ostream& os) const"
<< "{"
<< "os << \"unknown argument '\" << argument () << \"'\";"
<< "}"
+
<< "const char* unknown_argument::" << endl
<< "what () const throw ()"
<< "{"
@@ -69,11 +78,13 @@ generate_runtime_source (context& ctx)
<< "~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 ()"
<< "{"
@@ -88,12 +99,14 @@ generate_runtime_source (context& ctx)
<< "~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 ()"
<< "{"
@@ -109,12 +122,37 @@ generate_runtime_source (context& ctx)
<< "{"
<< "os << what ();"
<< "}"
+
<< "const char* eos_reached::" << endl
<< "what () const throw ()"
<< "{"
<< "return \"end of argument stream reached\";"
<< "}";
+ // file_io_failure
+ //
+ if (ctx.options.generate_file_scanner ())
+ {
+ 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\";"
+ << "}";
+ }
+
// scanner
//
os << "// scanner" << endl
@@ -147,8 +185,23 @@ generate_runtime_source (context& ctx)
<< "const char* argv_scanner::" << endl
<< "next ()"
<< "{"
- << "if (i_ < argc_)" << endl
- << "return argv_[i_++];"
+ << "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 ();"
<< "}"
@@ -157,11 +210,176 @@ generate_runtime_source (context& ctx)
<< "skip ()"
<< "{"
<< "if (i_ < argc_)" << endl
- << "i_++;"
+ << "++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 ());"
+ << endl
+ << "if (" << (sep ? "!skip_ && " : "") << "a == option_)"
+ << "{"
+ << "base::next ();"
+ << endl
+ << "if (!base::more ())" << endl
+ << "throw missing_value (option_);"
+ << 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 ();"
+ << "}"
+
+ << "void argv_file_scanner::" << endl
+ << "load (const char* file)"
+ << "{"
+ << "using namespace std;"
+ << endl
+ << "ifstream is (file);"
+ << 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 (" << (sep ? "!skip_ && " : "") << "s1 == option_)" << endl
+ << "load (s2.c_str ());"
+ << "else"
+ << "{"
+ << "args_.push_back (s1);"
+ << "args_.push_back (s2);"
+ << "}"
+ << "}"
+ << "}" // while
+ << "}";
+ }
+
// parser class template & its specializations
//
os << "template <typename X>" << endl