From 780a199410056c9589f5868a836f565b452fb14e Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sun, 18 Oct 2009 15:18:15 +0200 Subject: Write getting started guide --- doc/guide/guide.html2ps | 63 ++++ doc/guide/index.xhtml | 921 ++++++++++++++++++++++++++++++++++++++++++++++++ doc/guide/makefile | 40 +++ 3 files changed, 1024 insertions(+) create mode 100644 doc/guide/guide.html2ps create mode 100644 doc/guide/index.xhtml create mode 100644 doc/guide/makefile (limited to 'doc/guide') diff --git a/doc/guide/guide.html2ps b/doc/guide/guide.html2ps new file mode 100644 index 0000000..74005f7 --- /dev/null +++ b/doc/guide/guide.html2ps @@ -0,0 +1,63 @@ +@html2ps { + option { + toc: hb; + colour: 1; + hyphenate: 1; + titlepage: 1; + } + + datefmt: "%B %Y"; + + titlepage { + content: " +
+

CLI Language

+

Getting Started Guide

+

 

+

 

+

 

+

 

+

 

+

 

+
+

Copyright © 2009 Code Synthesis Tools CC

+ +

Permission is granted to copy, distribute and/or modify this document + under the terms of the + MIT License. +

+ +

This document is available in the following formats: + XHTML, + PDF, and + PostScript.

"; + } + + toc { + indent: 2em; + } + + header { + odd-right: $H; + even-left: $H; + } + + footer { + odd-left: $D; + odd-center: $T; + odd-right: $N; + + even-left: $N; + even-center: $T; + even-right: $D; + } +} + +body { + font-size: 12pt; + text-align: justify; +} + +pre { + font-size: 10pt; +} diff --git a/doc/guide/index.xhtml b/doc/guide/index.xhtml new file mode 100644 index 0000000..b021cbf --- /dev/null +++ b/doc/guide/index.xhtml @@ -0,0 +1,921 @@ + + + + + CLI Language Getting Started Guide + + + + + + + + + + + + + +
+
+ +
+ +
+
CLI Language
+
Getting Started Guide
+ +

Copyright © 2009 Code Synthesis Tools CC

+ +

Permission is granted to copy, distribute and/or modify this document + under the terms of the + MIT License. +

+ +

This document is available in the following formats: + XHTML, + PDF, and + PostScript.

+ +
+ +

Table of Contents

+ + + + + + + + + + + + + +
1Introduction
2Hello World Example + + + + + +
2.1Defining Command Line Interface
2.2Translating CLI Definitions to C++
2.3Implementing Application Logic
2.4Compiling and Running
+
3CLI Language + + + + + +
3.1Options Class Definition
3.2Option Definition
3.3Include Directive
3.4Namespace Definition
+
+
+ + + +

1 Introduction

+ +

Command Line Interface (CLI) definition language is a domain-specific + language (DSL) that was designed for specifying command line interfaces + of C++ programs. CLI defintions are translated to C++ classes using the + CLI compiler. These classes implement parsing of the command line + arguments and provide a convenient interface for accessing the + extracted data.

+ +

Beyond this guide, you may also find the following sources of + information useful:

+ +
    +
  • CLI + Compiler Command Line Manual
  • + +
  • The INSTALL file in the CLI distribution provides build + instructions for various platforms.
  • + +
  • The examples/ directory in the CLI distribution contains + a collection of examples and a README file with an overview of each + example.
  • + +
  • The cli-users + mailing list is the place to ask technical questions about the CLI language + and compiler. Furthermore, the + cli-users mailing + list archives may already have answers to some of your questions.
  • +
+ + + + + +

2 Hello World Example

+ +

In this chapter we will examine how to define a very simple command + line interface in CLI, translate this interface to C++, and use the + result in our application. The code presented in this chapter is based + on the hello example which can be found in the + examples/hello/ directory of the CLI distribution.

+ +

2.1 Defining Command Line Interface

+ +

Our hello application is going to print a greeting + line for each name supplied on the command line. It will also + support a number of command line options that can be used to + customize the greeting line. These options are: --greeting + and --exclamations. The --greeting + option allows us to specify the greeting pharse instead of the + default "Hello". The --exclamations + option is used to specify how many exclamations marks should + be prined at the end of each greeting. We will also support + the --help option which triggers printing of the + usage information.

+ +

Now we can write a description of the above command line interface + in the CLI language and save it into hello.cli:

+ +
+include <string>;
+
+class options
+{
+  bool --help;
+  std::string --greeting = "Hello";
+  unsigned int --exclamations = 1;
+};
+  
+ +

While some details in the above code fragment might not be completely + clear (the CLI language is covered in greater detail in the next + chapter), it should be easy to connect declarations in + hello.cli to the command line interface described in + the preceeding paragrpahs. The next step is to translate this + interface specification to C++.

+ +

2.2 Translating CLI Defintions to C++

+ +

Now we are ready to translate our hello.cli to C++. + To do this we invoke the CLI compiler from a terminal (UNIX) or + a command prompt (Windows): +

+ +
+$ cli hello.cli
+  
+ +

This invocation of the CLI compiler produces three C++ files: + hello.hxx hello.ixx, and + hello.cxx. You can change the extensions in these + files with the compiler command line options. See the + CLI + Compiler Command Line Manual for more information.

+ +

The following code fragment is taken from hello.hxx; it + should give you an idea about what gets generated:

+ +
+#include <string>
+
+class options
+{
+public:
+  options (int argc, char** argv);
+  options (int argc, char** argv, int& end);
+
+  // Option accessors.
+  //
+public:
+  bool
+  help () const;
+
+  const std::string&
+  greeting () const;
+
+  unsigned int
+  exclamations () const;
+
+private:
+  ..
+};
+  
+ +

The options C++ class corresponds to the options + CLI class. For each option in this CLI class an accessor function is + generated inside the C++ class. The options C++ class also + defines a number of overloaded constructs that we can use to parse the + argc/argv array. Let's now see how we can use this genearted + class to implement option parsing in our hello application.

+ +

2.3 Implementing Application Logic

+ +

At this point we have all the parts we need to implement our + application:

+ +
+#include <iostream>
+#include "hello.hxx"
+
+using namespace std;
+
+void
+usage ()
+{
+  cerr << "usage: driver <options> <names>" << endl
+       << "  [--help]" << endl
+       << "  [--greeting <string>]" << endl
+       << "  [--exclamations <integer>]" << endl;
+}
+
+int
+main (int argc, char* argv[])
+{
+  try
+  {
+    int end; // End of options.
+    options o (argc, argv, end);
+
+    if (o.help ())
+    {
+      usage ();
+      return 0;
+    }
+
+    if (end == argc)
+    {
+      cerr << "no names provided" << endl;
+      usage ();
+      return 1;
+    }
+
+    // Print the greetings.
+    //
+    for (int i = end; i < argc; i++)
+    {
+      cout << o.greeting () << ", " << argv[i];
+
+      for (unsigned int j = 0; j < o.exclamations (); j++)
+        cout << '!';
+
+      cout << endl;
+    }
+  }
+  catch (const cli::exception& e)
+  {
+    cerr << e << endl;
+    usage ();
+    return 1;
+  }
+}
+
+ +

At the beginning of our application we create the options + object which parses the command line. The end variable + contains the index of the first non-option argument. Then we access + the option values as needed during the application execution. We also + catch and print the cli::exception exception in case + something goes wrong (for example, an unknown option is specified + or an option value is invalid). +

+ +

2.4 Compiling and Running

+ +

After saving our application from the previous section in + driver.cxx, we are ready to build and run our program. + On UNIX this can be done with the following commands:

+ +
+$ c++ -o driver driver.cxx hello.cxx
+
+$ ./driver world
+Hello, world!
+
+$ ./driver --greeting Hi --exclamations 3 John Jane
+Hi, John!!!
+Hi, Jane!!!
+  
+ +

We can also test the error handling:

+ +
+$ ./driver -n 3 Jane
+unknown option '-n'
+usage: driver <options> <names>
+  [--help]
+  [--greeting <string>]
+  [--exclamations <integer>]
+
+$ ./driver --exclamations abc Jane
+invalid value 'abc' for option '--exclamations'
+usage: driver <options> <names>
+  [--help]
+  [--greeting <string>]
+  [--exclamations <integer>]
+  
+ + + + + +

3 CLI Language

+ +

This chapter describes the CLI language and its mapping to C++. + A CLI definition unit consists of zero or more Include + Directives followed by one or more Namespace Definition + or Option Class Definition. C and C++-style comments + can be used anywhere in the CLI definition unit except in character and + string literals.

+ +

3.1 Option Class Definition

+ +

The central part of the CLI language is option class. An + option class contains one or more option definition, for + example:

+ +
+class options
+{
+  bool --help;
+  int --compression;
+};
+  
+ +

If we translate the above CLI fragment to C++, we will get a C++ + class with the following interface:

+ +
+class options
+{
+public:
+  options (int argc,
+           char** argv,
+           cli::unknown_mode opt_mode = cli::unknown_mode::fail,
+           cli::unknown_mode arg_mode = cli::unknown_mode::stop);
+
+  options (int start,
+           int argc,
+           char** argv,
+           cli::unknown_mode opt_mode = cli::unknown_mode::fail,
+           cli::unknown_mode arg_mode = cli::unknown_mode::stop);
+
+  options (int argc,
+           char** argv,
+           int& end,
+           cli::unknown_mode opt_mode = cli::unknown_mode::fail,
+           cli::unknown_mode arg_mode = cli::unknown_mode::stop);
+
+  options (int start,
+           int argc,
+           char** argv,
+           int& end,
+           cli::unknown_mode opt_mode = cli::unknown_mode::fail,
+           cli::unknown_mode arg_mode = cli::unknown_mode::stop);
+
+  options (const options&);
+
+  options&
+  operator= (const options&);
+
+public:
+  bool
+  help () const;
+
+  int
+  compression () const;
+};
+  
+ + +

An option class is mapped to a C++ class with the same name. The + C++ class defines a set of public overloaded constructors, public + copy constructor and assignment operator, as well as a set of public + accessor functions corresponding to option definitions.

+ +

The argc/argv arguments in the overloaded constructors + are used to pass the command line arguments array, normally as passed + to mail(). The start argument is used to + specify the position in the arguments array from which the parsing + should start. Constructor that don't have this argument start from + position 1, skipping the executable name in argv[0]. + The end argument is used to return the position in + the arguments array where parsing of options stopped. This is the + position of the first program argument, if any.

+ +

The opt_mode and arg_mode arguments + specify the parser behavior when it encounters an unknown option + and argument, respectively. The unknown_mode type + is part of the generated CLI runtime support code. It has the + following interface:

+ +
+namespace cli
+{
+  class unknown_mode
+  {
+  public:
+    enum value
+    {
+      skip,
+      stop,
+      fail
+    };
+
+    unknown_mode (value v);
+    operator value () const;
+  };
+}
+  
+ +

If the mode is skip, the parser skips an unknown + option or argument and continue parsing. If the mode is + stop, the parser stops the parsing process. The + position of the unknown entitiy is stored in the end + argument. If the mode is fail, the parser throws the + cli::unknown_option or cli::unknown_argument + exception (described blow) on encounertin an unknown option or argument, + respectively.

+ +

The parsing constructor (those with the argc/argv arguments) + can throw the following exceptions: cli::unknown_option, + cli::unknown_argument, cli::missing_value, and + cli::invalid_value. The first two exceptions are thrown + on encountering unknown options and arguments, respectively, as + described above. The missing_value exception is thrown when + an option value is missing. The invalid_value exception is + thrown when an option value is invalid, for example, a non-integer value + is speicified for an option of type int.

+ +

All CLI exceptions are derived from the common cli::exception + class which provides a polymorphic std::ostream insertion + support. For example, you can catch the cli::unknown_option + exception as cli::exception and print it to + std::cerr, you will get the error message corresponding + to the unknown_option exception.

+ +

The exceptions described above are part of the generated CLI runtime + support code and have the following interfaces:

+ +
+#include <exception>
+
+namespace cli
+{
+  class exception: public std::exception
+  {
+  public:
+    virtual void
+    print (std::ostream&) const = 0;
+  };
+
+  inline std::ostream&
+  operator<< (std::ostream& os, const exception& e)
+  {
+    os << e;
+    return os;
+  }
+
+  class unknown_option: public exception
+  {
+  public:
+    unknown_option (const std::string& option);
+
+    const std::string&
+    option () const;
+
+    virtual void
+    print (std::ostream&) const;
+
+    virtual const char*
+    what () const throw ();
+  };
+
+  class unknown_argument: public exception
+  {
+  public:
+    unknown_argument (const std::string& argument);
+
+    const std::string&
+    argument () const;
+
+    virtual void
+    print (std::ostream&) const;
+
+    virtual const char*
+    what () const throw ();
+  };
+
+  class missing_value: public exception
+  {
+  public:
+    missing_value (const std::string& option);
+
+    const std::string&
+    option () const;
+
+    virtual void
+    print (std::ostream&) const;
+
+    virtual const char*
+    what () const throw ();
+  };
+
+  class invalid_value: public exception
+  {
+  public:
+    invalid_value (const std::string& option,
+                   const std::string& value);
+
+    const std::string&
+    option () const;
+
+    const std::string&
+    value () const;
+
+    virtual void
+    print (std::ostream&) const;
+
+    virtual const char*
+    what () const throw ();
+  };
+}
+  
+ + +

3.2 Option Definition

+ +

An option definition consists of there components: type, + name, and default value. Option type can be any + C++ type as long as its string representation can be extracted using + the std::istream interface. If the option type is + user-define then you will need to include its declaration using + the Include Directive.

+ +

An option of any type other that bool is expected to + have a value. An option of type bool is treated as + a flag and does not have a value. That is, a mere presence of such + an option on the command line sets this option value to + true.

+ +

The name component specifies the option name as it will be entered + in the command line. A name can contain any number of aliases seperated + by |. The C++ acessor function name is derived from the + first name by removing any leading special characters, such as + -, /, etc., and replacing special characters + in other places with underscore. For example, the following option + definition:

+ +
+class options
+{
+  int --compression-level | --comp | -c;
+};
+  
+ +

Will result in the following accessor function:

+ +
+class options
+{
+  int
+  compression_level () const;
+};
+  
+ +

While any option alias can be used on the command line to specify + this option value.

+ +

If the option name conflicts with one of the CLI language keywords, + it can be specified as a string literal:

+ +
+class options
+{
+  bool "int";
+};
+  
+ +

The final component of the option definition is the optional default + value. If the default value is not specified then the option is + initialized with the default constructor. In particular, this means + that a bool option will be initialized to false, + an int option will be initialized to 0, etc.

+ +

Similar to C++ variable initialization, the default option value + can be specified using two syntactic forms: an assignment initialization + and constructor initialization. The two forms are equivalent except + that the constructor initialization can be used with multiple arguments, + for example:

+ +
+include <string>;
+
+class options
+{
+  int -i1 = 5;
+  int -i2 (5);
+
+  std::string -s1 = "John";
+  std::string -s2 ("Mr John Doe", 8, 3);
+};
+  
+ +

The assignment initialization supports character, string, boolean, and + simple integer literals (including negative integers) as well + as identifiers. For more complex expression use the constructor + initialization or wrap the expression in parenthesis, for example:

+ +
+include "constants.hxx"; // Defines default_value.
+
+class options
+{
+  int -a = default_value;
+  int -b (25 * 4);
+  int -c = (25 / default_value + 3);
+};
+  
+ +

By default when an option is specified two or more times on the command + line, the last value overrides all the previous ones. However, a number + of standard C++ containers are handled differently to allow collecting + multiple option values or building key-value maps. These + containers are std::vector, std::set and + std::map.

+ +

When std::vector or std::set is specified + as an option type, all values for this option are inserted into the + container in the order they are encountered. As a result, + std::vector will conatin all the values, inclduing + duplicates while std::set will contain all the unique + values. For example:

+ +
+include <set>;
+include <vector>;
+
+class options
+{
+  std::vector<int> --vector | -v;
+  std::set<int> --set | -s;
+};
+  
+ +

If we have a command line like this: + -v 1 -v 2 -v 1 -s 1 -s 2 -s 1, then the vector returned + by the vector() accessor function will contain three + elements: 1, 2, and 1 while + the set returned by the set() accessor will contain + two elements: 1 and 2.

+ +

When std::map is specified as an option type, the option + value is expected to have two parts: the key and the value, separated + by =. All the option values are then parsed into key/value + pairs and inserted into the map. For example:

+ +
+include <map>;
+include <string>;
+
+class options
+{
+  std::map<std::string, std::string> --map | -m;
+};
+  
+ +

The possible option values for this interface are: -m a=A, + -m =B (key is an empty string), -m c= (value + is an empty string), or -m d (same as -m d=).

+ +

3.3 Include Directive

+ +

If you are using user-defined types in your option definitions, + you will need to include their declarations with the include + directive. Include directives can specify < > or + " "-enclosed paths. The CLI compiler does not + actully open or read these files. Instead the include directives + are translated to C++ preprocessor #include directive + in the generated C++ header file. For example, the following CLI + definition:

+ +
+include <string>;
+include "types.hxx"; // Defines the name class.
+
+class options
+{
+  std::string --string;
+  name --name;
+};
+  
+ +

Will result in the following C++ header file:

+ +
+#include <string>
+#include "types.hxx"
+
+class options
+{
+  ...
+
+  const std::string&
+  string () const;
+
+  const name&
+  name () const;
+
+  ...
+};
+  
+ +

Without the #include directives the std::string + and name type in the options class would be + undeclared and result in compillation errors.

+ +

3.4 Namespace Definition

+ +

Option classes can be placed into namespaces which are translated + directly to C++ namespaces. For example:

+ +
+namespace compiler
+{
+  namespace lexer
+  {
+    class options
+    {
+      int --warning-level = 0;
+    };
+  }
+
+  namespace parser
+  {
+    class options
+    {
+      int --warning-level = 0;
+    };
+  }
+
+  namespace generator
+  {
+    class options
+    {
+      int --target-width = 32;
+    };
+  }
+}
+  
+ +

The above CLI namespace strcture would result in the equivalent C++ + namespaces strcture:

+ +
+namespace compiler
+{
+  namespace lexer
+  {
+    class options
+    {
+      int
+      warning_level () const;
+    };
+  }
+
+  namespace parser
+  {
+    class options
+    {
+      int
+      warning_level () const;
+    };
+  }
+
+  namespace generator
+  {
+    class options
+    {
+      int
+      target_width () const;
+    };
+  }
+}
+  
+ + +
+
+ + + + diff --git a/doc/guide/makefile b/doc/guide/makefile new file mode 100644 index 0000000..405c51d --- /dev/null +++ b/doc/guide/makefile @@ -0,0 +1,40 @@ +# file : doc/guide/makefile +# author : Boris Kolpackov +# copyright : Copyright (c) 2009 Code Synthesis Tools CC +# license : MIT; see accompanying LICENSE file + +include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make + +default := $(out_base)/ +install := $(out_base)/.install +cleandoc := $(out_base)/.cleandoc + +# Build. +# +$(default): $(out_base)/cli-guide.ps $(out_base)/cli-guide.pdf + + +$(out_base)/cli-guide.ps: $(src_base)/index.xhtml \ + $(src_base)/guide.html2ps \ + | $(out_base)/. + $(call message,html2ps $<,html2ps -f $(src_base)/guide.html2ps -o $@ $<) + +$(out_base)/cli-guide.pdf: $(out_base)/cli-guide.ps | $(out_base)/. + $(call message,ps2pdf $<,ps2pdf14 $< $@) + +# Install. +# +$(install): $(out_base)/cli-guide.ps $(out_base)/cli-guide.pdf + $(call install-data,$(src_base)/index.xhtml,$(install_doc_dir)/cli/guide/index.xhtml) + $(call install-data,$(src_base)/cli-guide.ps,$(install_doc_dir)/cli/guide/cli-guide.ps) + $(call install-data,$(src_base)/cli-guide.pdf,$(install_doc_dir)/cli/guide/cli-guide.pdf) + +# Clean. +# +$(cleandoc): + $(call message,rm $$1,rm -f $$1,$(out_base)/cli-guide.ps) + $(call message,rm $$1,rm -f $$1,$(out_base)/cli-guide.pdf) + +# How to. +# +$(call include,$(bld_root)/install.make) -- cgit v1.1