From 720c5a33b6a49cf328fdd7611f49153cf8f60247 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 8 Apr 2020 14:51:57 +0300 Subject: Separate tests and examples into individual packages Also make cli module to be explicitly enabled via the config.cli configuration variable. --- cli/doc/.gitignore | 2 + cli/doc/buildfile | 20 + cli/doc/cli-epilogue.1 | 21 + cli/doc/cli-epilogue.xhtml | 24 + cli/doc/cli-prologue.1 | 59 ++ cli/doc/cli-prologue.xhtml | 72 +++ cli/doc/default.css | 322 +++++++++++ cli/doc/doc.sh | 78 +++ cli/doc/guide/.gitignore | 2 + cli/doc/guide/guide.html2ps | 63 ++ cli/doc/guide/index.xhtml | 1336 +++++++++++++++++++++++++++++++++++++++++++ cli/doc/language.txt | 128 +++++ 12 files changed, 2127 insertions(+) create mode 100644 cli/doc/.gitignore create mode 100644 cli/doc/buildfile create mode 100644 cli/doc/cli-epilogue.1 create mode 100644 cli/doc/cli-epilogue.xhtml create mode 100644 cli/doc/cli-prologue.1 create mode 100644 cli/doc/cli-prologue.xhtml create mode 100644 cli/doc/default.css create mode 100755 cli/doc/doc.sh create mode 100644 cli/doc/guide/.gitignore create mode 100644 cli/doc/guide/guide.html2ps create mode 100644 cli/doc/guide/index.xhtml create mode 100644 cli/doc/language.txt (limited to 'cli/doc') diff --git a/cli/doc/.gitignore b/cli/doc/.gitignore new file mode 100644 index 0000000..562ecbd --- /dev/null +++ b/cli/doc/.gitignore @@ -0,0 +1,2 @@ +cli.xhtml +cli.1 diff --git a/cli/doc/buildfile b/cli/doc/buildfile new file mode 100644 index 0000000..f47adad --- /dev/null +++ b/cli/doc/buildfile @@ -0,0 +1,20 @@ +# file : doc/buildfile +# license : MIT; see accompanying LICENSE file + +define css: doc +css{*}: extension = css + +define xhtml: doc +xhtml{*}: extension = xhtml + +./: {man1 xhtml}{cli} \ + css{default} \ + file{cli-*} + +./: guide/doc{cli-guide*} \ + guide/xhtml{index} \ + guide/file{*.html2ps} + +doc{*}: install.subdirs = true + +./: file{doc.sh} diff --git a/cli/doc/cli-epilogue.1 b/cli/doc/cli-epilogue.1 new file mode 100644 index 0000000..5e2ffae --- /dev/null +++ b/cli/doc/cli-epilogue.1 @@ -0,0 +1,21 @@ +.\" +.\" DIAGNOSTICS +.\" +.SH DIAGNOSTICS +If the input file is not a valid CLI definition, +.B cli +will issue diagnostic messages to STDERR and exit with non-zero exit code. +.\" +.\" BUGS +.\" +.SH BUGS +Send bug reports to the cli-users@codesynthesis.com mailing list. +.\" +.\" COPYRIGHT +.\" +.SH COPYRIGHT +Copyright (c) $copyright$. + +Permission is granted to copy, distribute and/or modify this document under +the terms of the MIT License. Copy of this license can be obtained from +http://www.codesynthesis.com/licenses/mit.txt diff --git a/cli/doc/cli-epilogue.xhtml b/cli/doc/cli-epilogue.xhtml new file mode 100644 index 0000000..8a70d81 --- /dev/null +++ b/cli/doc/cli-epilogue.xhtml @@ -0,0 +1,24 @@ +

DIAGNOSTICS

+ +

If the input file is not a valid CLI definition, cli + will issue diagnostic messages to STDERR and exit with non-zero exit + code.

+ +

BUGS

+ +

Send bug reports to the + cli-users@codesynthesis.com mailing list.

