Here is the complete example of a simple calculator. The user types expressions, and the calculator prints the result. You can define and recall variables, too.

#include "boost/spirit.hpp"
#include "boost/spirit/phoenix/binders.hpp"
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <istream>
#include <map>
#include <ostream>
#include <string>

// Semantic actions can be functors. The operator() function is called
// with two iterators that specify the range of input that matches the production.
struct do_help {
  template<typename Iter>
  void operator()(Iter, Iter) const
  {
    std::cout << "help - to be implemented\n";
  }
};

struct do_quit {
  template<typename Iter>
  void operator()(Iter, Iter) const
  {
    std::exit(EXIT_SUCCESS);
  }
};

// Symbol table for storing variables.
typedef std::map<std::string, double> symtab_t;

struct calculator : boost::spirit::grammar<calculator>
{
  // The parser object is copied a lot, so instead of keeping its own table
  // of variables, it keeps track of a reference to a common table.
  calculator(symtab_t& variables) : variables(variables) {}

  // A production can have an associated closure, to store information
  // for that production.
  struct value_closure : boost::spirit::closure<value_closure, double>
  {
    member1 value;
  };

  struct assignment_closure :
    boost::spirit::closure<assignment_closure, std::string, double>
  {
    member1 name;
    member2 value;
  };

  struct string_closure : boost::spirit::closure<string_closure, std::string>
  {
    member1 name;
  };

  // Following is the grammar definition.
  template <typename ScannerT>
  struct definition
  {
    definition(calculator const& self)
    {
      using namespace boost::spirit;
      using namespace phoenix;

      // The commands are linked to functors or member functions,
      // to demonstrate both styles. In real code, you should choose
      // one style and use it uniformly.
      command
        = as_lower_d["help"][do_help()]
        | as_lower_d["quit"][do_quit()]
        | as_lower_d["dump"][bind(&calculator::dump)(self)]
        ;

      // The lexeme_d directive tells the scanner to treat white space as
      // significant. Thus, an identifier cannot have internal white space.
      // The alpha_p and alnum_p parsers are built-in.
      // Notice how the semantic action uses a Phoenix lambda function
      // that constructs a std::string. The arg1 and arg2 placeholders are
      // are bound at runtime to the iterator range that matches this rule.
      identifier
        = lexeme_d
        [
          ( alpha_p | '_')
          >> *( alnum_p | '_')
        ][identifier.name = construct_<std::string>(arg1, arg2)]
        ;

      group
        = '('
          >> expression[group.value = arg1]
          >> ')'
        ;

      // An assignment statement must store the variable name and value.
      // The name and the value are stored in the closure, then the define
      // function is called to store the definition. Notice how a rule can
      // have multiple semantic actions.
      assignment
        = identifier[assignment.name = arg1]
          >> '='
          >> expression[assignment.value = arg1]
               [bind(&calculator::define)(self, assignment.name, assignment.value)]
        ;

      // A statement can end at the end of the line, or with a semicolon.
      statement
        =   ( command
            | assignment
            | expression[bind(&calculator::do_print)(self, arg1)]
          )
        >> (end_p | ';')
        ;

      // The longest_d directive is built-in to tell the parser to make
      // the longest match it can. Thus "1.23" matches real_p rather than
      // int_p followed by ".23".
      literal
        = longest_d
        [
          int_p[literal.value = arg1]
          | real_p[literal.value = arg1]
        ]
        ;

      // A variable name must be looked up. This is a straightforward
      // Phoenix binding.
      factor
        = literal[factor.value = arg1]
        | group[factor.value = arg1]
        | identifier[factor.value = bind(&calculator::lookup)(self, arg1)]
        ;

      term
        = factor[term.value = arg1]
          >> *( ('*' >> factor[term.value *= arg1])
              | ('/' >> factor[term.value /= arg1])
            )
        ;

      expression
        = term[expression.value = arg1]
          >> *( ('+' >> term[expression.value += arg1])
              | ('-' >> term[expression.value -= arg1])
            )
        ;
    }

    // The start symbol is returned from start().
    boost::spirit::rule<ScannerT> const&
    start() const { return statement; }

    // Each rule must be declared, optionally with an associated closure.
    boost::spirit::rule<ScannerT> command, statement;
    boost::spirit::rule<ScannerT, assignment_closure::context_t> assignment;
    boost::spirit::rule<ScannerT, string_closure::context_t> identifier;
    boost::spirit::rule<ScannerT, value_closure::context_t> expression, factor,
                                                            group, literal, term;
  };

  // Member functions that are called in semantic actions.
  void define(const std::string& name, double value) const
  {
    variables[name] = value;
  }

  double lookup(const std::string& name) const
  {
    symtab_t::iterator it = variables.find(name);
    if (it == variables.end()) {
      std::cerr << "undefined name: " << name << '\n';
      return 0.0;
    }
    else
      return (*it).second;
  }

  void do_print(double x) const
  {
    std::cout << x << '\n';
  }

  void dump() const
  {
    // Dump the entire symbol table. Notice how this function uses
    // Boost lambda functions instead of Phoenix, just to show you that
    // you can mix the two in a single file.
    using namespace boost::lambda;
    typedef std::pair<const std::string, double> symtab_pair;
    for_each(variables.begin(), variables.end(),
      std::cout << bind(&symtab_pair::first, _1) << '=' <<
                   bind(&symtab_pair::second, _1) << '\n');
  }

private:
  symtab_t& variables;
};

int main()
{
  using namespace boost::spirit;
  using namespace std;
  symtab_t variables;
  calculator calc(variables);
  string line;

  variables["pi"] = 3.141592653589792;
  while (getline(cin, line))
  {
    // Read one line of text and parse it. If the parser does not consume
    // the entire string, keep parsing the same string until an error occurs
    // or the string is consumed. Then go back and read another string.
    string::iterator first = line.begin();
    parse_info<string::iterator> info;
    do {
      info = parse(first, line.end(), calc, space_p);
      if (! info.hit)
        // Display a caret that points to the position where the error
        // was detected.
        cerr << setw(info.stop - line.begin()) << " " << "^ error\n";
      else if (! info.full)
        // Keep track of where to start parsing the next statement.
        first = info.stop;
    } while(! info.full && info.hit);
  }
}

Return to C++: Beyond the Standard Library.