123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- [/==============================================================================
- Copyright (C) 2001-2011 Joel de Guzman
- Copyright (C) 2001-2011 Hartmut Kaiser
- Distributed under the Boost Software License, Version 1.0. (See accompanying
- file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- ===============================================================================/]
- [section Warming up]
- Learning how to use __karma__ is really simple. We will start from trivial
- examples, ramping up as we go.
- [heading Trivial Example #1 Generating a number]
- Let's create a generator that will output a floating-point number:
- double_
- Easy huh? The above code actually instantiates a Spirit floating point
- generator (a built-in generator). Spirit has many pre-defined generators and
- consistent naming conventions will help you finding your way through the maze.
- Especially important to note is that things related to identical entities (as
- in this case, floating point numbers) are named identically in __karma__ and in
- __qi__. Actually, both libraries are using the very same variable instance to
- refer to a floating point generator or parser: `double_`.
- [heading Trivial Example #2 Generating two numbers]
- Now, let's create a generator that will output a line consisting of two
- floating-point numbers.
- double_ << double_
- Here you see the familiar floating-point numeric generator `double_` used twice,
- once for each number. If you are used to see the `'>>'` operator for concatenating
- two parsers in __qi__ you might wonder, what's that `'<<'` operator doing in
- there? We decided to distinguish generating and parsing of sequences the same
- way as the std::stream libraries do: we use operator `'>>'` for input (parsing),
- and operator `'<<'` for output (generating). Other than that there is no
- significant difference. The above program creates a generator from two simpler
- generators, glueing them together with the sequence operator. The result is a
- generator that is a composition of smaller generators. Whitespace between
- numbers can implicitly be inserted depending on how the generator is invoked
- (see below).
- [note When we combine generators, we end up with a "bigger" generator, but
- it's still a generator. Generators can get bigger and bigger, nesting more
- and more, but whenever you glue two generators together, you end up with one
- bigger generator. This is an important concept.
- ]
- [heading Trivial Example #3 Generating one or more numbers]
- Now, creating output for two numbers is not too interesting. Let's create a
- generator that will output zero or more floating-point numbers in a row.
- *double_
- This is like a regular-expression Kleene Star. We moved the `*` to the front for
- the same reasons we did in __qi__: we must work with the syntax rules of C++.
- But if you know regular expressions (and for sure you remember those C++ syntax
- rules) it will start to look very familiar in a matter of a very short time.
- Any expression that evaluates to a generator may be used with the Kleene Star.
- Keep in mind, though, that due to C++ operator precedence rules you may need
- to put the expression in parentheses for complex expressions. As above,
- whitespace can be inserted implicitly in between the generated numbers, if
- needed.
- [heading Trivial Example #4 Generating a comma-delimited list of numbers]
- We follow the lead of __qi__'s warming up section and will create a generator
- that produces a comma-delimited list of numbers.
- double_ << *(lit(',') << double_)
- Notice `lit(',')`. It is a literal character generator that simply generates
- the comma `','`. In this case, the Kleene Star is modifying a more complex
- generator, namely, the one generated by the expression:
- (lit(',') << double_)
- Note that this is a case where the parentheses are necessary. The Kleene Star
- encloses the complete expression above, repeating the whole pattern in the
- generated output zero or more times.
- [heading Let's Generate!]
- We're done with defining the generator. All that's left is to invoke the
- generator to do its work. For now, we will use the `generate_delimited` function.
- One overload of this function accepts four arguments:
- # An output iterator accepting the generated characters
- # The generator expression
- # Another generator called the delimiting generator
- # The data to format and output
- While comparing this minimal example with an equivalent parser example we
- notice a significant difference. It is possible (and actually, it makes a lot
- of sense) to use a parser without creating any internal representation of the
- parsed input (i.e. without 'producing' any data from the parsed input). Using
- a parser in this mode checks the provided input against
- the given parser expression allowing to verify whether the input is parsable.
- For generators this mode doesn't make any sense. What is output generation
- without generating any output? So we always will have to supply the data the
- output should be generated from. In our example we supply a list of `double`
- numbers as the last parameter to the function `generate_delimited` (see code
- below).
- In this example, we wish to delimit the generated numbers by spaces. Another
- generator named `space` is included in Spirit's repertoire of predefined
- generators. It is a very trivial generator that simply produces spaces. It is
- the equivalent to writing `lit(' ')`, or simply `' '`. It has been
- implemented for similarity with the corresponding predefined `space` parser.
- We will use `space` as our delimiter. The delimiter is the one responsible for
- inserting characters in between generator elements such as the `double_` and
- `lit`.
- Ok, so now let's generate (for the complete source code of this example please
- refer to [@../../example/karma/num_list1.cpp num_list1.cpp]).
- [import ../../example/karma/num_list1.cpp]
- [tutorial_karma_numlist1]
- [note You might wonder how a `vector<double>`, which is actually a single data
- structure, can be used as an argument (we call it attribute) to a sequence
- of generators. This seems to be counter intuitive and doesn't match with
- your experience of using `printf`, where each formatting placeholder has
- to be matched with a corresponding argument. Well, we will explain this
- behavior in more detail later in this tutorial. For now just consider
- this to be a special case, implemented on purpose to allow more flexible
- output formatting of STL containers: sequences accept a single container
- attribute if all elements of this sequence accept attributes compatible
- with the elements held by this container.]
- The generate function returns `true` or `false` depending on the result of the
- output generation. As outlined in different places of this documentation, a
- generator may fail for different reasons. One of the possible reasons is an
- error in the underlying output iterator (memory exhausted or disk full, etc.).
- Another reason might be that the data doesn't match the requirements of a
- particular generator.
- [note `char` and `wchar_t` operands
- The careful reader may notice that the generator expression has `','` instead
- of `lit(',')` as the previous examples did. This is ok due to C++ syntax
- rules of conversion. Spirit provides `<<` operators that are overloaded to
- accept a `char` or `wchar_t` argument on its left or right (but not both).
- An operator may be overloaded if at least one of its parameters is a
- user-defined type. In this case, the `double_` is the 2nd argument to
- `operator<<`, and so the proper overload of `<<` is used, converting `','`
- into a character literal generator.
- The problem with omitting the `lit` should be obvious: `'a' << 'b'` is not a
- spirit generator, it is a numeric expression, left-shifting the ASCII (or
- another encoding) value of `'a'` by the ASCII value of `'b'`. However, both
- `lit('a') << 'b'` and `'a' << lit('b')` are Spirit sequence generators
- for the letter `'a'` followed by `'b'`. You'll get used to it, sooner or
- later.
- ]
- Note that we inlined the generator directly in the call to `generate_delimited`.
- Upon calling this function, the expression evaluates into a temporary,
- unnamed generator which is passed into the `generate_delimited` function,
- used, and then destroyed.
- Here, we chose to make the generate function generic by making it a template,
- parameterized by the output iterator type. By doing so, it can put the generated
- data into any STL conforming output iterator.
- [endsect] [/ Warming up]
|