+ + + + + + diff --git a/cli/doc/cli-prologue.1 b/cli/doc/cli-prologue.1 new file mode 100644 index 0000000..165cd1a --- /dev/null +++ b/cli/doc/cli-prologue.1 @@ -0,0 +1,59 @@ +.\" Process this file with +.\" groff -man -Tascii cli.1 +.\" +.TH CLI 1 "December 2009" "CLI 1.2.0" +.SH NAME +cli \- command line interface compiler for C++ +.\" +.\" +.\" +.\"-------------------------------------------------------------------- +.SH SYNOPSIS +.\"-------------------------------------------------------------------- +.B cli +.B [ +.I options +.B ] +.I file +.\" +.\" +.\" +.\"-------------------------------------------------------------------- +.SH DESCRIPTION +.\"-------------------------------------------------------------------- +.B cli +generates C++ implementation and documentation in various formats for a +command line interface defined in the CLI language. For an input file in +the form +.B name.cli +the following is generated. By default or if the +.B --generate-cxx +option is specified, the following C++ files are generated: +.B name.hxx +(header file), +.B name.ixx +(inline file, generated unless the +.B --suppress-inline +option is specified), and +.B name.cxx (source file). +If the +.B --generate-html +option is specified, then the +.B name.html +HTML documentation file is generated. If the +.B --generate-man +option is specified, then the +.B name.1 +man page file is generated. When +.B --generate-html +or +.B --generate-man +is specified, the +.B --stdout +option can be used to redirect the output to STDOUT instead of a file. +.\" +.\" +.\" +.\"-------------------------------------------------------------------- +.SH OPTIONS +.\"-------------------------------------------------------------------- diff --git a/cli/doc/cli-prologue.xhtml b/cli/doc/cli-prologue.xhtml new file mode 100644 index 0000000..9a57f0e --- /dev/null +++ b/cli/doc/cli-prologue.xhtml @@ -0,0 +1,72 @@ + + + + + CLI 1.2.0 Compiler Command Line Manual + + + + + + + + + + + +
+
+ +

NAME

+ +

cli - command line interface compiler for C++

+ +

SYNOPSIS

+ +
+
cli [options] file
+
+ +

DESCRIPTION

+ +

cli generates C++ implementation and + documentation in various formats for a command line interface + defined in the CLI language. For an input file in the form + name.cli the following is generated. By + default or if the --generate-cxx option is + specified, the following C++ files are generated: + name.hxx (header file), name.ixx + (inline file, generated unless the --suppress-inline + option is specified), and name.cxx (source file). + If the --generate-html option is specified, then + the name.html HTML documentation file is generated. + If the --generate-man option is specified, then + the name.1 man page file is generated. When + --generate-html or --generate-man + is specified, the --stdout option can be used to + redirect the output to STDOUT instead of a file.

+ +

OPTIONS

diff --git a/cli/doc/default.css b/cli/doc/default.css new file mode 100644 index 0000000..c73bb8d --- /dev/null +++ b/cli/doc/default.css @@ -0,0 +1,322 @@ +html { + margin : 0; + padding : 0; + background : white; +} + +body { + font-family : "Lucida Grande", Verdana, "Bitstream Vera Sans", sans-serif; + font-weight : normal; + font-size : 13px; + line-height : 19px; + + color : black; + + margin : 0 2em 0 2em; + padding : 0; +} + + +body { + min-width: 40em; +} + +#container { + max-width : 46em; + margin : 0 auto; + padding : 0 1em 0 1em; +} + + + +/* + * Footer + * + */ +#footer { + color : #3a84a7; + + padding : 1em 0 0.5em 0; + + font-size : 10px; + line-height : 15px; + + text-align: center; +} + +#footer a:link, #footer a:visited { + + color:#1d6699; + text-decoration: underline; +} + +#footer a { + margin-left: 0.7em; + margin-right: 0.7em; +} + +#footer p { + padding: 0; + margin: 0.3em 0 0 0; +} + +/* Distribution terms. */ +#footer #terms { + text-align: justify; + + font-size : 110%; + font-family : monospace; + + padding : 1em 0 0.5em 0; +} + + +/* + * Content + * + */ + +#content { + padding : 0em 0.1em 0 1.3em; + margin : 1.4em 0 0 0; +} + +#content p, +#content ol, +#content ul, +#content dl { + text-align: justify; +} + +#content h1 { + margin-left: -0.89em; +} + +a:link { + color:#0536d2; +} + + +/* + * Headings + * + */ + +h1, h2, h3, h4, h5, h6 { + font-weight : 500; +} + +h1 { font-size : 155%; } +h2 { font-size : 130%; } +h3 { font-size : 125%; } +h4 { font-size : 110%; } +h5 { font-size : 106%; } +h6 { font-size : 100%; } + +h1 { margin : 1.8em 0 0.8em 0;} +h2 { margin-top : 1.4em;} +h3 { margin-top : 1em;} + +p.indent { + margin-left : 1.5em; +} + + +/* + * Fix for IE 5.5 table font problem + * + */ + +table { + font-size : 13px; +} + + +/* + * table of content + * + */ + +ul.toc li { + padding : .4em 0em 0em 0em; +} + + +/* Toc links don't need to show when they are visited. */ +.toc a:visited { + color:#0536d2; +} + + +/* + * lists + * + */ + + +/* list of links */ +ul.menu { + list-style-type : none; +} + +ul.menu li { + padding-top : 0.3em; + padding-bottom : 0.3em; +} + + + +/* @@ I should probably use child selector here */ +/* list with multiline list-elements */ +ul.multiline li, ol.multiline li, dl.multiline dd { + padding-top : 0.16em; + padding-bottom : 0.16em; + + font-size : 11px; + line-height : 15px; +} + + +/* C++ code snippet */ +pre.cxx { + margin-top : 0em; + margin-bottom : 2em; + + margin-left : 1em; +} + +/* CLI code snippet */ +pre.cli { + margin-top : 0em; + margin-bottom : 2em; + + margin-left : 1em; +} + +/* make code snippet */ +pre.make { + margin-top : 0em; + margin-bottom : 2em; + + margin-left : 1em; +} + +/* terminal output */ +pre.term { + margin-top : 0em; + margin-bottom : 2em; + + margin-left : 1em; +} + + +/* Images */ +div.center { + text-align: center; +} + +/* Document info. */ +#docinfo { + margin-top: 4em; + border-top: 1px dashed #000000; + font-size: 70%; +} + + +/* Footnote */ + +#footnote { + margin-top : 2.5em; +} + +#footnote hr, hr.footnote { + margin-left: 0; + margin-bottom: 0.6em; + width: 8em; + border-top: 1px solid #000000; + border-right: none; + border-bottom: none; + border-left: none; + +} + +#footnote ol { + margin-left: 0; + padding-left: 1.45em; +} + +#footnote li { + text-align : left; + font-size : 11px; + line-height : 15px; + + padding : .4em 0 .4em 0; +} + + +/* Normal table with borders, etc. */ + +table.std { + margin: 2em 0 2em 0; + + border-collapse : collapse; + border : 1px solid; + border-color : #000000; + + font-size : 11px; + line-height : 14px; +} + +table.std th, table.std td { + border : 1px solid; + padding : 0.6em 0.8em 0.6em 0.8em; +} + +table.std th { + background : #cde8f6; +} + +table.std td { + text-align: left; +} + + +/* + * "item | description" table. + * + */ + +table.description { + border-style : none; + border-collapse : separate; + border-spacing : 0; + + font-size : 13px; + + margin : 0.6em 0 0.6em 0; + padding : 0 0 0 0; +} + +table.description tr { + padding : 0 0 0 0; + margin : 0 0 0 0; +} + +table.description * td, table.description * th { + border-style : none; + margin : 0 0 0 0; + vertical-align : top; +} + +table.description * th { + font-weight : normal; + padding : 0.4em 1em 0.4em 0; + text-align : left; + white-space : nowrap; + background : none; +} + +table.description * td { + padding : 0.4em 0 0.4em 1em; + text-align : justify; +} diff --git a/cli/doc/doc.sh b/cli/doc/doc.sh new file mode 100755 index 0000000..dde9aca --- /dev/null +++ b/cli/doc/doc.sh @@ -0,0 +1,78 @@ +#! /usr/bin/env bash + +version=1.2.0-b.6 + +trap 'exit 1' ERR +set -o errtrace # Trap in functions. + +function info () { echo "$*" 1>&2; } +function error () { info "$*"; exit 1; } + +date="$(date +"%B %Y")" +copyright="$(sed -n -re 's%^Copyright \(c\) (.+)\.$%\1%p' ../LICENSE)" + +while [ $# -gt 0 ]; do + case $1 in + --clean) + rm -f cli.xhtml cli.1 + rm -f guide/cli-guide.ps guide/cli-guide.pdf + exit 0 + ;; + *) + error "unexpected $1" + ;; + esac +done + +function compile () # +{ + local i=$1; shift + local o=$1; shift + + # Use a bash array to handle empty arguments. + # + local ops=() + while [ $# -gt 0 ]; do + ops=("${ops[@]}" "$1") + shift + done + + # --html-suffix .xhtml + ../cli/cli -I .. \ +-v project="cli" \ +-v version="$version" \ +-v date="$date" \ +-v copyright="$copyright" \ +"${ops[@]}" --generate-html --stdout \ +--html-prologue-file cli-prologue.xhtml \ +--html-epilogue-file cli-epilogue.xhtml \ +"../cli/$i.cli" >"$o.xhtml" + + # --man-suffix .1 + ../cli/cli -I .. \ +-v project="cli" \ +-v version="$version" \ +-v date="$date" \ +-v copyright="$copyright" \ +"${ops[@]}" --generate-man --stdout \ +--man-prologue-file cli-prologue.1 \ +--man-epilogue-file cli-epilogue.1 \ +"../cli/$i.cli" >"$o.1" +} + +compile options cli --suppress-undocumented + +# Manual. +# + +#function compile_doc () +#{ +# html2ps -f doc.html2ps:a4.html2ps -o "$n-a4.ps" "$n.xhtml" +# ps2pdf14 -sPAPERSIZE=a4 -dOptimize=true -dEmbedAllFonts=true "$n-a4.ps" "$n-a4.pdf" +# +# html2ps -f doc.html2ps:letter.html2ps -o "$n-letter.ps" "$n.xhtml" +# ps2pdf14 -sPAPERSIZE=letter -dOptimize=true -dEmbedAllFonts=true "$n-letter.ps" "$n-letter.pdf" +#} + +html2ps -f guide/guide.html2ps -o guide/cli-guide.ps guide/index.xhtml +ps2pdf14 -dOptimize=true -dEmbedAllFonts=true guide/cli-guide.ps guide/cli-guide.pdf diff --git a/cli/doc/guide/.gitignore b/cli/doc/guide/.gitignore new file mode 100644 index 0000000..239cc7f --- /dev/null +++ b/cli/doc/guide/.gitignore @@ -0,0 +1,2 @@ +*.ps +*.pdf diff --git a/cli/doc/guide/guide.html2ps b/cli/doc/guide/guide.html2ps new file mode 100644 index 0000000..a691002 --- /dev/null +++ b/cli/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-2020 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/cli/doc/guide/index.xhtml b/cli/doc/guide/index.xhtml new file mode 100644 index 0000000..675db03 --- /dev/null +++ b/cli/doc/guide/index.xhtml @@ -0,0 +1,1336 @@ + + + + + CLI Language Getting Started Guide + + + + + + + + + + + + + +
+
+ +
+ +
+
CLI Language
+
Getting Started Guide
+ +

Copyright © 2009-2020 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
2.5Adding Documentation
+
3CLI Language + + + + + + +
3.1Options Class Definition
3.2Option Definition
3.3Option Documentation
3.4Include Directive
3.5Namespace Definition
+
+
+ + + +

1 Introduction

+ +

Command Line Interface (CLI) definition language is a domain-specific + language (DSL) for defining command line interfaces of C++ programs. + CLI definitions are automatically translated to C++ classes using the + CLI compiler. These classes implement parsing of the command + line arguments and provide a convenient and type-safe 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 an 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 two command line options, --greeting + and --exclamations, that can be used to + customize the greeting line. The --greeting + option allows us to specify the greeting phrase instead of the + default "Hello". The --exclamations + option is used to specify how many exclamation marks should + be printed at the end of each greeting. We will also support + the --help option which triggers printing of the + usage information.

+ +

We can now 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 preceding paragraphs. The next step is to translate this + interface specification to C++.

+ +

2.2 Translating CLI Definitions to C++

+ +

Now we are ready to translate 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 file name extensions + for 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 generated + class to implement option parsing in our hello application.

+ +

2.3 Implementing Application Logic

+ +

At this point we have everything we need to implement our + application:

+ +
+#include <iostream>
+#include "hello.hxx"
+
+using namespace std;
+
+void
+usage (ostream& os)
+{
+  os << "usage: driver [options] <names>" << endl
+     << "options:" << endl;
+  options::print_usage (os);
+}
+
+int
+main (int argc, char* argv[])
+{
+  try
+  {
+    int end; // End of options.
+    options o (argc, argv, end);
+
+    if (o.help ())
+    {
+      usage (cout);
+      return 0;
+    }
+
+    if (end == argc)
+    {
+      cerr << "no names provided" << endl;
+      usage (cerr);
+      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 (cerr);
+    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. We then access + the option values as needed during the application execution. We also + catch and print cli::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>
+options:
+--help
+--greeting <arg>
+--exclamations <arg>
+
+$ ./driver --exclamations abc Jane
+invalid value 'abc' for option '--exclamations'
+usage: driver [options] <names>
+options:
+--help
+--greeting <arg>
+--exclamations <arg>
+  
+ +

2.5 Adding Documentation

+ +

As we have seen in the previous sections, the options + C++ class provides the print_usage() function which we + can use to display the application usage information. Right now this + information is very basic and does not include any description of + the purpose of each option:

+ +
+$ ./driver --help
+usage: driver [options] <names>
+options:
+--help
+--greeting <arg>
+--exclamations <arg>
+  
+ +

To make the usage information more descriptive we can document each + option in the command line interface definition. This information can + also be used to automatically generate program documentation in various + formats, such as HTML and man page. For example:

+ +
+include <string>;
+
+class options
+{
+  bool --help {"Print usage information and exit."};
+
+  std::string --greeting = "Hello"
+  {
+    "<text>",
+    "Use <text> as a greeting phrase instead of the default \"Hello\"."
+  };
+
+  unsigned int --exclamations = 1
+  {
+    "<num>",
+    "Print <num> exclamation marks instead of 1 by default."
+  };
+};
+  
+ +

If we now save this updated command line interface to + hello.cli and recompile our application, the usage + information printed by the program will look like this:

+ +
+usage: driver [options] <names>
+options:
+--help               Print usage information and exit.
+--greeting <text>    Use <text> as a greeting phrase instead of the
+                     default "Hello".
+--exclamations <num> Print <num> exclamation marks instead of 1 by
+                     default.
+  
+ +

We can also generate the program documentation in the HTML + (--generate-html CLI option) and man page + (--generate-man CLI option) formats. For example:

+ +
+$ cli --generate-html hello.cli
+  
+ +

The resulting hello.html file contains the following + documentation:

+ +
+
--help
+
Print usage information and exit.
+ +
--greeting text
+
Use text as a greeting phrase instead of the default "Hello".
+ +
--exclamations num
+
Print num exclamation marks instead of 1 by default.
+ +
+ +

This HTML fragment can be combined with custom prologue and epilogue + to create a complete program documentation + (--html-prologue/--html-epilogue options for the HTML + output, --man-prologue/--man-epilogue options for the + man page output). For an example of such complete documentation see + the CLI + Compiler Command Line Manual and the cli(1) man + page. For more information on the option documentation syntax, + see Section 3.3, Option Documentation.

+ + + + +

3 CLI Language

+ +

This chapter describes the CLI language and its mapping to C++. + A CLI file consists of zero or more Include + Directives followed by one or more Namespace Definitions + or Option Class Definitions. C and C++-style comments + can be used anywhere in the CLI file 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 definitions, 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,
+           bool erase = false,
+           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,
+           bool erase = false,
+           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,
+           bool erase = false,
+           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,
+           bool erase = false,
+           cli::unknown_mode opt_mode = cli::unknown_mode::fail,
+           cli::unknown_mode arg_mode = cli::unknown_mode::stop);
+
+  options (cli::scanner&,
+           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:
+  static void
+  print_usage (std::ostream&);
+
+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, a public + copy constructor and an assignment operator, as well as a set of public + accessor functions and, if the --generate-modifier CLI + compiler option is specified, modifier functions corresponding to option + definitions. It also defines a public static print_usage() + function that can be used to print the usage information for the options + defined by the class.

+ +

The argc/argv arguments in the overloaded constructors + are used to pass the command line arguments array, normally as passed + to main(). The start argument is used to + specify the position in the arguments array from which the parsing + should start. The constructors 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 the parsing of options stopped. This is the + position of the first program argument, if any. If the erase + argument is true, then the recognized options and their + values are removed from the argv array and the + argc count is updated accordingly.

+ +

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 entity 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 encountering an unknown option or argument, + respectively.

+ +

Instead of the argc/argv arguments, the last overloaded + constructor accepts the cli::scanner object. It is part + of the generated CLI runtime support code and has the following + abstract interface:

+ +
+namespace cli
+{
+  class scanner
+  {
+  public:
+    virtual bool
+    more () = 0;
+
+    virtual const char*
+    peek () = 0;
+
+    virtual const char*
+    next () = 0;
+
+    virtual void
+    skip () = 0;
+  };
+}
+  
+ +

The CLI runtime also provides two implementations of this interface: + cli::argv_scanner and cli::argv_file_scanner. + The first implementation is a simple scanner for the argv + array (it is used internally by all the other constructors) and has the + following interface:

+ +
+namespace cli
+{
+  class argv_scanner
+  {
+  public:
+    argv_scanner (int& argc, char** argv, bool erase = false);
+    argv_scanner (int start, int& argc, char** argv, bool erase = false);
+
+    int
+    end () const;
+
+    ...
+  };
+}
+  
+ +

The cli::argv_file_scanner implementation provides + support for reading command line arguments from the argv + array as well as files specified with command line options. It is + generated only if explicitly requested with the + --generate-file-scanner CLI compiler option and has + the following interface:

+ +
+namespace cli
+{
+  class argv_file_scanner
+  {
+  public:
+    argv_file_scanner (int& argc,
+                       char** argv,
+                       const std::string& option,
+                       bool erase = false);
+
+    argv_file_scanner (int start,
+                       int& argc,
+                       char** argv,
+                       const std::string& option,
+                       bool erase = false);
+
+    struct option_info
+    {
+      // If search_func is not NULL, it is called, with the arg
+      // value as the second argument, to locate the options file.
+      // If it returns an empty string, then the file is ignored.
+      //
+      const char* option;
+      std::string (*search_func) (const char*, void* arg);
+      void* arg;
+    };
+
+    argv_file_scanner (int& argc,
+                       char** argv,
+                       const option_info* options,
+                       std::size_t options_count,
+                       bool erase = false);
+
+    argv_file_scanner (int start,
+                       int& argc,
+                       char** argv,
+                       const option_info* options,
+                       std::size_t options_count,
+                       bool erase = false);
+    ...
+  };
+}
+  
+ +

The option argument in the first two constructors and + the options and options_count arguments + in the last two are used to pass the option name(s) that should be + recognized as specifying the file containing additional options. + Such a file contains a set of options, each appearing on a + separate line optionally followed by space and an option value. Empty lines + and lines starting with # are ignored. Option values can + be enclosed in double (") or single (') + quotes to preserve leading and trailing whitespaces as well as to + specify empty values. If the value itself contains trailing or leading + quotes, enclose it with an extra pair of quotes, for example + '"x"'. Non-leading and non-trailing quotes are interpreted + as being part of the option value.

+ +

The semantics of providing options in a file is equivalent to providing + the same set of options in the same order on the command line at the + point where the options file is specified, except that the shell escaping + and quoting is not required. Multiple files can be specified by including + several file options on the command line or inside other files.

+ +

The parsing constructor (those with the argc/argv or + cli::scanner 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 specified for an option of type int.

+ +

Furthermore, all scanners (and thus the parsing constructors that + call them) can throw the cli::eos_reached exception + which indicates that one of the peek(), next(), + or skip() functions were called while more() + returns false. Catching this exception normally indicates an + error in an option parser implementation. The argv_file_scanner + class can also throw the cli::file_io_failure exception + which indicates that a file could not be opened or there was a reading + error as well as the cli::unmatched_quote exception + which indicates that an unmatched leading or trailing quote was + found in an option value.

+ +

All CLI exceptions are derived from the common cli::exception + class which implements the polymorphic std::ostream insertion. + For example, if you 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)
+  {
+    e.print (os);
+    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 ();
+  };
+
+  class eos_reached: public exception
+  {
+  public:
+    virtual void
+    print (std::ostream&) const;
+
+    virtual const char*
+    what () const throw ();
+  };
+
+  class file_io_failure: public exception
+  {
+  public:
+    file_io_failure (const std::string& file);
+
+    const std::string&
+    file () const;
+
+    virtual void
+    print (std::ostream&) const;
+
+    virtual const char*
+    what () const throw ();
+  };
+
+  class unmatched_quote: public exception
+  {
+  public:
+    unmatched_quote (const std::string& argument);
+
+    const std::string&
+    argument () const;
+
+    virtual void
+    print (std::ostream&) const;
+
+    virtual const char*
+    what () const throw ();
+  };
+}
+  
+ + +

3.2 Option Definition

+ +

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

+ +

An option of a type other than 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's 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 separated + by |. The C++ accessor and modifier function names are + derived from the first name by removing any leading special characters, + such as -, /, etc., and replacing special + characters in other places with underscores. 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's 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 following 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 expressions use the constructor + initialization or wrap the expressions 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 the values for this option are inserted into the + container in the order they are encountered. As a result, + std::vector will contain all the values, including + 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=).

+ +

The last component in the option definition is optional documentation. + It is discussed in the next section.

+ +

3.3 Option Documentation

+ +

Option documentation mimics C++ string array initialization: + it is enclosed in {} and consists of one or more + documentation strings separated by a comma, for example:

+ +
+class options
+{
+  int --compression = 5
+  {
+    "<level>",
+    "Set compression to <level> instead of 5 by default.
+
+     With the higher compression levels the program may produce a
+     smaller output but may also take longer and use more memory."
+  };
+};
+  
+ +

The option documentation consists of a maximum of three documentation + strings. The first string is the value documentation string. + It describes the option value and is only applicable to options + with types other than bool (options of type + bool are flags and don't have an explicit value). + The second string (or the first string for options of type + bool) is the short documentation string. It + provides a brief description of the option. The last entry + in the option documentation is the long documentation string. + It provides a detailed description of the option. The short + documentation string is optional. If only two strings are + present in the option documentation (one string for options + of type bool), then the second (first) string is + assumed to be the long documentation string.

+ +

Option documentation is used to print the usage information + as well as to generate program documentation in the HTML and + man page formats. For usage information the short documentation + string is used if provided. If only the long string is available, + then, by default, only the first sentence from the long string + is used. You can override this behavior and include the complete + long string in the usage information by specifying the + --long-usage CLI compiler option. When generating + the program documentation, the long documentation strings are + always used.

+ +

The value documentation string can contain text enclosed in + <> which is automatically recognized by the CLI + compiler and typeset according to the selected output in all three + documentation strings. For example, in usage the level + value for the --compression option presented above + will be displayed as <level> while in the HTML and + man page output it will be typeset in italic as + level. Here is another example using the + std::map type:

+ +
+include <map>;
+include <string>;
+
+class options
+{
+  std::map<std::string, std::string> --map
+  {
+    "<key>=<value>",
+    "Add the <key>, <value> pair to the map."
+  };
+};
+  
+ +

The resulting HTML output for this option would look like this:

+ +
+
--map key=value
+
Add the key, value pair to the map.
+
+ +

As you might have noticed from the examples presented so far, the + documentation strings can span multiple lines which is not possible + in C++. Also, all three documentation strings support the following + basic formatting mechanisms. The start of a new paragraph is indicated + by a blank line. A fragment of text can be typeset in monospace font + (normally used for code fragments) by enclosing it in the + \c{} block. Similarly, text can be typeset in bold or + italic fonts using the \b{} and \i{} blocks, + respectively. You can also combine several font properties in a single + block, for example, \cb{bold code}. If you need to include + literal } in a formatting block, you can use the + \} escape sequence, for example, + \c{int a[] = {1, 2\}}. The following example shows how we + can use these mechanisms:

+ +
+class options
+{
+  int --compression = 5
+  {
+    "<level>",
+    "Set compression to <level> instead of 5 by default.
+
+     With the higher compression levels the program \i{may}
+     produce a smaller output but may also \b{take longer}
+     and \b{use more memory}."
+  };
+};
+  
+ +

The resulting HTML output for this option would look like this:

+ +
+
--compression level
+
Set compression to level instead of 5 by default. + +

With the higher compression levels the program may produce a + smaller output but may also take longer and use more memory.

+
+ +

3.4 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 use < > or + " "-enclosed paths. The CLI compiler does not + actually open or read these files. Instead, the include directives + are translated to C++ preprocessor #include directives + in the generated C++ header file. For example, the following CLI + definition:

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

Will result in the following C++ header file:

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

Without the #include directives the std::string + and name_type types in the options class would + be undeclared and result in compilation errors.

+ +

3.5 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 structure would result in the equivalent C++ + namespaces structure:

+ +
+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/cli/doc/language.txt b/cli/doc/language.txt new file mode 100644 index 0000000..e6c9161 --- /dev/null +++ b/cli/doc/language.txt @@ -0,0 +1,128 @@ +Token types: + keyword + identifier + punctuation (";" "{" "}" "(" ")" "," "|" "=" ":") + cxx-path-literal ("c++:path", , "path", ) + cli-path-literal ("cli:path", , "path.cli", ) + char-literal + string-literal + bool-literal + int-literal + float-literal + call-expr (e.g., (a, 2)) + template-expr (e.g., ) + end-of-stream + +def-unit: + include-decl-seq(opt) decl-seq(opt) + +include-decl-seq: + source-decl + include-decl + include-decl-seq include-decl + +source-decl: + "source" cli-path-literal ";" + +include-decl: + "include" include-path ";" + +include-path: + cxx-path-literal + cli-path-literal + +decl-seq: + decl + decl-seq decl + +decl: + source-decl + scope-doc + namespace-def + class-def + +scope-doc: + string-literal + "{" doc-string-seq "}" + +namespace-def: + "namespace" identifier "{" namespace-body "}" + +namespace-body: + decl-seq(opt) + +class-def: + "class" identifier inheritance-spec(opt) abstract-spec(opt) "{" class-decl-seq(opt) "};" + +inheritance-spec: + ":" base-seq + +base-seq: + qualified-name + base-seq "," qualified-name + +abstract-spec: + "=" "0" + +class-decl-seq: + class-decl + class-decl-seq class-decl + +class-decl + scope-doc + option-def + +option-def: + type-spec option-name-seq initializer(opt) option-def-trailer + +type-spec: + fundamental-type-spec + qualified-name + +option-name-seq: + option-name + option-name-seq "|" option-name + +option-name: + option-identifier + string-literal + +initializer: + "=" initializer-expr + call-expr + +initializer-expr: + bool-literal + int-literal + float-literal + char-literal + string-literal + qualified-name + call-expr + +option-def-trailer: + ";" + option-doc + +option-doc: + "{" doc-string-seq "}" + +doc-string-seq: + string-literal + doc-string-seq "," string-literal + +qualified-name: + "::" qualified-name-trailer + qualified-name-trailer + +qualified-name-trailer: + template-id + qualified-name "::" template-id + +template-id: + identifier template-expr(opt) + +fundamental-type-spec: + "bool" + ... + "long double" -- cgit v1.1