early-access version 3088

This commit is contained in:
pineappleEA
2022-11-05 15:35:56 +01:00
parent 4e4fc25ce3
commit b601909c6d
35519 changed files with 5996896 additions and 860 deletions

View File

@@ -0,0 +1,20 @@
#==============================================================================
# Copyright (c) 2010 Gevorg Voskanyan
# Copyright (c) 2001-2010 Joel de Guzman
# Copyright (c) 2001-2010 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)
#==============================================================================
# build all examples
local examples = [ glob-tree [jJ]amfile [jJ]amfile.v2 ] ;
for local ex in $(examples) {
local dir = $(ex:P) ;
if $(dir) != "" && $(dir) != "." {
build-project $(dir) ;
}
}

View File

@@ -0,0 +1,38 @@
#==============================================================================
# Copyright (c) 2001-2009 Hartmut Kaiser
# Copyright (c) 2001-2009 Joel de Guzman
#
# 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)
#==============================================================================
project spirit-karma-example
: requirements
<c++-template-depth>300
:
:
;
exe actions_example : actions.cpp ;
exe auto_facilities : auto_facilities.cpp ;
exe basic_facilities : basic_facilities.cpp /boost//date_time ;
exe karma_calc2_ast_dump : calc2_ast_dump.cpp ;
exe karma_calc2_ast_rpn : calc2_ast_rpn.cpp ;
exe karma_calc2_ast_vm : calc2_ast_vm.cpp ;
exe classify_char : classify_char.cpp ;
exe karma_complex_number : complex_number.cpp ;
exe karma_complex_number_easier : complex_number_easier.cpp ;
exe karma_complex_number_adapt : complex_number_adapt.cpp ;
exe karma_num_list1 : num_list1.cpp ;
exe karma_num_list2 : num_list2.cpp ;
exe karma_num_list3 : num_list3.cpp ;
exe karma_num_matrix : num_matrix.cpp ;
exe key_value_sequence : key_value_sequence.cpp ;
exe mini_xml_karma : mini_xml_karma.cpp ;
exe printf_style_double_format : printf_style_double_format.cpp ;
exe quick_start1 : quick_start1.cpp ;
exe karma_reference : reference.cpp ;
exe karma_reorder_struct : reorder_struct.cpp ;
exe karma_escaped_string : escaped_string.cpp ;
exe simple_columns_directive : simple_columns_directive.cpp ;
exe quoted_strings : quoted_strings.cpp ;

View File

@@ -0,0 +1,131 @@
/*=============================================================================
Copyright (c) 2001-2010 Hartmut Kaiser
Copyright (c) 2001-2010 Joel de Guzman
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)
=============================================================================*/
#include <boost/spirit/include/karma.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/bind/bind.hpp>
#include <iostream>
#include <sstream>
// Presented are various ways to attach semantic actions
// * Using plain function pointer
// * Using simple function object
// * Using boost.bind
// * Using boost.lambda
using boost::spirit::unused_type;
//[karma_tutorial_semantic_action_functions
namespace client
{
namespace karma = boost::spirit::karma;
// A plain function
void read_function(int& i)
{
i = 42;
}
// A member function
struct reader
{
void print(int& i) const
{
i = 42;
}
};
// A function object
struct read_action
{
void operator()(int& i, unused_type, unused_type) const
{
i = 42;
}
};
}
//]
///////////////////////////////////////////////////////////////////////////////
int main()
{
using boost::spirit::karma::int_;
using boost::spirit::karma::generate;
using client::read_function;
using client::reader;
using client::read_action;
{ // example using plain functions
using namespace boost::spirit;
std::string generated;
std::back_insert_iterator<std::string> outiter(generated);
//[karma_tutorial_attach_actions1
generate(outiter, '{' << int_[&read_function] << '}');
//]
std::cout << "Simple function: " << generated << std::endl;
}
{ // example using simple function object
using namespace boost::spirit;
std::string generated;
std::back_insert_iterator<std::string> outiter(generated);
//[karma_tutorial_attach_actions2
generate(outiter, '{' << int_[read_action()] << '}');
//]
std::cout << "Simple function object: " << generated << std::endl;
}
{ // example using plain function with boost.bind
using namespace boost::placeholders;
std::string generated;
std::back_insert_iterator<std::string> outiter(generated);
//[karma_tutorial_attach_actions3
generate(outiter, '{' << int_[boost::bind(&read_function, _1)] << '}');
//]
std::cout << "Simple function with Boost.Bind: " << generated << std::endl;
}
{ // example using member function with boost.bind
using namespace boost::placeholders;
std::string generated;
std::back_insert_iterator<std::string> outiter(generated);
//[karma_tutorial_attach_actions4
reader r;
generate(outiter, '{' << int_[boost::bind(&reader::print, &r, _1)] << '}');
//]
std::cout << "Member function: " << generated << std::endl;
}
{ // example using boost.lambda
namespace lambda = boost::lambda;
using namespace boost::spirit;
std::string generated;
std::back_insert_iterator<std::string> outiter(generated);
//[karma_tutorial_attach_actions5
std::stringstream strm("42");
generate(outiter, '{' << int_[strm >> lambda::_1] << '}');
//]
std::cout << "Boost.Lambda: " << generated << std::endl;
}
return 0;
}

View File

@@ -0,0 +1,243 @@
// Copyright (c) 2001-2010 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)
// The main purpose of this example is to show the uniform and easy way of
// output formatting for different container types.
//
// The 'auto_' primitive used below is very similar to the 'stream' primitive
// demonstrated in the example 'basic_facilities.cpp' as it allows to generate
// output from a multitude of data types. The main difference is that it is
// mapped to the correct Karma generator instead of using any available
// operator<<() for the contained data type. Additionally this means, that
// the format descriptions used below will be usable for any contained type as
// long as this type has a defined mapping to a Karma generator.
// use a larger value for the alignment field width (default is 10)
#define BOOST_KARMA_DEFAULT_FIELD_LENGTH 25
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <algorithm>
#include <cstdlib>
#include <boost/array.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/array.hpp>
#include <boost/proto/deep_copy.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/spirit/include/karma.hpp>
using namespace boost::spirit;
using namespace boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace spirit { namespace traits
{
// We add a specialization for the create_generator customization point
// defining a custom output format for the value type of the std::map used
// below (std::pair<int const, std::string>). Generally, any specialization
// for create_generator is expected to return the proto expression to be
// used to generate output for the type the customization point has been
// specialized for.
//
// We need to utilize proto::deep_copy as the expression contains a literal
// (the ':') which normally gets embedded in the proto expression by
// reference only. The deep copy converts the proto tree to hold this by
// value. The deep copy operation can be left out for simpler proto
// expressions (not containing references to temporaries). Alternatively
// you could use the proto::make_expr() facility to build the required
// proto expression.
template <>
struct create_generator<std::pair<int const, std::string> >
{
typedef proto::result_of::deep_copy<
BOOST_TYPEOF(int_ << ':' << string)
>::type type;
static type call()
{
return proto::deep_copy(int_ << ':' << string);
}
};
}}}
///////////////////////////////////////////////////////////////////////////////
// Output the given containers in list format
// Note: the format description does not depend on the type of the sequence
// nor does it depend on the type of the elements contained in the
// sequence
///////////////////////////////////////////////////////////////////////////////
template <typename Container>
void output_container(std::ostream& os, Container const& c)
{
// output the container as a sequence without separators
os <<
karma::format(
auto_, // format description
c // data
) << std::endl << std::endl;
os <<
karma::format(
*auto_, // format description
c // data
) << std::endl << std::endl;
// output the container as a space separated sequence
os <<
karma::format_delimited(
auto_, // format description
space, // delimiter
c // data
) << std::endl << std::endl;
os <<
karma::format_delimited(
*auto_, // format description
space, // delimiter
c // data
) << std::endl << std::endl;
os <<
karma::format_delimited(
'[' << *auto_ << ']', // format description
space, // delimiter
c // data
) << std::endl << std::endl;
// output the container as a comma separated list
os <<
karma::format(
auto_ % ", ", // format description
c // data
) << std::endl << std::endl;
os <<
karma::format(
'[' << (auto_ % ", ") << ']', // format description
c // data
) << std::endl << std::endl;
os <<
karma::format(
'[' << -(auto_ % ", ") << ']', // format description
c // data
) << std::endl << std::endl;
os <<
karma::format(
'[' << (+auto_ | "empty") << ']', // format description
c // data
) << std::endl << std::endl;
// output the container as a comma separated list of items enclosed in '()'
os <<
karma::format(
('(' << auto_ << ')') % ", ", // format description
c // data
) << std::endl << std::endl;
os <<
karma::format(
'[' << (
('(' << auto_ << ')') % ", "
) << ']', // format description
c // data
) << std::endl << std::endl;
// output the container as a HTML list
os <<
karma::format_delimited(
"<ol>" <<
*verbatim["<li>" << auto_ << "</li>"]
<< "</ol>", // format description
'\n', // delimiter
c // data
) << std::endl;
// output the container as right aligned column
os <<
karma::format_delimited(
*verbatim[
"|" << right_align[auto_] << "|"
], // format description
'\n', // delimiter
c // data
) << std::endl;
os << std::endl;
}
int main()
{
///////////////////////////////////////////////////////////////////////////
// C-style array
int i[4] = { 3, 6, 9, 12 };
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "int i[]" << std::endl;
output_container(std::cout, boost::make_iterator_range(i, i+4));
///////////////////////////////////////////////////////////////////////////
// vector
std::vector<int> v (5);
std::generate(v.begin(), v.end(), std::rand); // randomly fill the vector
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "std::vector<int>" << std::endl;
output_container(std::cout, v);
///////////////////////////////////////////////////////////////////////////
// list
std::list<char> l;
l.push_back('A');
l.push_back('B');
l.push_back('C');
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "std::list<char>" << std::endl;
output_container(std::cout, l);
///////////////////////////////////////////////////////////////////////////
// strings
std::string str("Hello world!");
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "std::string" << std::endl;
output_container(std::cout, str);
///////////////////////////////////////////////////////////////////////////
// boost::array
boost::array<long, 5> arr;
std::generate(arr.begin(), arr.end(), std::rand); // randomly fill the array
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "boost::array<long, 5>" << std::endl;
output_container(std::cout, arr);
///////////////////////////////////////////////////////////////////////////
// map of int --> string mappings
std::map<int, std::string> mappings;
mappings.insert(std::make_pair(0, "zero"));
mappings.insert(std::make_pair(1, "one"));
mappings.insert(std::make_pair(2, "two"));
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "std::map<int, std::string>" << std::endl;
output_container(std::cout, mappings);
return 0;
}

View File

@@ -0,0 +1,227 @@
// Copyright (c) 2001-2010 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)
// The main purpose of this example is to show the uniform and easy way of
// output formatting for different container types.
//
// Since the 'stream' primitive used below uses the streaming operator defined
// for the container value_type, you must make sure to have a corresponding
// operator<<() available for this contained data type. OTOH this means, that
// the format descriptions used below will be usable for any contained type as
// long as this type has an associated streaming operator defined.
// use a larger value for the alignment field width (default is 10)
#define BOOST_KARMA_DEFAULT_FIELD_LENGTH 25
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <algorithm>
#include <cstdlib>
#include <boost/array.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/range/iterator_range.hpp>
///////////////////////////////////////////////////////////////////////////////
// This streaming operator is needed to generate the output from the map below
// Yes, it's heresy, but this operator has to live in namespace std to be
// picked up by the compiler.
namespace std {
inline std::ostream&
operator<<(std::ostream& os, std::pair<int const, std::string> v)
{
os << v.first << ": " << v.second;
return os;
}
}
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/karma_format.hpp>
using namespace boost::spirit;
using namespace boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
// Output the given containers in list format
// Note: the format description does not depend on the type of the sequence
// nor does it depend on the type of the elements contained in the
// sequence
///////////////////////////////////////////////////////////////////////////////
template <typename Container>
void output_container(std::ostream& os, Container const& c)
{
// output the container as a space separated sequence
os <<
karma::format(
*stream, // format description
c // data
) << std::endl << std::endl;
// output the container as a space separated sequence
os <<
karma::format_delimited(
*stream, // format description
space, // delimiter
c // data
) << std::endl << std::endl;
os <<
karma::format_delimited(
'[' << *stream << ']', // format description
space, // delimiter
c // data
) << std::endl << std::endl;
// output the container as a comma separated list
os <<
karma::format(
stream % ", ", // format description
c // data
) << std::endl << std::endl;
os <<
karma::format(
'[' << (stream % ", ") << ']', // format description
c // data
) << std::endl << std::endl;
os <<
karma::format(
'[' << -(stream % ", ") << ']', // format description
c // data
) << std::endl << std::endl;
os <<
karma::format(
'[' << (+stream | "empty") << ']', // format description
c // data
) << std::endl << std::endl;
// output the container as a comma separated list of items enclosed in '()'
os <<
karma::format(
('(' << stream << ')') % ", ", // format description
c // data
) << std::endl << std::endl;
os <<
karma::format(
'[' << (
('(' << stream << ')') % ", "
) << ']', // format description
c // data
) << std::endl << std::endl;
// output the container as a HTML list
os <<
karma::format_delimited(
"<ol>" <<
*verbatim["<li>" << stream << "</li>"]
<< "</ol>", // format description
'\n', // delimiter
c // data
) << std::endl;
// output the container as right aligned column
os <<
karma::format_delimited(
*verbatim[
"|" << right_align[stream] << "|"
], // format description
'\n', // delimiter
c // data
) << std::endl;
os << std::endl;
}
int main()
{
///////////////////////////////////////////////////////////////////////////
// C-style array
int i[4] = { 3, 6, 9, 12 };
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "int i[]" << std::endl;
output_container(std::cout, boost::make_iterator_range(i, i+4));
///////////////////////////////////////////////////////////////////////////
// vector
std::vector<int> v (5);
std::generate(v.begin(), v.end(), std::rand); // randomly fill the vector
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "std::vector<int>" << std::endl;
output_container(std::cout, v);
///////////////////////////////////////////////////////////////////////////
// list
std::list<char> l;
l.push_back('A');
l.push_back('B');
l.push_back('C');
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "std::list<char>" << std::endl;
output_container(std::cout, l);
///////////////////////////////////////////////////////////////////////////
// strings
std::string str("Hello world!");
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "std::string" << std::endl;
output_container(std::cout, str);
///////////////////////////////////////////////////////////////////////////
// boost::array
boost::array<long, 5> arr;
std::generate(arr.begin(), arr.end(), std::rand); // randomly fill the array
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "boost::array<long, 5>" << std::endl;
output_container(std::cout, arr);
///////////////////////////////////////////////////////////////////////////
// vector of boost::date objects
// Note: any registered facets get used!
using namespace boost::gregorian;
std::vector<date> dates;
dates.push_back(date(2005, Jun, 25));
dates.push_back(date(2006, Jan, 13));
dates.push_back(date(2007, May, 03));
date_facet* facet(new date_facet("%A %B %d, %Y"));
std::cout.imbue(std::locale(std::cout.getloc(), facet));
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "std::vector<boost::date>" << std::endl;
output_container(std::cout, dates);
///////////////////////////////////////////////////////////////////////////
// map of int --> string mappings
std::map<int, std::string> mappings;
mappings.insert(std::make_pair(0, "zero"));
mappings.insert(std::make_pair(1, "one"));
mappings.insert(std::make_pair(2, "two"));
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "std::map<int, std::string>" << std::endl;
output_container(std::cout, mappings);
return 0;
}

View File

@@ -0,0 +1,177 @@
/*=============================================================================
Copyright (c) 2001-2010 Joel de Guzman
Copyright (c) 2001-2010 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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// A Calculator example demonstrating generation of AST which gets dumped into
// a human readable format afterwards.
//
// [ JDG April 28, 2008 ]
// [ HK April 28, 2008 ]
//
///////////////////////////////////////////////////////////////////////////////
#if !defined(SPIRIT_EXAMPLE_CALC2_AST_APR_30_2008_1011AM)
#define SPIRIT_EXAMPLE_CALC2_AST_APR_30_2008_1011AM
#include <boost/variant.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/function.hpp>
#include <boost/phoenix/statement.hpp>
#include <boost/spirit/include/karma_domain.hpp>
#include <boost/spirit/include/support_attributes_fwd.hpp>
///////////////////////////////////////////////////////////////////////////////
// Our AST
///////////////////////////////////////////////////////////////////////////////
struct binary_op;
struct unary_op;
struct nil {};
struct expression_ast
{
typedef
boost::variant<
nil // can't happen!
, int
, boost::recursive_wrapper<binary_op>
, boost::recursive_wrapper<unary_op>
>
type;
// expose variant types
typedef type::types types;
// expose variant functionality
int which() const { return expr.which(); }
// constructors
expression_ast()
: expr(nil()) {}
expression_ast(unary_op const& expr)
: expr(expr) {}
expression_ast(binary_op const& expr)
: expr(expr) {}
expression_ast(unsigned int expr)
: expr(expr) {}
expression_ast(type const& expr)
: expr(expr) {}
expression_ast& operator+=(expression_ast const& rhs);
expression_ast& operator-=(expression_ast const& rhs);
expression_ast& operator*=(expression_ast const& rhs);
expression_ast& operator/=(expression_ast const& rhs);
type expr;
};
// expose variant functionality
namespace boost
{
// this function has to live in namespace boost for ADL to correctly find it
template <typename T>
inline T get(expression_ast const& expr)
{
return boost::get<T>(expr.expr);
}
namespace spirit { namespace traits
{
// the specialization below tells Spirit to handle expression_ast as
// if it where a 'real' variant (if used with Spirit.Karma)
template <>
struct not_is_variant<expression_ast, karma::domain>
: mpl::false_ {};
// the specialization of variant_which allows to generically extract
// the current type stored in the given variant like type
template <>
struct variant_which<expression_ast>
{
static int call(expression_ast const& v)
{
return v.which();
}
};
}}
}
///////////////////////////////////////////////////////////////////////////////
struct binary_op
{
binary_op() {}
binary_op(
char op
, expression_ast const& left
, expression_ast const& right)
: op(op), left(left), right(right) {}
char op;
expression_ast left;
expression_ast right;
};
struct unary_op
{
unary_op(
char op
, expression_ast const& right)
: op(op), right(right) {}
char op;
expression_ast right;
};
inline expression_ast& expression_ast::operator+=(expression_ast const& rhs)
{
expr = binary_op('+', expr, rhs);
return *this;
}
inline expression_ast& expression_ast::operator-=(expression_ast const& rhs)
{
expr = binary_op('-', expr, rhs);
return *this;
}
inline expression_ast& expression_ast::operator*=(expression_ast const& rhs)
{
expr = binary_op('*', expr, rhs);
return *this;
}
inline expression_ast& expression_ast::operator/=(expression_ast const& rhs)
{
expr = binary_op('/', expr, rhs);
return *this;
}
// We should be using expression_ast::operator-. There's a bug
// in phoenix type deduction mechanism that prevents us from
// doing so. Phoenix will be switching to BOOST_TYPEOF. In the
// meantime, we will use a phoenix::function below:
template <char Op>
struct unary_expr
{
template <typename T>
struct result { typedef T type; };
expression_ast operator()(expression_ast const& expr) const
{
return unary_op(Op, expr);
}
};
boost::phoenix::function<unary_expr<'+'> > pos;
boost::phoenix::function<unary_expr<'-'> > neg;
#endif

View File

@@ -0,0 +1,169 @@
/*=============================================================================
Copyright (c) 2001-2010 Joel de Guzman
Copyright (c) 2001-2010 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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// A Calculator example demonstrating generation of AST which gets dumped into
// a human readable format afterwards.
//
// [ JDG April 28, 2008 ]
// [ HK April 28, 2008 ]
//
///////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <vector>
#include <string>
#include "calc2_ast.hpp"
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
using namespace boost::spirit;
using namespace boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
// Our calculator parser grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct calculator
: qi::grammar<Iterator, expression_ast(), space_type>
{
calculator() : calculator::base_type(expression)
{
expression =
term [_val = _1]
>> *( ('+' >> term [_val += _1])
| ('-' >> term [_val -= _1])
)
;
term =
factor [_val = _1]
>> *( ('*' >> factor [_val *= _1])
| ('/' >> factor [_val /= _1])
)
;
factor =
uint_ [_val = _1]
| '(' >> expression [_val = _1] >> ')'
| ('-' >> factor [_val = neg(_1)])
| ('+' >> factor [_val = pos(_1)])
;
}
qi::rule<Iterator, expression_ast(), space_type> expression, term, factor;
};
// We need to tell fusion about our binary_op and unary_op structs
// to make them a first-class fusion citizen
//
// Note: we register the members exactly in the same sequence as we need them
// in the grammar
BOOST_FUSION_ADAPT_STRUCT(
binary_op,
(expression_ast, left)
(char, op)
(expression_ast, right)
)
BOOST_FUSION_ADAPT_STRUCT(
unary_op,
(char, op)
(expression_ast, right)
)
///////////////////////////////////////////////////////////////////////////////
// Our AST grammar for the generator, this just dumps the AST as a expression
///////////////////////////////////////////////////////////////////////////////
template <typename OuputIterator>
struct dump_ast
: karma::grammar<OuputIterator, expression_ast(), space_type>
{
dump_ast() : dump_ast::base_type(ast_node)
{
ast_node %= int_ | binary_node | unary_node;
binary_node %= '(' << ast_node << char_ << ast_node << ')';
unary_node %= '(' << char_ << ast_node << ')';
}
karma::rule<OuputIterator, expression_ast(), space_type> ast_node;
karma::rule<OuputIterator, binary_op(), space_type> binary_node;
karma::rule<OuputIterator, unary_op(), space_type> unary_node;
};
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Dump AST's for simple expressions...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
// Our parser grammar definitions
typedef std::string::const_iterator iterator_type;
typedef calculator<iterator_type> calculator;
calculator calc;
// Our generator grammar definitions
typedef std::back_insert_iterator<std::string> output_iterator_type;
typedef dump_ast<output_iterator_type> dump_ast;
dump_ast ast_grammar;
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
expression_ast ast;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
bool r = qi::phrase_parse(iter, end, calc, space, ast);
if (r && iter == end)
{
std::string generated;
output_iterator_type outit(generated);
r = karma::generate_delimited(outit, ast_grammar, space, ast);
if (r)
{
std::cout << "Got AST:" << std::endl << generated
<< std::endl;
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \": " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,171 @@
/*=============================================================================
Copyright (c) 2001-2010 Joel de Guzman
Copyright (c) 2001-2010 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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// A Calculator example demonstrating generation of AST which gets dumped into
// a reverse polish notation afterwards.
//
// [ JDG April 28, 2008 ]
// [ HK April 28, 2008 ]
//
///////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <vector>
#include <string>
#include "calc2_ast.hpp"
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
using namespace boost::spirit;
using namespace boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
// Our calculator parser grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct calculator
: qi::grammar<Iterator, expression_ast(), space_type>
{
calculator() : calculator::base_type(expression)
{
expression =
term [_val = _1]
>> *( ('+' >> term [_val += _1])
| ('-' >> term [_val -= _1])
)
;
term =
factor [_val = _1]
>> *( ('*' >> factor [_val *= _1])
| ('/' >> factor [_val /= _1])
)
;
factor =
uint_ [_val = _1]
| '(' >> expression [_val = _1] >> ')'
| ('-' >> factor [_val = neg(_1)])
| ('+' >> factor [_val = pos(_1)])
;
}
qi::rule<Iterator, expression_ast(), space_type> expression, term, factor;
};
// We need to tell fusion about our binary_op and unary_op structs
// to make them a first-class fusion citizen
//
// Note: we register the members exactly in the same sequence as we need them
// in the grammar
BOOST_FUSION_ADAPT_STRUCT(
binary_op,
(expression_ast, left)
(expression_ast, right)
(char, op)
)
BOOST_FUSION_ADAPT_STRUCT(
unary_op,
(expression_ast, right)
(char, op)
)
///////////////////////////////////////////////////////////////////////////////
// Our AST grammar for the generator, this prints the AST in reverse polish
// notation
///////////////////////////////////////////////////////////////////////////////
template <typename OuputIterator>
struct ast_rpn
: karma::grammar<OuputIterator, expression_ast(), space_type>
{
ast_rpn() : ast_rpn::base_type(ast_node)
{
ast_node %= int_ | binary_node | unary_node;
binary_node %= ast_node << ast_node << char_;
unary_node %= '(' << ast_node << char_ << ')';
}
karma::rule<OuputIterator, expression_ast(), space_type> ast_node;
karma::rule<OuputIterator, binary_op(), space_type> binary_node;
karma::rule<OuputIterator, unary_op(), space_type> unary_node;
};
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "RPN generator for simple expressions...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
// Our parser grammar definitions
typedef std::string::const_iterator iterator_type;
typedef calculator<iterator_type> calculator;
calculator calc;
// Our generator grammar definitions
typedef std::back_insert_iterator<std::string> output_iterator_type;
typedef ast_rpn<output_iterator_type> ast_rpn;
ast_rpn ast_grammar;
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
expression_ast ast; // this will hold the generated AST
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
bool r = qi::phrase_parse(iter, end, calc, space, ast);
if (r && iter == end)
{
std::string generated;
output_iterator_type outit(generated);
r = karma::generate_delimited(outit, ast_grammar, space, ast);
if (r)
{
std::cout << "RPN for '" << str << "': \n" << generated
<< std::endl;
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \": " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,247 @@
/*=============================================================================
Copyright (c) 2001-2010 Joel de Guzman
Copyright (c) 2001-2010 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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// A Calculator example demonstrating generation of AST from which we generate
// a simple byte code representation being interpreted by a similar virtual
// machine.
//
// [ JDG April 28, 2008 ]
// [ HK May 05, 2008 ]
//
///////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <vector>
#include <string>
#include "calc2_ast_vm.hpp"
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
using namespace boost::spirit;
using namespace boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
// Our calculator parser grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct calculator
: qi::grammar<Iterator, expression_ast(), space_type>
{
calculator() : calculator::base_type(expression)
{
expression =
term [_val = _1]
>> *( ('+' >> term [_val += _1])
| ('-' >> term [_val -= _1])
)
;
term =
factor [_val = _1]
>> *( ('*' >> factor [_val *= _1])
| ('/' >> factor [_val /= _1])
)
;
factor =
uint_ [_val = _1]
| '(' >> expression [_val = _1] >> ')'
| ('-' >> factor [_val = neg(_1)])
| ('+' >> factor [_val = pos(_1)])
;
}
qi::rule<Iterator, expression_ast(), space_type> expression, term, factor;
};
///////////////////////////////////////////////////////////////////////////////
// The Virtual Machine
///////////////////////////////////////////////////////////////////////////////
class vmachine
{
public:
union element {
int code;
char bytes[sizeof(int)];
};
vmachine(unsigned stackSize = 4096)
: stack(stackSize)
, stack_ptr(stack.begin())
{
}
int top() const { return stack_ptr[-1]; };
void execute(std::vector<element> const& code);
private:
std::vector<int> stack;
std::vector<int>::iterator stack_ptr;
};
void vmachine::execute(std::vector<element> const& code)
{
std::vector<element>::const_iterator pc = code.begin();
stack_ptr = stack.begin();
while ((*pc).code && pc != code.end())
{
switch ((*pc++).code)
{
case op_neg:
stack_ptr[-1] = -stack_ptr[-1];
break;
case op_add:
--stack_ptr;
stack_ptr[-1] += stack_ptr[0];
break;
case op_sub:
--stack_ptr;
stack_ptr[-1] -= stack_ptr[0];
break;
case op_mul:
--stack_ptr;
stack_ptr[-1] *= stack_ptr[0];
break;
case op_div:
--stack_ptr;
stack_ptr[-1] /= stack_ptr[0];
break;
case op_int:
*stack_ptr++ = (*pc++).code;
break;
}
}
}
// We need to tell fusion about our binary_op and unary_op structs
// to make them a first-class fusion citizen
//
// Note: we register the members exactly in the same sequence as we need them
// in the grammar
BOOST_FUSION_ADAPT_STRUCT(
binary_op,
(expression_ast, left)
(expression_ast, right)
(int, op)
)
BOOST_FUSION_ADAPT_STRUCT(
unary_op,
(expression_ast, right)
(int, op)
)
///////////////////////////////////////////////////////////////////////////////
// Our AST grammar for the generator, this just dumps the AST as a expression
///////////////////////////////////////////////////////////////////////////////
template <typename OuputIterator, typename Delimiter>
struct generate_byte_code
: karma::grammar<OuputIterator, expression_ast(), Delimiter>
{
generate_byte_code() : generate_byte_code::base_type(ast_node)
{
ast_node %= int_node | binary_node | unary_node;
int_node %= dword(op_int) << dword;
binary_node %= ast_node << ast_node << byte_;
unary_node %= ast_node << byte_;
}
karma::rule<OuputIterator, expression_ast(), Delimiter> ast_node;
karma::rule<OuputIterator, int(), Delimiter> int_node;
karma::rule<OuputIterator, binary_op(), Delimiter> binary_node;
karma::rule<OuputIterator, unary_op(), Delimiter> unary_node;
};
///////////////////////////////////////////////////////////////////////////////
// helper function helping to deduce the delimiter type
template <typename Delimiter>
bool generate_vm_code(expression_ast const& ast,
std::vector<vmachine::element>& code, Delimiter const& d)
{
// Our generator grammar definitions
typedef char* output_iterator_type;
typedef generate_byte_code<output_iterator_type, Delimiter> generate_byte_code;
char* outbuffer = (*code.begin()).bytes;
generate_byte_code gen_vm;
return karma::generate_delimited(outbuffer, gen_vm, d, ast);
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Compile simple expressions to bytecode...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
// Our parser grammar definitions
typedef std::string::const_iterator iterator_type;
typedef calculator<iterator_type> calculator;
calculator calc;
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
expression_ast ast;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
bool r = qi::phrase_parse(iter, end, calc, space, ast);
if (r && iter == end)
{
// we assume a vm code size of 4096 is sufficient
std::vector<vmachine::element> code (4096);
r = generate_vm_code(ast, code, pad(4));
if (r)
{
vmachine vm;
vm.execute(code);
std::cout << "\nresult = " << vm.top() << std::endl;
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \": " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,190 @@
/*=============================================================================
Copyright (c) 2001-2010 Joel de Guzman
Copyright (c) 2001-2010 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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// A Calculator example demonstrating generation of AST which gets dumped into
// a human readable format afterwards.
//
// [ JDG April 28, 2008 ]
// [ HK April 28, 2008 ]
//
///////////////////////////////////////////////////////////////////////////////
#if !defined(SPIRIT_EXAMPLE_CALC2_AST_APR_30_2008_1011AM)
#define SPIRIT_EXAMPLE_CALC2_AST_APR_30_2008_1011AM
#include <boost/variant.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/function.hpp>
#include <boost/phoenix/statement.hpp>
#include <boost/spirit/include/karma_domain.hpp>
#include <boost/spirit/include/support_attributes_fwd.hpp>
///////////////////////////////////////////////////////////////////////////////
// Our AST
///////////////////////////////////////////////////////////////////////////////
struct binary_op;
struct unary_op;
struct nil {};
struct expression_ast
{
typedef
boost::variant<
nil // can't happen!
, int
, boost::recursive_wrapper<binary_op>
, boost::recursive_wrapper<unary_op>
>
type;
// expose variant types
typedef type::types types;
// expose variant functionality
int which() const { return expr.which(); }
// constructors
expression_ast()
: expr(nil()) {}
expression_ast(unary_op const& expr)
: expr(expr) {}
expression_ast(binary_op const& expr)
: expr(expr) {}
expression_ast(unsigned int expr)
: expr(expr) {}
expression_ast(type const& expr)
: expr(expr) {}
expression_ast& operator+=(expression_ast const& rhs);
expression_ast& operator-=(expression_ast const& rhs);
expression_ast& operator*=(expression_ast const& rhs);
expression_ast& operator/=(expression_ast const& rhs);
type expr;
};
// expose variant functionality
namespace boost
{
// this function has to live in namespace boost for ADL to correctly find it
template <typename T>
inline T get(expression_ast const& expr)
{
return boost::get<T>(expr.expr);
}
// the specialization below tells Spirit to handle expression_ast as if it
// where a 'real' variant
namespace spirit { namespace traits
{
// the specialization below tells Spirit to handle expression_ast as
// if it where a 'real' variant (if used with Spirit.Karma)
template <>
struct not_is_variant<expression_ast, karma::domain>
: mpl::false_ {};
// the specialization of variant_which allows to generically extract
// the current type stored in the given variant like type
template <>
struct variant_which<expression_ast>
{
static int call(expression_ast const& v)
{
return v.which();
}
};
}}
}
enum byte_code
{
op_neg = 1, // negate the top stack entry
op_pos, // essentially a no-op (unary plus)
op_add, // add top two stack entries
op_sub, // subtract top two stack entries
op_mul, // multiply top two stack entries
op_div, // divide top two stack entries
op_int, // push constant integer into the stack
};
///////////////////////////////////////////////////////////////////////////////
struct binary_op
{
binary_op() {}
binary_op(
int op
, expression_ast const& left
, expression_ast const& right)
: op(op), left(left), right(right) {}
int op;
expression_ast left;
expression_ast right;
};
struct unary_op
{
unary_op(
int op
, expression_ast const& right)
: op(op), right(right) {}
int op;
expression_ast right;
};
inline expression_ast& expression_ast::operator+=(expression_ast const& rhs)
{
expr = binary_op(op_add, expr, rhs);
return *this;
}
inline expression_ast& expression_ast::operator-=(expression_ast const& rhs)
{
expr = binary_op(op_sub, expr, rhs);
return *this;
}
inline expression_ast& expression_ast::operator*=(expression_ast const& rhs)
{
expr = binary_op(op_mul, expr, rhs);
return *this;
}
inline expression_ast& expression_ast::operator/=(expression_ast const& rhs)
{
expr = binary_op(op_div, expr, rhs);
return *this;
}
// We should be using expression_ast::operator-. There's a bug
// in phoenix type deduction mechanism that prevents us from
// doing so. Phoenix will be switching to BOOST_TYPEOF. In the
// meantime, we will use a phoenix::function below:
template <char Op>
struct unary_expr
{
template <typename T>
struct result { typedef T type; };
expression_ast operator()(expression_ast const& expr) const
{
return unary_op(Op, expr);
}
};
boost::phoenix::function<unary_expr<op_pos> > pos;
boost::phoenix::function<unary_expr<op_neg> > neg;
#endif

View File

@@ -0,0 +1,102 @@
// Copyright (c) 2001-2010 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)
///////////////////////////////////////////////////////////////////////////////
//
// A character classification example
//
// [ HK August 12, 2009 ] spirit2
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <iostream>
#include <string>
#include <complex>
namespace client
{
///////////////////////////////////////////////////////////////////////////
// Our character classification generator
///////////////////////////////////////////////////////////////////////////
//[tutorial_karma_complex_number
template <typename OutputIterator>
bool classify_character(OutputIterator sink, char c)
{
using boost::spirit::ascii::char_;
using boost::spirit::ascii::digit;
using boost::spirit::ascii::xdigit;
using boost::spirit::ascii::alpha;
using boost::spirit::ascii::punct;
using boost::spirit::ascii::space;
using boost::spirit::ascii::cntrl;
using boost::spirit::karma::omit;
using boost::spirit::karma::generate;
if (!boost::spirit::char_encoding::ascii::isascii_(c))
return false;
return generate(sink,
// Begin grammar
(
"The character '" << char_ << "' is "
<< ( &digit << "a digit"
| &xdigit << "a xdigit"
| &alpha << "a alpha"
| &punct << "a punct"
| &space << "a space"
| &cntrl << "a cntrl"
| "of unknown type"
)
),
// End grammar
c, c
);
}
//]
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "\t\tA character classification micro generator for Spirit...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Give me a character to classify\n";
std::cout << "Type [q or Q] to quit\n\n";
std::string str;
while (getline(std::cin, str))
{
if (str.empty())
break;
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
if (!client::classify_character(sink, str[0]))
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << generated << "\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,131 @@
// Copyright (c) 2001-2010 Hartmut Kaiser
// Copyright (c) 2001-2010 Joel de Guzman
//
// 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)
///////////////////////////////////////////////////////////////////////////////
//
// A complex number micro generator.
//
// [ HK July 7, 2009 ] spirit2
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <iostream>
#include <string>
#include <complex>
namespace client
{
///////////////////////////////////////////////////////////////////////////
// Our complex number parser/compiler (that's just a copy of the complex
// number example from Qi (see examples/qi/complex_number.cpp)
///////////////////////////////////////////////////////////////////////////
template <typename Iterator>
bool parse_complex(Iterator first, Iterator last, std::complex<double>& c)
{
using boost::spirit::qi::double_;
using boost::spirit::qi::_1;
using boost::spirit::qi::phrase_parse;
using boost::spirit::ascii::space;
using boost::phoenix::ref;
double rN = 0.0;
double iN = 0.0;
bool r = phrase_parse(first, last,
(
'(' >> double_[ref(rN) = _1]
>> -(',' >> double_[ref(iN) = _1]) >> ')'
| double_[ref(rN) = _1]
),
space);
if (!r || first != last) // fail if we did not get a full match
return false;
c = std::complex<double>(rN, iN);
return r;
}
///////////////////////////////////////////////////////////////////////////
// Our complex number generator
///////////////////////////////////////////////////////////////////////////
//[tutorial_karma_complex_number
template <typename OutputIterator>
bool generate_complex(OutputIterator sink, std::complex<double> const& c)
{
using boost::spirit::karma::eps;
using boost::spirit::karma::double_;
using boost::spirit::karma::_1;
using boost::spirit::karma::generate;
return generate(sink,
// Begin grammar
(
eps(c.imag() != 0) <<
'(' << double_[_1 = c.real()] << ", " << double_[_1 = c.imag()] << ')'
| double_[_1 = c.real()]
)
// End grammar
);
}
//]
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "\t\tA complex number micro generator for Spirit...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Give me a complex number of the form r or (r) or (r,i) \n";
std::cout << "Type [q or Q] to quit\n\n";
std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::complex<double> c;
if (client::parse_complex(str.begin(), str.end(), c))
{
std::cout << "-------------------------\n";
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
if (!client::generate_complex(sink, c))
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Generated: " << generated << "\n";
std::cout << "-------------------------\n";
}
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,152 @@
// Copyright (c) 2001-2010 Hartmut Kaiser
// Copyright (c) 2001-2010 Joel de Guzman
//
// 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)
///////////////////////////////////////////////////////////////////////////////
//
// A complex number micro generator - take 3.
//
// Look'ma, still no semantic actions! And no explicit access to member
// functions any more.
//
// [ HK April 6, 2010 ] spirit2
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
#include <boost/spirit/include/support_adapt_adt_attributes.hpp>
#include <iostream>
#include <string>
#include <complex>
///////////////////////////////////////////////////////////////////////////////
// The following macro adapts the type std::complex<double> as a fusion
// sequence.
//[tutorial_karma_complex_number_adapt_class
// We can leave off the setters as Karma does not need them.
BOOST_FUSION_ADAPT_ADT(
std::complex<double>,
(bool, bool, obj.imag() != 0, /**/)
(double, double, obj.real(), /**/)
(double, double, obj.imag(), /**/)
)
//]
namespace client
{
///////////////////////////////////////////////////////////////////////////
// Our complex number parser/compiler (that's just a copy of the complex
// number example from Qi (see examples/qi/complex_number.cpp)
///////////////////////////////////////////////////////////////////////////
template <typename Iterator>
bool parse_complex(Iterator first, Iterator last, std::complex<double>& c)
{
using boost::spirit::qi::double_;
using boost::spirit::qi::_1;
using boost::spirit::qi::phrase_parse;
using boost::spirit::ascii::space;
using boost::phoenix::ref;
double rN = 0.0;
double iN = 0.0;
bool r = phrase_parse(first, last,
(
'(' >> double_[ref(rN) = _1]
>> -(',' >> double_[ref(iN) = _1]) >> ')'
| double_[ref(rN) = _1]
),
space);
if (!r || first != last) // fail if we did not get a full match
return false;
c = std::complex<double>(rN, iN);
return r;
}
///////////////////////////////////////////////////////////////////////////
// Our complex number generator
///////////////////////////////////////////////////////////////////////////
//[tutorial_karma_complex_number_adapt
template <typename OutputIterator>
bool generate_complex(OutputIterator sink, std::complex<double> const& c)
{
using boost::spirit::karma::double_;
using boost::spirit::karma::bool_;
using boost::spirit::karma::true_;
using boost::spirit::karma::omit;
using boost::spirit::karma::generate;
return generate(sink,
// Begin grammar
(
&true_ << '(' << double_ << ", " << double_ << ')'
| omit[bool_] << double_ << omit[double_]
),
// End grammar
c // Data to output
);
}
//]
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "\t\tA complex number micro generator for Spirit...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Give me a complex number of the form r or (r) or (r,i) \n";
std::cout << "Type [q or Q] to quit\n\n";
std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::complex<double> c;
if (client::parse_complex(str.begin(), str.end(), c))
{
std::cout << "-------------------------\n";
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
if (!client::generate_complex(sink, c))
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Generated: " << generated << "\n";
std::cout << "-------------------------\n";
}
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,132 @@
// Copyright (c) 2001-2010 Hartmut Kaiser
// Copyright (c) 2001-2010 Joel de Guzman
//
// 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)
///////////////////////////////////////////////////////////////////////////////
//
// A complex number micro generator - take 2. Look'ma no semantic actions!
//
// [ HK July 26, 2009 ] spirit2
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <iostream>
#include <string>
#include <complex>
namespace client
{
///////////////////////////////////////////////////////////////////////////
// Our complex number parser/compiler (that's just a copy of the complex
// number example from Qi (see examples/qi/complex_number.cpp)
///////////////////////////////////////////////////////////////////////////
template <typename Iterator>
bool parse_complex(Iterator first, Iterator last, std::complex<double>& c)
{
using boost::spirit::qi::double_;
using boost::spirit::qi::_1;
using boost::spirit::qi::phrase_parse;
using boost::spirit::ascii::space;
using boost::phoenix::ref;
double rN = 0.0;
double iN = 0.0;
bool r = phrase_parse(first, last,
(
'(' >> double_[ref(rN) = _1]
>> -(',' >> double_[ref(iN) = _1]) >> ')'
| double_[ref(rN) = _1]
),
space);
if (!r || first != last) // fail if we did not get a full match
return false;
c = std::complex<double>(rN, iN);
return r;
}
///////////////////////////////////////////////////////////////////////////
// Our complex number generator
///////////////////////////////////////////////////////////////////////////
//[tutorial_karma_complex_number_easier
template <typename OutputIterator>
bool generate_complex(OutputIterator sink, std::complex<double> const& c)
{
using boost::spirit::karma::double_;
using boost::spirit::karma::omit;
using boost::spirit::karma::generate;
return generate(sink,
// Begin grammar
(
!double_(0.0) << '(' << double_ << ", " << double_ << ')'
| omit[double_] << double_ << omit[double_]
),
// End grammar
c.imag(), c.real(), c.imag() // Data to output
);
}
//]
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "\t\tA complex number micro generator for Spirit...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Give me a complex number of the form r or (r) or (r,i) \n";
std::cout << "Type [q or Q] to quit\n\n";
std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::complex<double> c;
if (client::parse_complex(str.begin(), str.end(), c))
{
std::cout << "-------------------------\n";
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
if (!client::generate_complex(sink, c))
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Generated: " << generated << "\n";
std::cout << "-------------------------\n";
}
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,126 @@
/*=============================================================================
Copyright (c) 2001-2011 Hartmut Kaiser
http://spirit.sourceforge.net/
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)
=============================================================================*/
//[customize_karma_counter_includes
#include <boost/spirit/include/karma.hpp>
#include <iostream>
#include <vector>
//]
///////////////////////////////////////////////////////////////////////////////
//[customize_karma_counter_data
namespace client
{
struct counter
{
// expose the current value of the counter as our iterator
typedef int iterator;
// expose 'int' as the type of each generated element
typedef int type;
counter(int max_count)
: counter_(0), max_count_(max_count)
{}
int counter_;
int max_count_;
};
}
//]
//[customize_karma_counter_traits
// All specializations of attribute customization points have to be placed into
// the namespace boost::spirit::traits.
//
// Note that all templates below are specialized using the 'const' type.
// This is necessary as all attributes in Karma are 'const'.
namespace boost { namespace spirit { namespace traits
{
// The specialization of the template 'is_container<>' will tell the
// library to treat the type 'client::counter' as a container providing
// the items to generate output from.
template <>
struct is_container<client::counter const>
: mpl::true_
{};
// The specialization of the template 'container_iterator<>' will be
// invoked by the library to evaluate the iterator type to be used
// for iterating the data elements in the container.
template <>
struct container_iterator<client::counter const>
{
typedef client::counter::iterator type;
};
// The specialization of the templates 'begin_container<>' and
// 'end_container<>' below will be used by the library to get the iterators
// pointing to the begin and the end of the data to generate output from.
// These specializations respectively return the initial and maximum
// counter values.
//
// The passed argument refers to the attribute instance passed to the list
// generator.
template <>
struct begin_container<client::counter const>
{
static client::counter::iterator
call(client::counter const& c)
{
return c.counter_;
}
};
template <>
struct end_container<client::counter const>
{
static client::counter::iterator
call(client::counter const& c)
{
return c.max_count_;
}
};
}}}
//]
//[customize_karma_counter_iterator_traits
// All specializations of attribute customization points have to be placed into
// the namespace boost::spirit::traits.
namespace boost { namespace spirit { namespace traits
{
// The specialization of the template 'deref_iterator<>' will be used to
// dereference the iterator associated with our counter data structure.
// Since we expose the current value as the iterator we just return the
// current iterator as the return value.
template <>
struct deref_iterator<client::counter::iterator>
{
typedef client::counter::type type;
static type call(client::counter::iterator const& it)
{
return it;
}
};
}}}
//]
///////////////////////////////////////////////////////////////////////////////
namespace karma = boost::spirit::karma;
int main()
{
//[customize_karma_counter
// use the instance of a 'client::counter' instead of a STL vector
client::counter count(4);
std::cout << karma::format(karma::int_ % ", ", count) << std::endl; // prints: '0, 1, 2, 3'
//]
return 0;
}

View File

@@ -0,0 +1,107 @@
/*=============================================================================
Copyright (c) 2001-2011 Hartmut Kaiser
http://spirit.sourceforge.net/
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)
=============================================================================*/
//[customize_karma_embedded_container_includes
#include <boost/spirit/include/karma.hpp>
#include <iostream>
#include <vector>
//]
///////////////////////////////////////////////////////////////////////////////
//[customize_karma_embedded_container_data
namespace client
{
struct embedded_container
{
// expose the iterator of the embedded vector as our iterator
typedef std::vector<int>::const_iterator iterator;
// expose the type of the held data elements as our type
typedef std::vector<int>::value_type type;
// this is the vector holding the actual elements we need to generate
// output from
std::vector<int> data;
};
}
//]
//[customize_karma_embedded_container_traits
// All specializations of attribute customization points have to be placed into
// the namespace boost::spirit::traits.
//
// Note that all templates below are specialized using the 'const' type.
// This is necessary as all attributes in Karma are 'const'.
namespace boost { namespace spirit { namespace traits
{
// The specialization of the template 'is_container<>' will tell the
// library to treat the type 'client::embedded_container' as a
// container holding the items to generate output from.
template <>
struct is_container<client::embedded_container const>
: mpl::true_
{};
// The specialization of the template 'container_iterator<>' will be
// invoked by the library to evaluate the iterator type to be used
// for iterating the data elements in the container. We simply return
// the type of the iterator exposed by the embedded 'std::vector<int>'.
template <>
struct container_iterator<client::embedded_container const>
{
typedef client::embedded_container::iterator type;
};
// The specialization of the templates 'begin_container<>' and
// 'end_container<>' below will be used by the library to get the iterators
// pointing to the begin and the end of the data to generate output from.
// These specializations simply return the 'begin' and 'end' iterators as
// exposed by the embedded 'std::vector<int>'.
//
// The passed argument refers to the attribute instance passed to the list
// generator.
template <>
struct begin_container<client::embedded_container const>
{
static client::embedded_container::iterator
call(client::embedded_container const& d)
{
return d.data.begin();
}
};
template <>
struct end_container<client::embedded_container const>
{
static client::embedded_container::iterator
call(client::embedded_container const& d)
{
return d.data.end();
}
};
}}}
//]
///////////////////////////////////////////////////////////////////////////////
namespace karma = boost::spirit::karma;
int main()
{
//[customize_karma_embedded_container
client::embedded_container d1; // create some test data
d1.data.push_back(1);
d1.data.push_back(2);
d1.data.push_back(3);
// use the instance of an 'client::embedded_container' instead of a
// STL vector
std::cout << karma::format(karma::int_ % ", ", d1) << std::endl; // prints: '1, 2, 3'
//]
return 0;
}

View File

@@ -0,0 +1,163 @@
/*=============================================================================
Copyright (c) 2001-2011 Hartmut Kaiser
http://spirit.sourceforge.net/
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)
=============================================================================*/
//[customize_karma_use_as_container_includes
#include <boost/spirit/include/karma.hpp>
#include <iostream>
#include <string>
#include <vector>
//]
///////////////////////////////////////////////////////////////////////////////
//[customize_karma_use_as_container_data
namespace client
{
struct use_as_container
{
// Expose a pair holding a pointer to the use_as_container and to the
// current element as our iterator.
// We intentionally leave out having it a 'operator==()' to demonstrate
// the use of the 'compare_iterators' customization point.
struct iterator
{
iterator(use_as_container const* container, int const* current)
: container_(container), current_(current)
{}
use_as_container const* container_;
int const* current_;
};
// expose 'int' as the type of each generated element
typedef int type;
use_as_container(int value1, int value2, int value3)
: value1_(value1), value2_(value2), value3_(value3)
{}
int value1_;
std::string dummy1_; // insert some unrelated data
int value2_;
std::string dummy2_; // insert some more unrelated data
int value3_;
};
}
//]
//[customize_karma_use_as_container_traits
// All specializations of attribute customization points have to be placed into
// the namespace boost::spirit::traits.
//
// Note that all templates below are specialized using the 'const' type.
// This is necessary as all attributes in Karma are 'const'.
namespace boost { namespace spirit { namespace traits
{
// The specialization of the template 'is_container<>' will tell the
// library to treat the type 'client::use_as_container' as a
// container holding the items to generate output from.
template <>
struct is_container<client::use_as_container const>
: mpl::true_
{};
// The specialization of the template 'container_iterator<>' will be
// invoked by the library to evaluate the iterator type to be used
// for iterating the data elements in the container. We simply return
// the type of the iterator exposed by the embedded 'std::vector<int>'.
template <>
struct container_iterator<client::use_as_container const>
{
typedef client::use_as_container::iterator type;
};
// The specialization of the templates 'begin_container<>' and
// 'end_container<>' below will be used by the library to get the iterators
// pointing to the begin and the end of the data to generate output from.
//
// The passed argument refers to the attribute instance passed to the list
// generator.
template <>
struct begin_container<client::use_as_container const>
{
static client::use_as_container::iterator
call(client::use_as_container const& c)
{
return client::use_as_container::iterator(&c, &c.value1_);
}
};
template <>
struct end_container<client::use_as_container const>
{
static client::use_as_container::iterator
call(client::use_as_container const& c)
{
return client::use_as_container::iterator(&c, (int const*)0);
}
};
}}}
//]
//[customize_karma_use_as_container_iterator_traits
// All specializations of attribute customization points have to be placed into
// the namespace boost::spirit::traits.
namespace boost { namespace spirit { namespace traits
{
// The specialization of the template 'deref_iterator<>' will be used to
// dereference the iterator associated with our counter data structure.
template <>
struct deref_iterator<client::use_as_container::iterator>
{
typedef client::use_as_container::type type;
static type call(client::use_as_container::iterator const& it)
{
return *it.current_;
}
};
template <>
struct next_iterator<client::use_as_container::iterator>
{
static void call(client::use_as_container::iterator& it)
{
if (it.current_ == &it.container_->value1_)
it.current_ = &it.container_->value2_;
else if (it.current_ == &it.container_->value2_)
it.current_ = &it.container_->value3_;
else
it.current_ = 0;
}
};
template <>
struct compare_iterators<client::use_as_container::iterator>
{
static bool call(client::use_as_container::iterator const& it1
, client::use_as_container::iterator const& it2)
{
return it1.current_ == it2.current_ &&
it1.container_ == it2.container_;
}
};
}}}
//]
///////////////////////////////////////////////////////////////////////////////
namespace karma = boost::spirit::karma;
int main()
{
//[customize_karma_use_as_container
client::use_as_container d2 (1, 2, 3);
// use the instance of a 'client::use_as_container' instead of a STL vector
std::cout << karma::format(karma::int_ % ", ", d2) << std::endl; // prints: '1, 2, 3'
//]
return 0;
}

View File

@@ -0,0 +1,65 @@
// Copyright (c) 2001-2010 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)
// The purpose of this example is to show how any character sequence can be
// printed while being properly quoted.
#include <boost/spirit/include/karma.hpp>
namespace client
{
namespace karma = boost::spirit::karma;
template <typename OutputIterator>
struct escaped_string
: karma::grammar<OutputIterator, std::string(char const*)>
{
escaped_string()
: escaped_string::base_type(esc_str)
{
esc_char.add('\a', "\\a")('\b', "\\b")('\f', "\\f")('\n', "\\n")
('\r', "\\r")('\t', "\\t")('\v', "\\v")('\\', "\\\\")
('\'', "\\\'")('\"', "\\\"")
;
esc_str = karma::lit(karma::_r1)
<< *(esc_char | karma::print | "\\x" << karma::hex)
<< karma::lit(karma::_r1)
;
}
karma::rule<OutputIterator, std::string(char const*)> esc_str;
karma::symbols<char, char const*> esc_char;
};
}
///////////////////////////////////////////////////////////////////////////////
int main()
{
namespace karma = boost::spirit::karma;
typedef std::back_insert_iterator<std::string> sink_type;
std::string generated;
sink_type sink(generated);
std::string str("string to escape: \n\r\t\"'\x19");
char const* quote = "'''";
client::escaped_string<sink_type> g;
if (!karma::generate(sink, g(quote), str))
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Generated: " << generated << "\n";
std::cout << "-------------------------\n";
}
return 0;
}

View File

@@ -0,0 +1,79 @@
// Copyright (c) 2001-2010 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)
///////////////////////////////////////////////////////////////////////////////
//
// Several small snippets generating different C++ code constructs
//
// [ HK October 08, 2009 ] Spirit V2.2
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/spirit/include/karma.hpp>
#include <boost/phoenix.hpp>
#include <iostream>
#include <string>
#include <complex>
namespace client
{
namespace karma = boost::spirit::karma;
namespace phoenix = boost::phoenix;
// create for instance: int name[5] = { 1, 2, 3, 4, 5 };
template <typename Iterator>
struct int_array : karma::grammar<Iterator, std::vector<int>()>
{
int_array(char const* name) : int_array::base_type(start)
{
using karma::int_;
using karma::uint_;
using karma::eol;
using karma::lit;
using karma::_val;
using karma::_r1;
start = array_def(phoenix::size(_val)) << " = " << initializer
<< ';' << eol;
array_def = "int " << lit(name) << "[" << uint_(_r1) << "]";
initializer = "{ " << -(int_ % ", ") << " }";
}
karma::rule<Iterator, void(unsigned)> array_def;
karma::rule<Iterator, std::vector<int>()> initializer;
karma::rule<Iterator, std::vector<int>()> start;
};
typedef std::back_insert_iterator<std::string> iterator_type;
bool generate_array(char const* name, std::vector<int> const& v)
{
std::string generated;
iterator_type sink(generated);
int_array<iterator_type> g(name);
if (karma::generate(sink, g, v))
{
std::cout << generated;
return true;
}
return false;
}
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int main()
{
// generate an array of integers with initializers
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
client::generate_array("array1", v);
return 0;
}

View File

@@ -0,0 +1,73 @@
// Copyright (c) 2001-2010 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)
// The main purpose of this example is to show how we can generate output from
// a container holding key/value pairs.
//
// For more information see here: http://spirit.sourceforge.net/home/?p=400
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/karma_stream.hpp>
#include <boost/phoenix.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdlib>
namespace client
{
namespace karma = boost::spirit::karma;
typedef std::pair<std::string, boost::optional<std::string> > pair_type;
template <typename OutputIterator>
struct keys_and_values
: karma::grammar<OutputIterator, std::vector<pair_type>()>
{
keys_and_values()
: keys_and_values::base_type(query)
{
query = pair << *('&' << pair);
pair = karma::string << -('=' << karma::string);
}
karma::rule<OutputIterator, std::vector<pair_type>()> query;
karma::rule<OutputIterator, pair_type()> pair;
};
}
///////////////////////////////////////////////////////////////////////////////
int main()
{
namespace karma = boost::spirit::karma;
typedef std::vector<client::pair_type>::value_type value_type;
typedef std::back_insert_iterator<std::string> sink_type;
std::vector<client::pair_type> v;
v.push_back(value_type("key1", boost::optional<std::string>("value1")));
v.push_back(value_type("key2", boost::optional<std::string>()));
v.push_back(value_type("key3", boost::optional<std::string>("")));
std::string generated;
sink_type sink(generated);
client::keys_and_values<sink_type> g;
if (!karma::generate(sink, g, v))
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Generated: " << generated << "\n";
std::cout << "-------------------------\n";
}
return 0;
}

View File

@@ -0,0 +1,220 @@
/*=============================================================================
Copyright (c) 2001-2010 Joel de Guzman
Copyright (c) 2001-2010 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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// A mini XML-like parser, Karma is used to print out the generated AST
//
// [ JDG March 25, 2007 ] spirit2
// [ HK April 02, 2007 ] spirit2
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/fusion.hpp>
#include <boost/phoenix/function.hpp>
#include <boost/phoenix/stl.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace boost::spirit;
using namespace boost::spirit::ascii;
namespace fusion = boost::fusion;
namespace phoenix = boost::phoenix;
using phoenix::at_c;
using phoenix::push_back;
///////////////////////////////////////////////////////////////////////////////
// Our mini XML tree representation
///////////////////////////////////////////////////////////////////////////////
struct mini_xml;
typedef
boost::variant<
boost::recursive_wrapper<mini_xml>
, std::string
>
mini_xml_node;
struct mini_xml
{
std::string name; // tag name
std::vector<mini_xml_node> children; // children
};
// We need to tell fusion about our mini_xml struct
// to make it a first-class fusion citizen
BOOST_FUSION_ADAPT_STRUCT(
mini_xml,
(std::string, name)
(std::vector<mini_xml_node>, children)
)
///////////////////////////////////////////////////////////////////////////////
// Our mini XML grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct mini_xml_parser :
qi::grammar<Iterator, mini_xml(), space_type>
{
mini_xml_parser() : mini_xml_parser::base_type(xml)
{
text = lexeme[+(char_ - '<') [_val += _1]];
node = (xml | text) [_val = _1];
start_tag =
'<'
>> !lit('/')
>> lexeme[+(char_ - '>') [_val += _1]]
>> '>'
;
end_tag =
"</"
>> lit(_r1)
>> '>'
;
xml =
start_tag [at_c<0>(_val) = _1]
>> *node [push_back(at_c<1>(_val), _1)]
>> end_tag(at_c<0>(_val))
;
}
qi::rule<Iterator, mini_xml(), space_type> xml;
qi::rule<Iterator, mini_xml_node(), space_type> node;
qi::rule<Iterator, std::string(), space_type> text;
qi::rule<Iterator, std::string(), space_type> start_tag;
qi::rule<Iterator, void(std::string), space_type> end_tag;
};
///////////////////////////////////////////////////////////////////////////////
// A couple of phoenix functions helping to access the elements of the
// generated AST
///////////////////////////////////////////////////////////////////////////////
template <typename T>
struct get_element
{
template <typename T1>
struct result { typedef T const& type; };
T const& operator()(mini_xml_node const& node) const
{
return boost::get<T>(node);
}
};
phoenix::function<get_element<std::string> > _string;
phoenix::function<get_element<mini_xml> > _xml;
///////////////////////////////////////////////////////////////////////////////
// The output grammar defining the format of the generated data
///////////////////////////////////////////////////////////////////////////////
template <typename OutputIterator>
struct mini_xml_generator
: karma::grammar<OutputIterator, mini_xml()>
{
mini_xml_generator() : mini_xml_generator::base_type(xml)
{
node %= string | xml;
xml =
'<' << string[_1 = at_c<0>(_val)] << '>'
<< (*node)[_1 = at_c<1>(_val)]
<< "</" << string[_1 = at_c<0>(_val)] << '>'
;
}
karma::rule<OutputIterator, mini_xml()> xml;
karma::rule<OutputIterator, mini_xml_node()> node;
};
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
char const* filename;
if (argc > 1)
{
filename = argv[1];
}
else
{
std::cerr << "Error: No input file provided." << std::endl;
return 1;
}
std::ifstream in(filename, std::ios_base::in);
if (!in)
{
std::cerr << "Error: Could not open input file: "
<< filename << std::endl;
return 1;
}
std::string storage; // We will read the contents here.
in.unsetf(std::ios::skipws); // No white space skipping!
std::copy(
std::istream_iterator<char>(in),
std::istream_iterator<char>(),
std::back_inserter(storage));
typedef mini_xml_parser<std::string::const_iterator> mini_xml_parser;
mini_xml_parser xmlin; // Our grammar definition
mini_xml ast; // our tree
std::string::const_iterator iter = storage.begin();
std::string::const_iterator end = storage.end();
bool r = qi::phrase_parse(iter, end, xmlin, space, ast);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
typedef std::back_insert_iterator<std::string> outiter_type;
typedef mini_xml_generator<outiter_type> mini_xml_generator;
mini_xml_generator xmlout; // Our grammar definition
std::string generated;
outiter_type outit(generated);
bool r = karma::generate(outit, xmlout, ast);
if (r)
std::cout << generated << std::endl;
return 0;
}
else
{
std::string::const_iterator begin = storage.begin();
std::size_t dist = std::distance(begin, iter);
std::string::const_iterator some =
iter + (std::min)(storage.size()-dist, std::size_t(30));
std::string context(iter, some);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \": " << context << "...\"\n";
std::cout << "-------------------------\n";
return 1;
}
}

View File

@@ -0,0 +1,120 @@
/*=============================================================================
Copyright (c) 2002-2010 Hartmut Kaiser
Copyright (c) 2002-2010 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// This sample demonstrates a generator for a comma separated list of numbers.
// No actions. It is based on the example qi/num_lists.cpp for reading in
// some numbers to generate.
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <iostream>
#include <string>
#include <list>
namespace client
{
namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace ascii = boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////
// Our number list parser, please see the example qi/numlist1.cpp for
// more information
///////////////////////////////////////////////////////////////////////////
template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last, std::list<double>& v)
{
using qi::double_;
using qi::phrase_parse;
using ascii::space;
bool r = phrase_parse(first, last, double_ >> *(',' >> double_), space, v);
if (first != last)
return false;
return r;
}
///////////////////////////////////////////////////////////////////////////
// Our number list generator
///////////////////////////////////////////////////////////////////////////
//[tutorial_karma_numlist1
template <typename OutputIterator>
bool generate_numbers(OutputIterator& sink, std::list<double> const& v)
{
using karma::double_;
using karma::generate_delimited;
using ascii::space;
bool r = generate_delimited(
sink, // destination: output iterator
double_ << *(',' << double_), // the generator
space, // the delimiter-generator
v // the data to output
);
return r;
}
//]
}
////////////////////////////////////////////////////////////////////////////
// Main program
////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "\t\tA comma separated list generator for Spirit...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Give me a comma separated list of numbers.\n";
std::cout << "Type [q or Q] to quit\n\n";
std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::list<double> v; // here we put the data to generate
if (client::parse_numbers(str.begin(), str.end(), v))
{
// ok, we got some numbers, now print them back out
std::cout << "-------------------------\n";
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
if (!client::generate_numbers(sink, v))
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Generated: " << generated << "\n";
std::cout << "-------------------------\n";
}
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,116 @@
/*=============================================================================
Copyright (c) 2002-2010 Hartmut Kaiser
Copyright (c) 2002-2010 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// This sample demonstrates a generator for a comma separated list of numbers.
// No actions. It is based on the example qi/num_lists.cpp for reading in
// some numbers to generate.
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace client
{
///////////////////////////////////////////////////////////////////////////
// Our number list parser, please see the example qi/numlist1.cpp for
// more information
///////////////////////////////////////////////////////////////////////////
template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last, std::vector<double>& v)
{
using boost::spirit::qi::double_;
using boost::spirit::qi::phrase_parse;
using boost::spirit::ascii::space;
bool r = phrase_parse(first, last, double_ % ',', space, v);
if (first != last)
return false;
return r;
}
///////////////////////////////////////////////////////////////////////////
// Our number list generator
///////////////////////////////////////////////////////////////////////////
//[tutorial_karma_numlist2
template <typename OutputIterator, typename Container>
bool generate_numbers(OutputIterator& sink, Container const& v)
{
using boost::spirit::karma::double_;
using boost::spirit::karma::generate_delimited;
using boost::spirit::ascii::space;
bool r = generate_delimited(
sink, // destination: output iterator
double_ % ',', // the generator
space, // the delimiter-generator
v // the data to output
);
return r;
}
//]
}
////////////////////////////////////////////////////////////////////////////
// Main program
////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "\t\tA comma separated list generator for Spirit...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Give me a comma separated list of numbers.\n";
std::cout << "Type [q or Q] to quit\n\n";
std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::vector<double> v; // here we put the data to generate
if (client::parse_numbers(str.begin(), str.end(), v))
{
// ok, we got some numbers, now print them back out
std::cout << "-------------------------\n";
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
if (!client::generate_numbers(sink, v))
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Generated: " << generated << "\n";
std::cout << "-------------------------\n";
}
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,149 @@
/*=============================================================================
Copyright (c) 2002-2010 Hartmut Kaiser
Copyright (c) 2002-2010 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// This sample demonstrates a generator for a comma separated list of numbers.
// No actions. It is based on the example qi/num_lists.cpp for reading in
// some numbers to generate.
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace client
{
///////////////////////////////////////////////////////////////////////////
// Our number list parser, please see the example qi/numlist1.cpp for
// more information
///////////////////////////////////////////////////////////////////////////
template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last, std::vector<double>& v)
{
using boost::spirit::qi::double_;
using boost::spirit::qi::phrase_parse;
using boost::spirit::ascii::space;
bool r = phrase_parse(first, last, double_ % ',', space, v);
if (first != last)
return false;
return r;
}
//[tutorial_karma_numlist3_complex
// a simple complex number representation z = a + bi
struct complex
{
complex (double a, double b = 0.0) : a(a), b(b) {}
double a;
double b;
};
// the streaming operator for the type complex
std::ostream&
operator<< (std::ostream& os, complex const& z)
{
os << "{" << z.a << "," << z.b << "}";
return os;
}
//]
///////////////////////////////////////////////////////////////////////////
// Our number list generator
///////////////////////////////////////////////////////////////////////////
//[tutorial_karma_numlist3
template <typename OutputIterator, typename Container>
bool generate_numbers(OutputIterator& sink, Container const& v)
{
using boost::spirit::karma::stream;
using boost::spirit::karma::generate;
using boost::spirit::karma::eol;
bool r = generate(
sink, // destination: output iterator
stream % eol, // the generator
v // the data to output
);
return r;
}
//]
}
////////////////////////////////////////////////////////////////////////////
// Main program
////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "\tA comma separated list generator for Spirit...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Give me a comma separated list of numbers.\n";
std::cout << "Type [q or Q] to quit\n\n";
std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::vector<double> v; // here we put the data gotten from input
if (client::parse_numbers(str.begin(), str.end(), v))
{
// ok, we got some numbers, fill a vector of client::complex
// instances and print them back out
std::vector<client::complex> vc;
std::vector<double>::const_iterator end = v.end();
for (std::vector<double>::const_iterator it = v.begin();
it != end; ++it)
{
double real(*it);
if (++it != end)
vc.push_back(client::complex(real, *it));
else {
vc.push_back(client::complex(real));
break;
}
}
std::cout << "-------------------------\n";
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
if (!client::generate_numbers(sink, vc))
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Generated:\n" << generated << "\n";
std::cout << "-------------------------\n";
}
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,113 @@
/*=============================================================================
Copyright (c) 2002-2010 Hartmut Kaiser
Copyright (c) 2002-2010 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// This sample demonstrates a generator formatting and printing a matrix
// of integers taken from a simple vector of vectors. The size and the
// contents of the printed matrix is generated randomly.
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/spirit/include/karma.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <ctime>
namespace karma = boost::spirit::karma;
namespace client
{
///////////////////////////////////////////////////////////////////////////
// Our matrix generator
///////////////////////////////////////////////////////////////////////////
//[tutorial_karma_nummatrix_grammar
template <typename OutputIterator>
struct matrix_grammar
: karma::grammar<OutputIterator, std::vector<std::vector<int> >()>
{
matrix_grammar()
: matrix_grammar::base_type(matrix)
{
using karma::int_;
using karma::right_align;
using karma::eol;
element = right_align(10)[int_];
row = '|' << *element << '|';
matrix = row % eol;
}
karma::rule<OutputIterator, std::vector<std::vector<int> >()> matrix;
karma::rule<OutputIterator, std::vector<int>()> row;
karma::rule<OutputIterator, int()> element;
};
//]
//[tutorial_karma_nummatrix
template <typename OutputIterator>
bool generate_matrix(OutputIterator& sink
, std::vector<std::vector<int> > const& v)
{
matrix_grammar<OutputIterator> matrix;
return karma::generate(
sink, // destination: output iterator
matrix, // the generator
v // the data to output
);
}
//]
}
////////////////////////////////////////////////////////////////////////////
// Main program
////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "\tPrinting integers in a matrix using Spirit...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
// here we put the data to generate
std::vector<std::vector<int> > v;
// now, generate the size and the contents for the matrix
std::srand((unsigned int)std::time(NULL));
std::size_t rows = std::rand() / (RAND_MAX / 10);
std::size_t columns = std::rand() / (RAND_MAX / 10);
v.resize(rows);
for (std::size_t row = 0; row < rows; ++row)
{
v[row].resize(columns);
std::generate(v[row].begin(), v[row].end(), std::rand);
}
// ok, we got the matrix, now print it out
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
if (!client::generate_matrix(sink, v))
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Generated:\n" << generated << "\n";
std::cout << "-------------------------\n";
}
return 0;
}

View File

@@ -0,0 +1,281 @@
// Copyright (c) 2001-2010 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)
// The main purpose of this example is to show how a single container type can
// be formatted using different output grammars.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <cmath>
using namespace boost::spirit;
///////////////////////////////////////////////////////////////////////////////
// This policy allows to use printf style formatting specifiers for Karma
// floating point generators. This policy understands the following format:
//
// The format string must conform to the following format, otherwise a
// std::runtime_error will be thrown:
//
// %[flags][fill][width][.precision]type
//
// where:
// flags (only one possible):
// +: Always denote the sign '+' or '-' of a number
// -: Left-align the output
// fill:
// 0: Uses 0 instead of spaces to left-fill a fixed-length field
// width:
// number: Left-pad the output with spaces until it is at least number
// characters wide. if number has a leading '0', that is
// interpreted as a 'fill', the padding is done with '0'
// characters instead of spaces.
// precision:
// number: Causes the decimal portion of the output to be expressed
// in at least number digits
// type (only one possible):
// e: force scientific notation, with a lowercase "e"
// E: force scientific notation, with a uppercase "E"
// f: floating point format
// g: use %e or %f, whichever is shorter
// G: use %E or %f, whichever is shorter
//
///////////////////////////////////////////////////////////////////////////////
// define a data structure and a corresponding parser to hold the formatting
// information extracted from the format specification string
namespace client
{
struct format_data
{
char flag;
char fill;
int width;
int precision;
char type;
};
}
// We need to tell fusion about our format_data struct
// to make it a first-class fusion citizen
BOOST_FUSION_ADAPT_STRUCT(
client::format_data,
(char, flag)
(char, fill)
(int, width)
(int, precision)
(char, type)
)
namespace client
{
///////////////////////////////////////////////////////////////////////////
// Grammar for format specification string as described above
template <typename Iterator>
struct format_grammar : qi::grammar<Iterator, format_data()>
{
format_grammar() : format_grammar::base_type(format)
{
using qi::uint_;
using qi::attr;
using ascii::char_;
using ascii::no_case;
format %= '%' >> flags >> fill >> width >> prec >> type;
// default flags is right aligned
flags = char_('+') | char_('-') | attr(' ');
fill = char_('0') | attr(' '); // default fill is space
width = uint_ | attr(-1);
prec = '.' >> uint_ | attr(3); // default is 3 digits
type = no_case[char_('e')] | char_('f') | no_case[char_('g')];
};
qi::rule<Iterator, format_data()> format;
qi::rule<Iterator, char()> flags;
qi::rule<Iterator, char()> fill;
qi::rule<Iterator, int()> width;
qi::rule<Iterator, int()> prec;
qi::rule<Iterator, char()> type;
};
}
///////////////////////////////////////////////////////////////////////////////
// real_policies implementation allowing to use a printf style format
// specification for Karma floating pointing number generators
template <typename T>
struct format_policies : karma::real_policies<T>
{
typedef karma::real_policies<T> base_policy_type;
///////////////////////////////////////////////////////////////////////////
// This real_policies implementation requires the output_iterator to
// implement buffering and character counting. This needs to be reflected
// in the properties exposed by the generator
typedef boost::mpl::int_<
karma::generator_properties::countingbuffer
> properties;
///////////////////////////////////////////////////////////////////////////
format_policies(char const* fmt = "%f")
{
char const* last = fmt;
while (*last)
last++;
client::format_grammar<char const*> g;
if (!qi::parse(fmt, last, g, format_))
throw std::runtime_error("bad format string");
}
///////////////////////////////////////////////////////////////////////////
// returns the overall format: scientific or fixed
int floatfield(T n) const
{
if (format_.type == 'e' || format_.type == 'E')
return base_policy_type::fmtflags::scientific;
if (format_.type == 'f')
return base_policy_type::fmtflags::fixed;
BOOST_ASSERT(format_.type == 'g' || format_.type == 'G');
return this->base_policy_type::floatfield(n);
}
///////////////////////////////////////////////////////////////////////////
// returns whether to emit a sign even for non-negative numbers
bool const force_sign(T) const
{
return format_.flag == '+';
}
///////////////////////////////////////////////////////////////////////////
// returns the number of required digits for the fractional part
unsigned precision(T) const
{
return format_.precision;
}
///////////////////////////////////////////////////////////////////////////
// emit the decimal dot
template <typename OutputIterator>
static bool dot (OutputIterator& sink, T n, unsigned precision)
{
// don't print the dot if no fractional digits are to be emitted
if (precision == 0)
return true;
return base_policy_type::dot(sink, n, precision);
}
template <typename CharEncoding, typename Tag, typename OutputIterator>
bool exponent (OutputIterator& sink, long n) const
{
if (format_.type == 'E' || format_.type == 'G') {
// print exponent symbol in upper case
return this->base_policy_type::
template exponent<char_encoding::ascii, tag::upper>(sink, n);
}
return this->base_policy_type::
template exponent<CharEncoding, Tag>(sink, n);
}
///////////////////////////////////////////////////////////////////////////
// this gets called by the numeric generators at the top level, it allows
// to do alignment and other high level things
template <typename Inserter, typename OutputIterator, typename Policies>
bool call (OutputIterator& sink, T n, Policies const& p) const
{
bool r = false;
if (format_.flag == '-') { // left align
// wrap the given output iterator to allow counting
karma::detail::enable_counting<OutputIterator> counting(sink);
// first generate the actual floating point number
r = Inserter::call_n(sink, n, p);
// pad the output until the max width is reached
while(r && int(counting.count()) < format_.width)
r = karma::generate(sink, ' ');
}
else { // right align
// wrap the given output iterator to allow left padding
karma::detail::enable_buffering<OutputIterator> buffering(
sink, format_.width);
// first generate the actual floating point number
{
karma::detail::disable_counting<OutputIterator> nocounting(sink);
r = Inserter::call_n(sink, n, p);
}
buffering.disable(); // do not perform buffering any more
// generate the left padding
karma::detail::enable_counting<OutputIterator> counting(
sink, buffering.buffer_size());
while(r && int(counting.count()) < format_.width)
r = karma::generate(sink, format_.fill);
// copy the buffered output to the target output iterator
if (r)
buffering.buffer_copy();
}
return r;
}
client::format_data format_;
};
///////////////////////////////////////////////////////////////////////////////
// This is the generator usable in any Karma output format expression, it needs
// to be utilized as
//
// generate(sink, real("%6.3f"), 3.1415926536); // prints: ' 3.142'
//
// and it supports the format specification as described above.
typedef karma::real_generator<double, format_policies<double> > real;
///////////////////////////////////////////////////////////////////////////////
int main()
{
std::cout << "/////////////////////////////////////////////////////////////\n\n";
std::cout << "A format driven floating point number generator for Spirit...\n\n";
std::cout << "/////////////////////////////////////////////////////////////\n\n";
std::cout << "Give me a printf style format\n";
std::cout << "Type [enter] to quit\n\n";
std::string str;
while (getline(std::cin, str))
{
if (str.empty())
break;
try {
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
if (!karma::generate(sink, real(str.c_str()), 4*std::atan(1.0)))
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << ">" << generated << "<\n";
}
}
catch (std::runtime_error const&) {
std::cout << "-------------------------\n";
std::cout << "Invalid format specified!\n";
std::cout << "-------------------------\n";
}
}
return 0;
}

View File

@@ -0,0 +1,118 @@
// Copyright (c) 2001-2010 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)
// The main purpose of this example is to show how a single container type can
// be formatted using different output grammars.
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/karma_stream.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
using namespace boost::spirit;
using namespace boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
int main()
{
///////////////////////////////////////////////////////////////////////////
// vector
std::vector<int> v (8);
std::generate(v.begin(), v.end(), std::rand); // randomly fill the vector
std::cout << "Output 8 integers from a std::vector<int>..." << std::endl;
// output the container as a sequence without any separation
std::cout << "...without any separation" << std::endl;
std::cout <<
karma::format(
*int_, // format description
v // data
) << std::endl << std::endl;
// output the container as a space separated sequence
std::cout << "...as space delimited list" << std::endl;
std::cout <<
karma::format_delimited(
*int_, // format description
space, // delimiter
v // data
) << std::endl << std::endl;
std::cout <<
karma::format_delimited(
'[' << *int_ << ']', // format description
space, // delimiter
v // data
) << std::endl << std::endl;
// output the container as a comma separated list
std::cout << "...as comma separated list" << std::endl;
std::cout <<
karma::format(
int_ % ", ", // format description
v // data
) << std::endl << std::endl;
std::cout <<
karma::format(
'[' << (int_ % ", ") << ']', // format description
v // data
) << std::endl << std::endl;
// output the container as a comma separated list of double's
std::cout << "...as comma separated list of doubles" << std::endl;
std::cout <<
karma::format(
double_ % ", ", // format description
v // data
) << std::endl << std::endl;
// output the container as a comma separated list of items enclosed in '()'
std::cout << "..as list of ints enclosed in '()'" << std::endl;
std::cout <<
karma::format(
('(' << int_ << ')') % ", ", // format description
v // data
) << std::endl << std::endl;
std::cout <<
karma::format(
'[' << (
('(' << int_ << ')') % ", "
) << ']', // format description
v // data
) << std::endl << std::endl;
// output the container as a HTML list
std::cout << "...as HTML bullet list" << std::endl;
std::cout <<
karma::format_delimited(
"<ol>" <<
// no delimiting within verbatim
*verbatim[" <li>" << int_ << "</li>"]
<< "</ol>", // format description
'\n', // delimiter
v // data
) << std::endl;
// output the container as right aligned column
std::cout << "...right aligned in a column" << std::endl;
std::cout <<
karma::format_delimited(
*verbatim[
"|" << right_align[int_] << "|"
], // format description
'\n', // delimiter
v // data
) << std::endl;
std::cout << std::endl;
return 0;
}

View File

@@ -0,0 +1,68 @@
// Copyright (c) 2001-2010 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)
// The purpose of this example is to demonstrate how to utilize alternatives
// and the built in matching capabilities of Karma generators to emit output
// in different formats based on the content of an attribute (not its type).
#include <string>
#include <vector>
#include <boost/spirit/include/karma.hpp>
#include <boost/phoenix/stl.hpp>
namespace client
{
namespace karma = boost::spirit::karma;
namespace phx = boost::phoenix;
template <typename OutputIterator>
struct quoted_strings
: karma::grammar<OutputIterator, std::vector<std::string>()>
{
quoted_strings()
: quoted_strings::base_type(strings)
{
strings = (bareword | qstring) % ' ';
bareword = karma::repeat(phx::size(karma::_val))
[ karma::alnum | karma::char_("-.,_$") ];
qstring = '"' << karma::string << '"';
}
karma::rule<OutputIterator, std::vector<std::string>()> strings;
karma::rule<OutputIterator, std::string()> bareword, qstring;
};
}
int main()
{
namespace karma = boost::spirit::karma;
typedef std::back_insert_iterator<std::string> sink_type;
std::string generated;
sink_type sink(generated);
std::vector<std::string> v;
v.push_back("foo");
v.push_back("bar baz");
v.push_back("hello");
client::quoted_strings<sink_type> g;
if (!karma::generate(sink, g, v))
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Generated: " << generated << "\n";
std::cout << "-------------------------\n";
}
return 0;
}

View File

@@ -0,0 +1,911 @@
/*=============================================================================
Copyright (c) 2001-2011 Hartmut Kaiser
http://spirit.sourceforge.net/
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)
=============================================================================*/
//[reference_karma_includes
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/support_utree.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/proto/deep_copy.hpp>
#include <iostream>
#include <string>
//]
//[reference_karma_includes_simple
#include <boost/spirit/include/karma.hpp>
#include <iostream>
#include <string>
//]
//[reference_karma_output_iterator
typedef std::back_insert_iterator<std::string> output_iterator_type;
//]
//[reference_karma_test
template <typename G>
void test_generator(char const* expected, G const& g)
{
std::string s;
std::back_insert_iterator<std::string> out(s);
if (boost::spirit::karma::generate(out, g) && s == expected)
std::cout << "ok" << std::endl;
else
std::cout << "fail" << std::endl;
}
//]
//[reference_karma_test_attr
template <typename G, typename T>
void test_generator_attr(char const* expected, G const& g, T const& attr)
{
std::string s;
std::back_insert_iterator<std::string> out(s);
if (boost::spirit::karma::generate(out, g, attr) && s == expected)
std::cout << "ok" << std::endl;
else
std::cout << "fail" << std::endl;
}
//]
//[reference_karma_test_attr2
template <typename G, typename T1, typename T2>
void test_generator_attr(char const* expected, G const& g, T1 const& attr1,
T2 const& attr2)
{
std::string s;
std::back_insert_iterator<std::string> out(s);
if (boost::spirit::karma::generate(out, g, attr1, attr2) && s == expected)
std::cout << "ok" << std::endl;
else
std::cout << "fail" << std::endl;
}
//]
//[reference_karma_test_attr_delim
template <typename G, typename Delimiter, typename T>
void test_generator_attr_delim(char const* expected, G const& g, Delimiter const& d, T const& attr)
{
std::string s;
std::back_insert_iterator<std::string> out(s);
if (boost::spirit::karma::generate_delimited(out, g, d, attr) && s == expected)
std::cout << "ok" << std::endl;
else
std::cout << "fail" << std::endl;
}
//]
//[reference_karma_binary_test
template <typename G>
void test_binary_generator(char const* expected, std::size_t size, G const& g)
{
std::string s;
std::back_insert_iterator<std::string> out(s);
if (boost::spirit::karma::generate(out, g) && !std::memcmp(s.c_str(), expected, size))
std::cout << "ok" << std::endl;
else
std::cout << "fail" << std::endl;
}
//]
//[reference_karma_binary_test_attr
template <typename G, typename T>
void test_binary_generator_attr(char const* expected, std::size_t size, G const& g, T const& attr)
{
std::string s;
std::back_insert_iterator<std::string> out(s);
if (boost::spirit::karma::generate(out, g, attr) && !std::memcmp(s.c_str(), expected, size))
std::cout << "ok" << std::endl;
else
std::cout << "fail" << std::endl;
}
//]
//[reference_karma_complex
// a simple complex number representation z = a + bi
struct complex
{
complex (double a, double b)
: a(a), b(b)
{}
double a;
double b;
};
//]
//[reference_karma_stream_complex
// define streaming operator for the type complex
std::ostream&
operator<< (std::ostream& os, complex const& z)
{
os << "{" << z.a << "," << z.b << "}";
return os;
}
//]
//[reference_karma_auto_complex
/*`The following construct is required to allow the `complex` data structure
to be utilized as a __fusion__ sequence. This is required as we will
emit output for this data structure with a __karma__ sequence:
`'{' << karma::double_ << ',' << karma::double_ << '}'`.
*/
BOOST_FUSION_ADAPT_STRUCT(
complex,
(double, a)
(double, b)
)
/*`We add a specialization for the create_generator customization point
defining a custom output format for the complex type. Generally, any
specialization for create_generator is expected to return the proto
expression to be used to generate output for the type the customization
point has been specialized for.
*/
/*`We need to utilize `proto::deep_copy` as the expression contains literals
(the `'{'`, `','`, and `'}'`) which normally get embedded in the proto
expression by reference only. The deep copy converts the proto tree to
hold this by value. The deep copy operation can be left out for simpler
proto expressions (not containing references to temporaries). Alternatively
you could use the `proto::make_expr` facility to build the required
proto expression.
*/
namespace boost { namespace spirit { namespace traits
{
template <>
struct create_generator<complex>
{
typedef proto::result_of::deep_copy<
BOOST_TYPEOF('{' << karma::double_ << ',' << karma::double_ << '}')
>::type type;
static type call()
{
return proto::deep_copy(
'{' << karma::double_ << ',' << karma::double_ << '}');
}
};
}}}
//]
//[reference_karma_auxiliary_attr_cast_data1
// this is just a test structure we want to use in place of an int
struct int_data
{
int i;
};
// we provide a custom attribute transformation to allow its use as an int
namespace boost { namespace spirit { namespace traits
{
template <>
struct transform_attribute<int_data const, int, karma::domain>
{
typedef int type;
static int pre(int_data const& d) { return d.i; }
};
}}}
//]
namespace client
{
using boost::spirit::karma::grammar;
using boost::spirit::karma::rule;
using boost::spirit::ascii::space_type;
//[karma_reference_grammar_definition
/*`Basic grammar usage:
*/
struct num_list : grammar<output_iterator_type, space_type, std::vector<int>()>
{
num_list() : base_type(start)
{
using boost::spirit::int_;
num = int_;
start = num << *(',' << num);
}
rule<output_iterator_type, space_type, std::vector<int>()> start;
rule<output_iterator_type, space_type, int()> num;
};
//]
}
int main()
{
///////////////////////////////////////////////////////////////////////////
// Operators
///////////////////////////////////////////////////////////////////////////
{
//[reference_karma_using_declarations_sequence
using boost::spirit::karma::double_;
//]
//[reference_karma_sequence
test_generator_attr("1.0,2.0", double_ << ',' << double_, std::make_pair(1.0, 2.0));
//]
}
{
//[reference_karma_using_declarations_alternative
using boost::spirit::karma::double_;
using boost::spirit::karma::ascii::string;
//]
//[reference_karma_alternative1
boost::variant<std::string, double> v1(1.0);
test_generator_attr("1.0", string | double_, v1);
test_generator_attr("2.0", string | double_, 2.0);
//]
//[reference_karma_alternative2
boost::variant<std::string, double> v2("example");
test_generator_attr("example", string | double_, v2);
test_generator_attr("example", string | double_, "example");
//]
}
{
//[reference_karma_using_declarations_kleene
using boost::spirit::karma::double_;
using boost::spirit::karma::space;
//]
//[reference_karma_kleene
std::vector<double> v;
v.push_back(1.0);
v.push_back(2.0);
v.push_back(3.0);
test_generator_attr_delim("1.0 2.0 3.0 ", *double_, space, v);
//]
}
{
//[reference_karma_using_declarations_plus
using boost::spirit::karma::double_;
using boost::spirit::karma::space;
//]
//[reference_karma_plus1
std::vector<double> v1;
v1.push_back(1.0);
v1.push_back(2.0);
v1.push_back(3.0);
test_generator_attr_delim("1.0 2.0 3.0 ", +double_, space, v1);
//]
//[reference_karma_plus2
std::vector<double> v2; // empty container
test_generator_attr("empty", +double_ | "empty", v2);
//]
}
{
//[reference_karma_using_declarations_list
using boost::spirit::karma::double_;
//]
//[reference_karma_list
std::vector<double> v1;
v1.push_back(1.0);
test_generator_attr("1.0", double_ % ',', v1);
v1.push_back(2.0);
test_generator_attr("1.0,2.0", double_ % ',', v1);
//]
}
{
//[reference_karma_using_declarations_optional
using boost::spirit::karma::double_;
//]
//[reference_karma_optional1
boost::optional<double> val(1.0);
test_generator_attr("1.0", -double_, val);
test_generator_attr("2.0", -double_, 2.0);
//]
}
{
using boost::spirit::karma::double_;
//[reference_karma_optional2
boost::optional<double> val; // empty optional
test_generator_attr("", -double_, val);
//]
}
{
//[reference_karma_using_declarations_and_predicate
using boost::spirit::karma::double_;
using boost::spirit::karma::ascii::char_;
using boost::spirit::karma::ascii::string;
using boost::phoenix::ref;
//]
//[reference_karma_and_predicate
test_generator_attr("b", &char_('a') << 'b' | 'c', 'a');
test_generator_attr("c", &char_('a') << 'b' | 'c', 'x');
test_generator_attr("abc", &string("123") << "abc" | "def", "123");
test_generator_attr("def", &string("123") << "abc" | "def", "456");
//]
}
{
//[reference_karma_using_declarations_not_predicate
using boost::spirit::karma::double_;
using boost::spirit::karma::ascii::char_;
using boost::spirit::karma::ascii::string;
using boost::phoenix::ref;
//]
//[reference_karma_not_predicate
test_generator_attr("c", !char_('a') << 'b' | 'c', 'a');
test_generator_attr("b", !char_('a') << 'b' | 'c', 'x');
test_generator_attr("def", !string("123") << "abc" | "def", "123");
test_generator_attr("abc", !string("123") << "abc" | "def", "456");
//]
}
///////////////////////////////////////////////////////////////////////////
// Directives
///////////////////////////////////////////////////////////////////////////
{
//[reference_karma_using_declarations_alignment
using boost::spirit::karma::double_;
using boost::spirit::karma::left_align;
using boost::spirit::karma::center;
using boost::spirit::karma::right_align;
//]
//[reference_karma_alignment
std::pair<double, double> p (1.0, 2.0);
test_generator_attr("1.0 |2.0", left_align(8)[double_] << '|' << double_, p);
test_generator_attr(" 1.0 |2.0", center(8)[double_] << '|' << double_, p);
test_generator_attr(" 1.0|2.0", right_align(8)[double_] << '|' << double_, p);
//]
}
{
//[reference_karma_using_declarations_repeat
using boost::spirit::karma::double_;
using boost::spirit::karma::repeat;
//]
//[reference_karma_repeat
std::vector<double> v;
v.push_back(1.0);
v.push_back(2.0);
v.push_back(3.0);
test_generator_attr("[1.0][2.0][3.0]", repeat['[' << double_ << ']'], v);
test_generator_attr("[1.0][2.0]", repeat(2)['[' << double_ << ']'], v);
// fails because of insufficient number of items
test_generator_attr("", repeat(4)['[' << double_ << ']'], v);
//]
}
{
//[reference_karma_using_declarations_delimit
using boost::spirit::karma::double_;
using boost::spirit::karma::delimit;
using boost::spirit::karma::verbatim;
//]
//[reference_karma_delimit
test_generator_attr("[ 2.0 , 4.3 ] ",
delimit['[' << double_ << ',' << double_ << ']'], 2.0, 4.3);
test_generator_attr("[*2.0*,*4.3*]*",
delimit('*')['[' << double_ << ',' << double_ << ']'], 2.0, 4.3);
test_generator_attr("[2.0, 4.3 ] ",
delimit[verbatim['[' << double_ << ','] << double_ << ']'], 2.0, 4.3);
//]
}
{
//[reference_karma_using_declarations_upperlower
using boost::spirit::karma::double_;
using boost::spirit::ascii::upper;
using boost::spirit::ascii::lower;
//]
//[reference_karma_upperlower
test_generator_attr("abc:2.0e-06", lower["ABC:" << double_], 2e-6);
test_generator_attr("ABC:2.0E-06", upper["abc:" << double_], 2e-6);
//]
}
{
//[reference_karma_using_declarations_maxwidth
using boost::spirit::karma::double_;
using boost::spirit::karma::maxwidth;
using boost::spirit::karma::left_align;
using boost::spirit::karma::right_align;
//]
//[reference_karma_maxwidth
test_generator("01234", maxwidth(5)["0123456789"]);
test_generator(" 012", maxwidth(5)[right_align(12)["0123456789"]]);
test_generator("0123 ", maxwidth(8)[left_align(8)["0123"]]);
//]
}
{
//[reference_karma_using_declarations_buffer
using boost::spirit::karma::double_;
using boost::spirit::karma::buffer;
//]
//[reference_karma_buffer
std::vector<double> v; // empty container
test_generator_attr("", -buffer['[' << +double_ << ']'], v);
v.push_back(1.0); // now, fill the container
v.push_back(2.0);
test_generator_attr("[1.02.0]", buffer['[' << +double_ << ']'], v);
//]
}
{
//[reference_karma_using_declarations_omit
using boost::spirit::karma::double_;
using boost::spirit::karma::omit;
//]
//[reference_karma_omit
std::pair<double, double> p (1.0, 2.0);
test_generator_attr("2.0", omit[double_] << double_, p);
//]
}
{
//[reference_karma_using_declarations_duplicate
using boost::spirit::karma::double_;
using boost::spirit::karma::duplicate;
using boost::spirit::karma::space;
//]
//[reference_karma_duplicate
test_generator_attr("2.02.0", duplicate[double_ << double_], 2.0);
test_generator_attr_delim("2.0 2.0 ", duplicate[double_ << double_], space, 2.0);
//]
}
{
//[reference_karma_using_declarations_columns
using boost::spirit::karma::double_;
using boost::spirit::karma::columns;
using boost::spirit::karma::space;
//]
//[reference_karma_columns
std::vector<double> v;
v.push_back(1.0);
v.push_back(2.0);
v.push_back(3.0);
test_generator_attr("1.0\n2.0\n3.0\n", columns(1)[*double_], v);
test_generator_attr_delim("1.0 2.0 \n3.0 \n", columns(2)[*double_], space, v);
//]
}
{
//[reference_karma_using_declarations_bool
using boost::spirit::karma::bool_;
using boost::spirit::karma::lit;
//]
//[reference_karma_bool
test_generator("true", lit(true));
test_generator("false", bool_(false));
test_generator_attr("true", bool_(true), true);
test_generator_attr("", bool_(true), false); // fails (as true != false)!
test_generator_attr("false", bool_, false);
//]
}
{
//[reference_karma_using_declarations_int
using boost::spirit::karma::int_;
using boost::spirit::karma::lit;
//]
//[reference_karma_int
test_generator("-2", lit(-2));
test_generator("-2", int_(-2));
test_generator_attr("-2", int_(-2), -2);
test_generator_attr("", int_(-2), 3); // fails (as -2 != 3)!
test_generator_attr("-2", int_, -2);
//]
}
{
//[reference_karma_using_declarations_uint
using boost::spirit::karma::uint_;
using boost::spirit::karma::lit;
//]
//[reference_karma_uint
test_generator("2", lit(2U));
test_generator("2", uint_(2));
test_generator_attr("2", uint_(2), 2);
test_generator_attr("", uint_(2), 3); // fails (as 2 != 3)!
test_generator_attr("2", uint_, 2);
//]
}
{
//[reference_karma_using_declarations_real
using boost::spirit::karma::double_;
using boost::spirit::karma::lit;
//]
//[reference_karma_real
test_generator("2.0", lit(2.0));
test_generator("2.0", double_(2));
test_generator_attr("2.0", double_(2.0), 2.0);
test_generator_attr("", double_(2.0), 3.0); // fails (as 2.0 != 3.0)!
test_generator_attr("-2.0", double_, -2.0);
test_generator_attr("1.234e05", double_, 1234.0e2);
test_generator_attr("1.234e-06", double_, 0.000001234);
//]
}
{
//[reference_karma_using_declarations_char
using boost::spirit::karma::lit;
using boost::spirit::ascii::char_;
//]
//[reference_karma_char
test_generator("A", 'A');
test_generator("A", lit('A'));
test_generator_attr("a", char_, 'a');
test_generator("A", char_('A'));
test_generator_attr("A", char_('A'), 'A');
test_generator_attr("", char_('A'), 'B'); // fails (as 'A' != 'B')
test_generator_attr("A", char_('A', 'Z'), 'A');
test_generator_attr("", char_('A', 'Z'), 'a'); // fails (as 'a' does not belong to 'A'...'Z')
test_generator_attr("k", char_("a-z0-9"), 'k');
test_generator_attr("", char_("a-z0-9"), 'A'); // fails (as 'A' does not belong to "a-z0-9")
//]
}
{
//[reference_karma_using_declarations_char_class
using boost::spirit::karma::alpha;
using boost::spirit::karma::upper;
//]
//[reference_karma_char_class
test_generator_attr("a", alpha, 'a');
test_generator_attr("A", alpha, 'A');
test_generator_attr("", alpha, '1'); // fails (as isalpha('1') is false)
test_generator_attr("A", upper[alpha], 'A');
test_generator_attr("", upper[alpha], 'a'); // fails (as isupper('a') is false)
//]
}
///////////////////////////////////////////////////////////////////////////
// string
{
//[reference_karma_using_declarations_string
using boost::spirit::karma::lit;
using boost::spirit::ascii::string;
//]
//[reference_karma_string
test_generator("abc", "abc");
test_generator("abc", lit("abc"));
test_generator("abc", lit(std::string("abc")));
test_generator_attr("abc", string, "abc");
test_generator("abc", string("abc"));
test_generator("abc", string(std::string("abc")));
test_generator_attr("abc", string("abc"), "abc");
test_generator_attr("", string("abc"), "cba"); // fails (as "abc" != "cba")
//]
}
///////////////////////////////////////////////////////////////////////////
// auxiliary
{
//[reference_karma_using_declarations_eol
using boost::spirit::karma::eol;
//]
//[reference_karma_eol
test_generator("\n", eol);
test_generator("abc\n", "abc" << eol);
//]
}
{
//[reference_karma_using_declarations_attr_cast
using boost::spirit::karma::int_;
//]
//[reference_karma_attr_cast1
int_data d = { 1 };
test_generator_attr("1", boost::spirit::karma::attr_cast(int_), d);
//]
}
{
//[reference_karma_using_declarations_eps
using boost::spirit::karma::eps;
using boost::phoenix::val;
//]
//[reference_karma_eps
test_generator("abc", eps[std::cout << val("starting eps example")] << "abc");
test_generator("abc", eps(true) << "abc");
test_generator("", eps(false) << "abc"); // fails as eps expression is 'false'
//]
}
{
//[reference_karma_using_declarations_lazy
namespace karma = boost::spirit::karma;
using boost::spirit::karma::_1;
using boost::spirit::ascii::string;
using boost::phoenix::val;
//]
//[reference_karma_lazy
test_generator_attr("abc", karma::lazy(val(string)), "abc");
test_generator("abc", karma::lazy(val(string))[_1 = "abc"]);
//]
}
///////////////////////////////////////////////////////////////////////////
// stream module
{
//[reference_karma_using_declarations_stream
using boost::spirit::karma::stream;
//]
//[reference_karma_stream
test_generator_attr("abc", stream, "abc");
test_generator("abc", stream("abc"));
test_generator_attr("{1.2,2.4}", stream, complex(1.2, 2.4));
test_generator("{1.2,2.4}", stream(complex(1.2, 2.4)));
//]
}
///////////////////////////////////////////////////////////////////////////
// auto module
{
//[reference_karma_using_declarations_auto
using boost::spirit::karma::auto_;
//]
//[reference_karma_auto
/*`Emit a simple string using the `karma::string` generator:
*/
test_generator_attr("abc", auto_, "abc");
test_generator("abc", auto_("abc"));
/*`Emit instances of the `complex` data type as defined above using the
generator defined by the customization point for `complex`:
*/
test_generator_attr("{1.2,2.4}", auto_, complex(1.2, 2.4));
test_generator("{1.2,2.4}", auto_(complex(1.2, 2.4)));
//]
}
///////////////////////////////////////////////////////////////////////////
// binary module
{
//[reference_karma_using_declarations_native_binary
using boost::spirit::karma::byte_;
using boost::spirit::karma::word;
using boost::spirit::karma::dword;
using boost::spirit::karma::qword;
//]
//[reference_karma_native_binary_little
test_binary_generator("\x01", 1, byte_(0x01));
test_binary_generator("\x01\x02", 2, word(0x0201));
test_binary_generator("\x01\x02\x03\x04", 4, dword(0x04030201));
test_binary_generator("\x01\x02\x03\x04\x05\x06\x07\x08", 8, qword(0x0807060504030201LL));
test_binary_generator_attr("\x01", 1, byte_, 0x01);
test_binary_generator_attr("\x01\x02", 2, word, 0x0201);
test_binary_generator_attr("\x01\x02\x03\x04", 4, dword, 0x04030201);
test_binary_generator_attr("\x01\x02\x03\x04\x05\x06\x07\x08", 8, qword, 0x0807060504030201LL);
//]
//[reference_karma_native_binary_big
test_binary_generator("\x01", 1, byte_(0x01));
test_binary_generator("\x02\x01", 2, word(0x0201));
test_binary_generator("\x04\x03\x02\x01", 4, dword(0x04030201));
test_binary_generator("\x08\x07\x06\x05\x04\x03\x02\x01", 8, qword(0x0807060504030201LL));
test_binary_generator_attr("\x01", 1, byte_, 0x01);
test_binary_generator_attr("\x02\x01", 2, word, 0x0201);
test_binary_generator_attr("\x04\x03\x02\x01", 4, dword, 0x04030201);
test_binary_generator_attr("\x08\x07\x06\x05\x04\x03\x02\x01", 8, qword, 0x0807060504030201LL);
//]
}
{
//[reference_karma_using_declarations_little_binary
using boost::spirit::karma::little_word;
using boost::spirit::karma::little_dword;
using boost::spirit::karma::little_qword;
//]
//[reference_karma_little_binary
test_binary_generator("\x01\x02", 2, little_word(0x0201));
test_binary_generator("\x01\x02\x03\x04", 4, little_dword(0x04030201));
test_binary_generator("\x01\x02\x03\x04\x05\x06\x07\x08", 8, little_qword(0x0807060504030201LL));
test_binary_generator_attr("\x01\x02", 2, little_word, 0x0201);
test_binary_generator_attr("\x01\x02\x03\x04", 4, little_dword, 0x04030201);
test_binary_generator_attr("\x01\x02\x03\x04\x05\x06\x07\x08", 8, little_qword, 0x0807060504030201LL);
//]
}
{
//[reference_karma_using_declarations_big_binary
using boost::spirit::karma::big_word;
using boost::spirit::karma::big_dword;
using boost::spirit::karma::big_qword;
//]
//[reference_karma_big_binary
test_binary_generator("\x02\x01", 2, big_word(0x0201));
test_binary_generator("\x04\x03\x02\x01", 4, big_dword(0x04030201));
test_binary_generator("\x08\x07\x06\x05\x04\x03\x02\x01", 8, big_qword(0x0807060504030201LL));
test_binary_generator_attr("\x02\x01", 2, big_word, 0x0201);
test_binary_generator_attr("\x04\x03\x02\x01", 4, big_dword, 0x04030201);
test_binary_generator_attr("\x08\x07\x06\x05\x04\x03\x02\x01", 8, big_qword, 0x0807060504030201LL);
//]
}
// action
{
//[reference_karma_using_declarations_action
using boost::spirit::karma::int_;
using boost::spirit::karma::string;
using boost::spirit::karma::_1;
using boost::phoenix::ref;
using boost::phoenix::val;
//]
//[reference_karma_action
int i = 42;
test_generator("42", int_[_1 = ref(i)]);
test_generator("abc", string[_1 = val("abc")]);
//]
}
// rule
{
//[karma_reference_rule
//`Some using declarations:
using boost::spirit::karma::rule;
using boost::spirit::karma::int_;
using boost::spirit::ascii::space;
using boost::spirit::ascii::space_type;
/*`Basic rule:
*/
rule<output_iterator_type> r;
r = int_(123);
test_generator("123", r);
/*`Rule with consumed attribute:
*/
rule<output_iterator_type, int()> ra;
ra = int_;
test_generator_attr("123", ra, 123);
/*`Rule with delimiter and consumed attribute:
*/
rule<output_iterator_type, std::vector<int>(), space_type> rs;
rs = *int_;
std::vector<int> v;
v.push_back(123);
v.push_back(456);
v.push_back(789);
test_generator_attr_delim("123 456 789", rs, space, v);
//]
}
// grammar
{
using client::num_list;
//[karma_reference_grammar_using
//`Some using declarations:
using boost::spirit::ascii::space_type;
using boost::spirit::ascii::space;
using boost::spirit::int_;
using boost::spirit::karma::grammar;
using boost::spirit::karma::rule;
//]
//[karma_reference_grammar
//`How to use the example grammar:
num_list nlist;
std::vector<int> v;
v.push_back(123);
v.push_back(456);
v.push_back(789);
test_generator_attr_delim("123 , 456 , 789", nlist, space, v);
//]
}
// symbols
{
//[reference_karma_using_declarations_symbols
using boost::spirit::karma::symbols;
//]
//[reference_karma_symbols
symbols<char, char const*> sym;
sym.add
('a', "Apple")
('b', "Banana")
('o', "Orange")
;
test_generator_attr("Banana", sym, 'b');
//]
}
// as
{
//[reference_karma_using_declarations_as
using boost::spirit::utree;
using boost::spirit::utree_type;
using boost::spirit::utf8_symbol_type;
using boost::spirit::karma::as;
using boost::spirit::karma::as_string;
using boost::spirit::karma::char_;
using boost::spirit::karma::double_;
//]
//[reference_karma_as
/*`To properly handle string concatenation with __utree__, we
make use of `as_string[]`. We also use `as<T>` to explicitly extract
a __utree__ symbol node.*/
typedef as<utf8_symbol_type> as_symbol_type;
as_symbol_type const as_symbol = as_symbol_type();
utree ut;
ut.push_back("xyz");
ut.push_back(1.23);
test_generator_attr("xyz1.23", as_string[*char_] << double_, ut);
test_generator_attr("xyz1.23", as<std::string>()[*char_] << double_, ut);
ut.clear();
ut.push_back(utf8_symbol_type("xyz"));
ut.push_back(1.23);
test_generator_attr("xyz1.23", as_symbol[*char_] << double_, ut);
test_generator_attr("xyz1.23", as<utf8_symbol_type>()[*char_] << double_, ut);
//]
}
return 0;
}

View File

@@ -0,0 +1,104 @@
// Copyright (c) 2001-2010 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)
// The main purpose of this example is to show how a single fusion sequence
// can be used to generate output of the elements in different sequences
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/struct.hpp>
#include <boost/fusion/include/nview.hpp>
#include <boost/assign/std/vector.hpp>
namespace fusion = boost::fusion;
namespace karma = boost::spirit::karma;
///////////////////////////////////////////////////////////////////////////////
namespace client
{
// Our employee struct
struct employee
{
std::string surname;
std::string forename;
int age;
double salary;
std::string department;
};
// define iterator type
typedef std::back_insert_iterator<std::string> iterator_type;
// This is the output routine taking a format description and the data to
// print
template <typename Generator, typename Sequence>
void generate(Generator const& g, Sequence const& s)
{
std::string generated;
iterator_type sink(generated);
karma::generate(sink, g, s);
std::cout << generated << std::endl;
}
}
// We need to tell fusion about our employee struct to make it a first-class
// fusion citizen. This has to be in global scope. Note that we don't need to
// list the members of our struct in the same sequence a they are defined
BOOST_FUSION_ADAPT_STRUCT(
client::employee,
(int, age)
(std::string, surname)
(std::string, forename)
(std::string, department)
(double, salary)
)
///////////////////////////////////////////////////////////////////////////////
int main()
{
std::string str;
// some employees
client::employee john = { "John", "Smith", 25, 2000.50, "Sales" };
client::employee mary = { "Mary", "Higgins", 23, 2200.36, "Marketing" };
client::employee tom = { "Tom", "Taylor", 48, 3200.00, "Boss" };
// print data about employees in different formats
{
// print forename and age
client::generate(
karma::string << ", " << karma::int_,
fusion::as_nview<2, 0>(john));
// print surname, forename, and salary
client::generate(
karma::string << ' ' << karma::string << ": " << karma::double_,
fusion::as_nview<1, 2, 4>(mary));
// print forename, age, and department
client::generate(
karma::string << ": " << karma::int_ << " (" << karma::string << ')',
fusion::as_nview<2, 0, 3>(tom));
}
// now make a list of all employees and print them all
std::vector<client::employee> employees;
{
using namespace boost::assign;
employees += john, mary, tom;
}
// print surname, forename, and salary for all employees
{
typedef
fusion::result_of::as_nview<client::employee const, 1, 2, 4>::type
names_and_salary;
karma::rule<client::iterator_type, names_and_salary()> r =
karma::string << ' ' << karma::string << ": " << karma::double_;
client::generate(r % karma::eol, employees);
}
return 0;
}

View File

@@ -0,0 +1,50 @@
// Copyright (c) 2001-2010 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)
// The purpose of this example is to show how a simple custom generator
// directive can be written. We develop a custom generator allowing to wrap
// the generated output after each 5th column.
//
// For more information see: http://boost-spirit.com/home/?page_id=659
#include <boost/spirit/include/karma_generate_attr.hpp>
#include <boost/spirit/include/karma_char.hpp>
#include <boost/spirit/include/karma_operator.hpp>
#include <boost/spirit/include/karma_numeric.hpp>
#include <iostream>
#include <string>
#include "simple_columns_directive.hpp"
namespace karma = boost::spirit::karma;
int main()
{
using custom_generator::columns;
std::vector<int> v;
for (int i = 0; i < 17; ++i)
v.push_back(i);
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
bool result = karma::generate_delimited(
sink, columns[*karma::int_], karma::space, v);
if (result)
{
std::cout << "-------------------------------- \n";
std::cout << "Generation succeeded\n";
std::cout << "generated output: " << "\n" << generated << "\n";
std::cout << "-------------------------------- \n";
}
else
{
std::cout << "-------------------------------- \n";
std::cout << "Generation failed\n";
std::cout << "-------------------------------- \n";
}
return 0;
}

View File

@@ -0,0 +1,133 @@
// Copyright (c) 2001-2010 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)
#if !defined(COLUMNS_DEC_05_2009_0716PM)
#define COLUMNS_DEC_05_2009_0716PM
#include <boost/spirit/include/karma_generate.hpp>
///////////////////////////////////////////////////////////////////////////////
// definition the place holder
namespace custom_generator
{
BOOST_SPIRIT_TERMINAL(columns)
}
///////////////////////////////////////////////////////////////////////////////
// implementation the enabler
namespace boost { namespace spirit
{
// We want custom_generator::columns to be usable as a directive only,
// and only for generator expressions (karma::domain).
template <>
struct use_directive<karma::domain, custom_generator::tag::columns>
: mpl::true_ {};
}}
///////////////////////////////////////////////////////////////////////////////
// implementation of the generator
namespace custom_generator
{
// special delimiter wrapping the original one while additionally emitting
// the column delimiter after each 5th invocation
template <typename Delimiter>
struct columns_delimiter
{
columns_delimiter(Delimiter const& delim)
: delimiter(delim), count(0) {}
// This function is called during the actual delimiter output
template <typename OutputIterator, typename Context
, typename Delimiter_, typename Attribute>
bool generate(OutputIterator& sink, Context&, Delimiter_ const&
, Attribute const&) const
{
// first invoke the wrapped delimiter
if (!karma::delimit_out(sink, delimiter))
return false;
// now we count the number of invocations and emit the column
// delimiter after each 5th column
if ((++count % 5) == 0)
*sink++ = '\n';
return true;
}
// Generate a final column delimiter if the last invocation didn't
// emit one
template <typename OutputIterator>
bool final_delimit_out(OutputIterator& sink) const
{
if (count % 5)
*sink++ = '\n';
return true;
}
Delimiter const& delimiter; // wrapped delimiter
mutable unsigned int count; // invocation counter
};
// That's the actual columns generator
template <typename Subject>
struct simple_columns_generator
: boost::spirit::karma::unary_generator<
simple_columns_generator<Subject> >
{
// Define required output iterator properties
typedef typename Subject::properties properties;
// Define the attribute type exposed by this parser component
template <typename Context, typename Iterator>
struct attribute
: boost::spirit::traits::attribute_of<Subject, Context, Iterator>
{};
simple_columns_generator(Subject const& s)
: subject(s)
{}
// This function is called during the actual output generation process.
// It dispatches to the embedded generator while supplying a new
// delimiter to use, wrapping the outer delimiter.
template <typename OutputIterator, typename Context
, typename Delimiter, typename Attribute>
bool generate(OutputIterator& sink, Context& ctx
, Delimiter const& delimiter, Attribute const& attr) const
{
columns_delimiter<Delimiter> d(delimiter);
return subject.generate(sink, ctx, d, attr) && d.final_delimit_out(sink);
}
// This function is called during error handling to create
// a human readable string for the error context.
template <typename Context>
boost::spirit::info what(Context& ctx) const
{
return boost::spirit::info("columns", subject.what(ctx));
}
Subject subject;
};
}
///////////////////////////////////////////////////////////////////////////////
// instantiation of the generator
namespace boost { namespace spirit { namespace karma
{
// This is the factory function object invoked in order to create
// an instance of our simple_columns_generator.
template <typename Subject, typename Modifiers>
struct make_directive<custom_generator::tag::columns, Subject, Modifiers>
{
typedef custom_generator::simple_columns_generator<Subject> result_type;
result_type operator()(unused_type, Subject const& s, unused_type) const
{
return result_type(s);
}
};
}}}
#endif

View File

@@ -0,0 +1,32 @@
#==============================================================================
# Copyright (c) 2001-2009 Joel de Guzman
# Copyright (c) 2001-2009 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)
#==============================================================================
project spirit-lexer-example
: requirements
<c++-template-depth>300
:
:
;
exe example1 : example1.cpp ;
exe example2 : example2.cpp ;
exe example3 : example3.cpp ;
exe example4 : example4.cpp ;
exe example5 : example5.cpp ;
exe example6 : example6.cpp ;
exe print_numbers : print_numbers.cpp ;
exe print_number_tokenids : print_number_tokenids.cpp ;
exe word_count : word_count.cpp ;
exe word_count_functor : word_count_functor.cpp ;
exe word_count_lexer : word_count_lexer.cpp ;
exe word_count_functor_flex : word_count_functor_flex.cpp ;
exe strip_comments : strip_comments.cpp ;
exe strip_comments_lexer : strip_comments_lexer.cpp ;
exe custom_token_attribute : custom_token_attribute.cpp ;
exe lexer_debug_support : lexer_debug_support.cpp ;

View File

@@ -0,0 +1,112 @@
// Copyright (c) 2001-2010 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)
// The purpose of this example is to demonstrate how custom, user defined types
// can be easily integrated with the lexer as token value types. Moreover, the
// custom token values are properly exposed to the parser as well, allowing to
// retrieve the custom values using the built in parser attribute propagation
// rules.
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/qi.hpp>
namespace lex = boost::spirit::lex;
namespace qi = boost::spirit::qi;
namespace mpl = boost::mpl;
///////////////////////////////////////////////////////////////////////////////
// This is just a simple custom rational data structure holding two ints to be
// interpreted as a rational number
struct rational
{
rational(int n = 0, int d = 0)
: nominator_(n), denominator_(d)
{}
int nominator_;
int denominator_;
};
///////////////////////////////////////////////////////////////////////////////
// A rational is represented as "{n,d}", where 'n' and 'd' are the nominator
// and denominator of the number. We use Spirit.Qi to do the low level parsing
// of the input sequence as matched by the lexer. Certainly, any other
// conversion could be used instead.
//
// The lexer uses the template assign_to_attribute_from_iterators<> to convert
// the matched input sequence (pair of iterators) to the token value type as
// specified while defining the lex::token_def<>.
//
// Our specialization of assign_to_attribute_from_iterators<> for the rational
// data type defined above has to be placed into the
// namespace boost::spirit::traits, otherwise it won't be found by the library.
namespace boost { namespace spirit { namespace traits
{
template <typename Iterator>
struct assign_to_attribute_from_iterators<rational, Iterator>
{
static void
call(Iterator const& first, Iterator const& last, rational& attr)
{
int x, y;
Iterator b = first;
qi::parse(b, last,
'{' >> qi::int_ >> ',' >> qi::int_ >> '}', x, y);
attr = rational(x, y);
}
};
}}}
///////////////////////////////////////////////////////////////////////////////
// a lexer recognizing a single token type: rational
template <typename Lexer>
struct lex_rational : lex::lexer<Lexer>
{
lex_rational()
{
this->self.add_pattern("INT", "[1-9][0-9]*");
rt = "\\{{INT},{INT}\\}";
this->self.add(rt);
}
lex::token_def<rational> rt;
};
int main()
{
// the token type needs to know the iterator type of the underlying
// input and the set of used token value types
typedef lex::lexertl::token<std::string::iterator,
mpl::vector<rational> > token_type;
// use actor_lexer<> here if your token definitions have semantic
// actions
typedef lex::lexertl::lexer<token_type> lexer_type;
// this is the iterator exposed by the lexer, we use this for parsing
typedef lexer_type::iterator_type iterator_type;
// create a lexer instance
std::string input("{3,4}");
std::string::iterator s = input.begin();
lex_rational<lexer_type> lex;
iterator_type b = lex.begin(s, input.end());
// use the embedded token_def as a parser, it exposes its token value type
// as its parser attribute type
rational r;
if (!qi::parse(b, lex.end(), lex.rt, r))
{
std::cerr << "Parsing failed!" << std::endl;
return -1;
}
std::cout << "Parsing succeeded: {"
<< r.nominator_ << ", " << r.denominator_ << "}" << std::endl;
return 0;
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) 2001-2010 Hartmut Kaiser
// Copyright (c) 2001-2007 Joel de Guzman
//
// 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)
#include <iostream>
#include <fstream>
#include <string>
///////////////////////////////////////////////////////////////////////////////
// Helper function reading a file into a string
///////////////////////////////////////////////////////////////////////////////
inline std::string
read_from_file(char const* infile)
{
std::ifstream instream(infile);
if (!instream.is_open()) {
std::cerr << "Couldn't open file: " << infile << std::endl;
exit(-1);
}
instream.unsetf(std::ios::skipws); // No white space skipping!
return std::string(std::istreambuf_iterator<char>(instream.rdbuf()),
std::istreambuf_iterator<char>());
}

View File

@@ -0,0 +1,132 @@
// Copyright (c) 2001-2010 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)
// Simple lexer/parser to test the Spirit installation.
//
// This example shows, how to create a simple lexer recognizing 5 different
// tokens, and how to use a single token definition as the skip parser during
// the parsing. Additionally, it demonstrates how to use one of the defined
// tokens as a parser component in the grammar.
//
// The grammar recognizes a simple input structure, for instance:
//
// {
// hello world, hello it is me
// }
//
// Any number of simple sentences (optionally comma separated) inside a pair
// of curly braces will be matched.
// #define BOOST_SPIRIT_LEXERTL_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include "example.hpp"
using namespace boost::spirit;
///////////////////////////////////////////////////////////////////////////////
// Token definition
///////////////////////////////////////////////////////////////////////////////
template <typename Lexer>
struct example1_tokens : lex::lexer<Lexer>
{
example1_tokens()
{
// define tokens and associate them with the lexer
identifier = "[a-zA-Z_][a-zA-Z0-9_]*";
this->self = lex::char_(',') | '{' | '}' | identifier;
// any token definition to be used as the skip parser during parsing
// has to be associated with a separate lexer state (here 'WS')
this->white_space = "[ \\t\\n]+";
this->self("WS") = white_space;
}
lex::token_def<> identifier, white_space;
};
///////////////////////////////////////////////////////////////////////////////
// Grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct example1_grammar
: qi::grammar<Iterator, qi::in_state_skipper<lex::token_def<> > >
{
template <typename TokenDef>
example1_grammar(TokenDef const& tok)
: example1_grammar::base_type(start)
{
start = '{' >> *(tok.identifier >> -ascii::char_(',')) >> '}';
}
qi::rule<Iterator, qi::in_state_skipper<lex::token_def<> > > start;
};
///////////////////////////////////////////////////////////////////////////////
int main()
{
// iterator type used to expose the underlying input stream
typedef std::string::iterator base_iterator_type;
// This is the token type to return from the lexer iterator
typedef lex::lexertl::token<base_iterator_type> token_type;
// This is the lexer type to use to tokenize the input.
// We use the lexertl based lexer engine.
typedef lex::lexertl::lexer<token_type> lexer_type;
// This is the lexer type (derived from the given lexer type).
typedef example1_tokens<lexer_type> example1_lex;
// This is the iterator type exposed by the lexer
typedef example1_lex::iterator_type iterator_type;
// This is the type of the grammar to parse
typedef example1_grammar<iterator_type> example1_grammar;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
example1_lex lex; // Our lexer
example1_grammar calc(lex); // Our grammar definition
std::string str (read_from_file("example1.input"));
// At this point we generate the iterator pair used to expose the
// tokenized input stream.
std::string::iterator it = str.begin();
iterator_type iter = lex.begin(it, str.end());
iterator_type end = lex.end();
// Parsing is done based on the token stream, not the character
// stream read from the input.
// Note, how we use the token_def defined above as the skip parser. It must
// be explicitly wrapped inside a state directive, switching the lexer
// state for the duration of skipping whitespace.
bool r = qi::phrase_parse(iter, end, calc, qi::in_state("WS")[lex.white_space]);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \"" << rest << "\"\n";
std::cout << "-------------------------\n";
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,5 @@
{
hello world,
hello world,
goodbye
}

View File

@@ -0,0 +1,168 @@
// Copyright (c) 2001-2010 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)
// This example shows how to create a simple lexer recognizing a couple of
// different tokens and how to use this with a grammar. This example has a
// heavily backtracking grammar which makes it a candidate for lexer based
// parsing (all tokens are scanned and generated only once, even if
// backtracking is required) which speeds up the overall parsing process
// considerably, out-weighting the overhead needed for setting up the lexer.
// Additionally it demonstrates how to use one of the defined tokens as a
// parser component in the grammar.
//
// The grammar recognizes a simple input structure: any number of English
// simple sentences (statements, questions and commands) are recognized and
// are being counted separately.
// #define BOOST_SPIRIT_DEBUG
// #define BOOST_SPIRIT_LEXERTL_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/phoenix/operator.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include "example.hpp"
using namespace boost::spirit;
using namespace boost::spirit::ascii;
using boost::phoenix::ref;
///////////////////////////////////////////////////////////////////////////////
// Token definition
///////////////////////////////////////////////////////////////////////////////
template <typename Lexer>
struct example2_tokens : lex::lexer<Lexer>
{
example2_tokens()
{
// A 'word' is comprised of one or more letters and an optional
// apostrophe. If it contains an apostrophe, there may only be one and
// the apostrophe must be preceded and succeeded by at least 1 letter.
// For example, "I'm" and "doesn't" meet the definition of 'word' we
// define below.
word = "[a-zA-Z]+('[a-zA-Z]+)?";
// Associate the tokens and the token set with the lexer. Note that
// single character token definitions as used below always get
// interpreted literally and never as special regex characters. This is
// done to be able to assign single characters the id of their character
// code value, allowing to reference those as literals in Qi grammars.
this->self = lex::token_def<>(',') | '!' | '.' | '?' | ' ' | '\n' | word;
}
lex::token_def<> word;
};
///////////////////////////////////////////////////////////////////////////////
// Grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct example2_grammar : qi::grammar<Iterator>
{
template <typename TokenDef>
example2_grammar(TokenDef const& tok)
: example2_grammar::base_type(story)
, paragraphs(0), commands(0), questions(0), statements(0)
{
story
= +paragraph
;
paragraph
= ( +( command [ ++ref(commands) ]
| question [ ++ref(questions) ]
| statement [ ++ref(statements) ]
)
>> *char_(' ') >> +char_('\n')
)
[ ++ref(paragraphs) ]
;
command
= +(tok.word | ' ' | ',') >> '!'
;
question
= +(tok.word | ' ' | ',') >> '?'
;
statement
= +(tok.word | ' ' | ',') >> '.'
;
BOOST_SPIRIT_DEBUG_NODE(story);
BOOST_SPIRIT_DEBUG_NODE(paragraph);
BOOST_SPIRIT_DEBUG_NODE(command);
BOOST_SPIRIT_DEBUG_NODE(question);
BOOST_SPIRIT_DEBUG_NODE(statement);
}
qi::rule<Iterator> story, paragraph, command, question, statement;
int paragraphs, commands, questions, statements;
};
///////////////////////////////////////////////////////////////////////////////
int main()
{
// iterator type used to expose the underlying input stream
typedef std::string::iterator base_iterator_type;
// This is the token type to return from the lexer iterator
typedef lex::lexertl::token<base_iterator_type> token_type;
// This is the lexer type to use to tokenize the input.
// Here we use the lexertl based lexer engine.
typedef lex::lexertl::lexer<token_type> lexer_type;
// This is the token definition type (derived from the given lexer type).
typedef example2_tokens<lexer_type> example2_tokens;
// this is the iterator type exposed by the lexer
typedef example2_tokens::iterator_type iterator_type;
// this is the type of the grammar to parse
typedef example2_grammar<iterator_type> example2_grammar;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
example2_tokens tokens; // Our lexer
example2_grammar calc(tokens); // Our parser
std::string str (read_from_file("example2.input"));
// At this point we generate the iterator pair used to expose the
// tokenized input stream.
std::string::iterator it = str.begin();
iterator_type iter = tokens.begin(it, str.end());
iterator_type end = tokens.end();
// Parsing is done based on the token stream, not the character
// stream read from the input.
bool r = qi::parse(iter, end, calc);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "There were "
<< calc.commands << " commands, "
<< calc.questions << " questions, and "
<< calc.statements << " statements.\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,7 @@
Our hiking boots are ready. So, let's pack!
Have you the plane tickets for there and back?
I do, I do. We're all ready to go. Grab my hand and be my beau.

View File

@@ -0,0 +1,149 @@
// Copyright (c) 2001-2010 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)
// This example shows how to create a simple lexer recognizing a couple of
// different tokens and how to use this with a grammar. This example has a
// heavily backtracking grammar which makes it a candidate for lexer based
// parsing (all tokens are scanned and generated only once, even if
// backtracking is required) which speeds up the overall parsing process
// considerably, out-weighting the overhead needed for setting up the lexer.
//
// Additionally, this example demonstrates, how to define a token set usable
// as the skip parser during parsing, allowing to define several tokens to be
// ignored.
//
// This example recognizes couplets, which are sequences of numbers enclosed
// in matching pairs of parenthesis. See the comments below to for details
// and examples.
// #define BOOST_SPIRIT_LEXERTL_DEBUG
// #define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include "example.hpp"
using namespace boost::spirit;
///////////////////////////////////////////////////////////////////////////////
// Token definition
///////////////////////////////////////////////////////////////////////////////
template <typename Lexer>
struct example3_tokens : lex::lexer<Lexer>
{
example3_tokens()
{
// define the tokens to match
ellipses = "\\.\\.\\.";
number = "[0-9]+";
// associate the tokens and the token set with the lexer
this->self = ellipses | '(' | ')' | number;
// define the whitespace to ignore (spaces, tabs, newlines and C-style
// comments)
this->self("WS")
= lex::token_def<>("[ \\t\\n]+") // whitespace
| "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/" // C style comments
;
}
// these tokens expose the iterator_range of the matched input sequence
lex::token_def<> ellipses, identifier, number;
};
///////////////////////////////////////////////////////////////////////////////
// Grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Lexer>
struct example3_grammar
: qi::grammar<Iterator, qi::in_state_skipper<Lexer> >
{
template <typename TokenDef>
example3_grammar(TokenDef const& tok)
: example3_grammar::base_type(start)
{
start
= +(couplet | tok.ellipses)
;
// A couplet matches nested left and right parenthesis.
// For example:
// (1) (1 2) (1 2 3) ...
// ((1)) ((1 2)(3 4)) (((1) (2 3) (1 2 (3) 4))) ...
// (((1))) ...
couplet
= tok.number
| '(' >> +couplet >> ')'
;
BOOST_SPIRIT_DEBUG_NODE(start);
BOOST_SPIRIT_DEBUG_NODE(couplet);
}
qi::rule<Iterator, qi::in_state_skipper<Lexer> > start, couplet;
};
///////////////////////////////////////////////////////////////////////////////
int main()
{
// iterator type used to expose the underlying input stream
typedef std::string::iterator base_iterator_type;
// This is the token type to return from the lexer iterator
typedef lex::lexertl::token<base_iterator_type> token_type;
// This is the lexer type to use to tokenize the input.
// Here we use the lexertl based lexer engine.
typedef lex::lexertl::lexer<token_type> lexer_type;
// This is the token definition type (derived from the given lexer type).
typedef example3_tokens<lexer_type> example3_tokens;
// this is the iterator type exposed by the lexer
typedef example3_tokens::iterator_type iterator_type;
// this is the type of the grammar to parse
typedef example3_grammar<iterator_type, example3_tokens::lexer_def> example3_grammar;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
example3_tokens tokens; // Our lexer
example3_grammar calc(tokens); // Our parser
std::string str (read_from_file("example3.input"));
// At this point we generate the iterator pair used to expose the
// tokenized input stream.
std::string::iterator it = str.begin();
iterator_type iter = tokens.begin(it, str.end());
iterator_type end = tokens.end();
// Parsing is done based on the token stream, not the character
// stream read from the input.
// Note how we use the lexer defined above as the skip parser.
bool r = qi::phrase_parse(iter, end, calc, qi::in_state("WS")[tokens.self]);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,5 @@
/* the following are couplets */
(1) (1 2) (1 2 3) ...
((1)) ((1 2)(3 4)) (((1) (2 3) (1 2 (3) 4))) ...
(((1))) ...

View File

@@ -0,0 +1,226 @@
// Copyright (c) 2001-2010 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)
// This example shows how to create a simple lexer recognizing a couple of
// different tokens aimed at a simple language and how to use this lexer with
// a grammar. It shows how to associate attributes to tokens and how to access
// the token attributes from inside the grammar.
//
// We use explicit token attribute types, making the corresponding token instances
// carry convert the matched input into an instance of that type. The token
// attribute is exposed as the parser attribute if this token is used as a
// parser component somewhere in a grammar.
//
// Additionally, this example demonstrates, how to define a token set usable
// as the skip parser during parsing, allowing to define several tokens to be
// ignored.
//
// This example recognizes a very simple programming language having
// assignment statements and if and while control structures. Look at the file
// example4.input for an example.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/phoenix/operator.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include "example.hpp"
using namespace boost::spirit;
using boost::phoenix::val;
///////////////////////////////////////////////////////////////////////////////
// Token definition
///////////////////////////////////////////////////////////////////////////////
template <typename Lexer>
struct example4_tokens : lex::lexer<Lexer>
{
example4_tokens()
{
// define the tokens to match
identifier = "[a-zA-Z_][a-zA-Z0-9_]*";
constant = "[0-9]+";
if_ = "if";
else_ = "else";
while_ = "while";
// associate the tokens and the token set with the lexer
this->self = lex::token_def<>('(') | ')' | '{' | '}' | '=' | ';' | constant;
this->self += if_ | else_ | while_ | identifier;
// define the whitespace to ignore (spaces, tabs, newlines and C-style
// comments)
this->self("WS")
= lex::token_def<>("[ \\t\\n]+")
| "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/"
;
}
//[example4_token_def
// these tokens expose the iterator_range of the matched input sequence
lex::token_def<> if_, else_, while_;
// The following two tokens have an associated attribute type, 'identifier'
// carries a string (the identifier name) and 'constant' carries the
// matched integer value.
//
// Note: any token attribute type explicitly specified in a token_def<>
// declaration needs to be listed during token type definition as
// well (see the typedef for the token_type below).
//
// The conversion of the matched input to an instance of this type occurs
// once (on first access), which makes token attributes as efficient as
// possible. Moreover, token instances are constructed once by the lexer
// library. From this point on tokens are passed by reference only,
// avoiding them being copied around.
lex::token_def<std::string> identifier;
lex::token_def<unsigned int> constant;
//]
};
///////////////////////////////////////////////////////////////////////////////
// Grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Lexer>
struct example4_grammar
: qi::grammar<Iterator, qi::in_state_skipper<Lexer> >
{
template <typename TokenDef>
example4_grammar(TokenDef const& tok)
: example4_grammar::base_type(program)
{
using boost::spirit::_val;
program
= +block
;
block
= '{' >> *statement >> '}'
;
statement
= assignment
| if_stmt
| while_stmt
;
assignment
= (tok.identifier >> '=' >> expression >> ';')
[
std::cout << val("assignment statement to: ") << _1 << "\n"
]
;
if_stmt
= ( tok.if_ >> '(' >> expression >> ')' >> block
>> -(tok.else_ >> block)
)
[
std::cout << val("if expression: ") << _2 << "\n"
]
;
while_stmt
= (tok.while_ >> '(' >> expression >> ')' >> block)
[
std::cout << val("while expression: ") << _2 << "\n"
]
;
// since expression has a variant return type accommodating for
// std::string and unsigned integer, both possible values may be
// returned to the calling rule
expression
= tok.identifier [ _val = _1 ]
| tok.constant [ _val = _1 ]
;
}
typedef boost::variant<unsigned int, std::string> expression_type;
qi::rule<Iterator, qi::in_state_skipper<Lexer> > program, block, statement;
qi::rule<Iterator, qi::in_state_skipper<Lexer> > assignment, if_stmt;
qi::rule<Iterator, qi::in_state_skipper<Lexer> > while_stmt;
// the expression is the only rule having a return value
qi::rule<Iterator, expression_type(), qi::in_state_skipper<Lexer> > expression;
};
///////////////////////////////////////////////////////////////////////////////
int main()
{
// iterator type used to expose the underlying input stream
typedef std::string::iterator base_iterator_type;
//[example4_token
// This is the lexer token type to use. The second template parameter lists
// all attribute types used for token_def's during token definition (see
// calculator_tokens<> above). Here we use the predefined lexertl token
// type, but any compatible token type may be used instead.
//
// If you don't list any token attribute types in the following declaration
// (or just use the default token type: lexertl_token<base_iterator_type>)
// it will compile and work just fine, just a bit less efficient. This is
// because the token attribute will be generated from the matched input
// sequence every time it is requested. But as soon as you specify at
// least one token attribute type you'll have to list all attribute types
// used for token_def<> declarations in the token definition class above,
// otherwise compilation errors will occur.
typedef lex::lexertl::token<
base_iterator_type, boost::mpl::vector<unsigned int, std::string>
> token_type;
//]
// Here we use the lexertl based lexer engine.
typedef lex::lexertl::lexer<token_type> lexer_type;
// This is the token definition type (derived from the given lexer type).
typedef example4_tokens<lexer_type> example4_tokens;
// this is the iterator type exposed by the lexer
typedef example4_tokens::iterator_type iterator_type;
// this is the type of the grammar to parse
typedef example4_grammar<iterator_type, example4_tokens::lexer_def> example4_grammar;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
example4_tokens tokens; // Our lexer
example4_grammar calc(tokens); // Our parser
std::string str (read_from_file("example4.input"));
// At this point we generate the iterator pair used to expose the
// tokenized input stream.
std::string::iterator it = str.begin();
iterator_type iter = tokens.begin(it, str.end());
iterator_type end = tokens.end();
// Parsing is done based on the token stream, not the character
// stream read from the input.
// Note how we use the lexer defined above as the skip parser. It must
// be explicitly wrapped inside a state directive, switching the lexer
// state for the duration of skipping whitespace.
bool r = qi::phrase_parse(iter, end, calc, qi::in_state("WS")[tokens.self]);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,17 @@
/* example4.input */
{
if (variable) { a = b ; }
while (10) {
if (2) { b = 10 ; }
if (x) { c = x ; } else { d = 10 ; }
}
variable = 42;
}

View File

@@ -0,0 +1,272 @@
// Copyright (c) 2001-2010 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)
// This example shows how to create a simple lexer recognizing a couple of
// different tokens aimed at a simple language and how to use this lexer with
// a grammar. It shows how to associate attributes to tokens and how to access the
// token attributes from inside the grammar.
//
// Additionally, this example demonstrates, how to define a token set usable
// as the skip parser during parsing, allowing to define several tokens to be
// ignored.
//
// The main purpose of this example is to show how inheritance can be used to
// overload parts of a base grammar and add token definitions to a base lexer.
//
// Further, it shows how you can use the 'omit' attribute type specifier
// for token definitions to force the token to have no attribute (expose an
// unused attribute).
//
// This example recognizes a very simple programming language having
// assignment statements and if and while control structures. Look at the file
// example5.input for an example.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/phoenix/operator.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include "example.hpp"
using namespace boost::spirit;
using boost::phoenix::val;
///////////////////////////////////////////////////////////////////////////////
// Token definition base, defines all tokens for the base grammar below
///////////////////////////////////////////////////////////////////////////////
template <typename Lexer>
struct example5_base_tokens : lex::lexer<Lexer>
{
protected:
// this lexer is supposed to be used as a base type only
example5_base_tokens() {}
public:
void init_token_definitions()
{
// define the tokens to match
identifier = "[a-zA-Z_][a-zA-Z0-9_]*";
constant = "[0-9]+";
if_ = "if";
while_ = "while";
// associate the tokens and the token set with the lexer
this->self += lex::token_def<>('(') | ')' | '{' | '}' | '=' | ';' | constant;
this->self += if_ | while_ | identifier;
// define the whitespace to ignore (spaces, tabs, newlines and C-style
// comments)
this->self("WS")
= lex::token_def<>("[ \\t\\n]+")
| "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/"
;
}
// these tokens have no attribute
lex::token_def<lex::omit> if_, while_;
// The following two tokens have an associated attribute type, 'identifier'
// carries a string (the identifier name) and 'constant' carries the
// matched integer value.
//
// Note: any token attribute type explicitly specified in a token_def<>
// declaration needs to be listed during token type definition as
// well (see the typedef for the token_type below).
//
// The conversion of the matched input to an instance of this type occurs
// once (on first access), which makes token attributes as efficient as
// possible. Moreover, token instances are constructed once by the lexer
// library. From this point on tokens are passed by reference only,
// avoiding them being copied around.
lex::token_def<std::string> identifier;
lex::token_def<unsigned int> constant;
};
///////////////////////////////////////////////////////////////////////////////
// Grammar definition base, defines a basic language
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Lexer>
struct example5_base_grammar
: qi::grammar<Iterator, qi::in_state_skipper<Lexer> >
{
template <typename TokenDef>
example5_base_grammar(TokenDef const& tok)
: example5_base_grammar::base_type(program)
{
using boost::spirit::_val;
program
= +block
;
block
= '{' >> *statement >> '}'
;
statement
= assignment
| if_stmt
| while_stmt
;
assignment
= (tok.identifier >> '=' >> expression >> ';')
[
std::cout << val("assignment statement to: ") << _1 << "\n"
]
;
if_stmt
= (tok.if_ >> '(' >> expression >> ')' >> block)
[
std::cout << val("if expression: ") << _1 << "\n"
]
;
while_stmt
= (tok.while_ >> '(' >> expression >> ')' >> block)
[
std::cout << val("while expression: ") << _1 << "\n"
]
;
// since expression has a variant return type accommodating for
// std::string and unsigned integer, both possible values may be
// returned to the calling rule
expression
= tok.identifier [ _val = _1 ]
| tok.constant [ _val = _1 ]
;
}
typedef qi::in_state_skipper<Lexer> skipper_type;
qi::rule<Iterator, skipper_type> program, block, statement;
qi::rule<Iterator, skipper_type> assignment, if_stmt;
qi::rule<Iterator, skipper_type> while_stmt;
// the expression is the only rule having a return value
typedef boost::variant<unsigned int, std::string> expression_type;
qi::rule<Iterator, expression_type(), skipper_type> expression;
};
///////////////////////////////////////////////////////////////////////////////
// Token definition for derived lexer, defines additional tokens
///////////////////////////////////////////////////////////////////////////////
template <typename Lexer>
struct example5_tokens : example5_base_tokens<Lexer>
{
typedef example5_base_tokens<Lexer> base_type;
example5_tokens()
{
// define the additional token to match
else_ = "else";
// associate the new token with the lexer, note we add 'else' before
// anything else to add it to the token set before the identifier
// token, otherwise "else" would be matched as an identifier
this->self = else_;
// now add the token definitions from the base class
this->base_type::init_token_definitions();
}
// this token has no attribute
lex::token_def<lex::omit> else_;
};
///////////////////////////////////////////////////////////////////////////////
// Derived grammar definition, defines a language extension
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Lexer>
struct example5_grammar : example5_base_grammar<Iterator, Lexer>
{
template <typename TokenDef>
example5_grammar(TokenDef const& tok)
: example5_base_grammar<Iterator, Lexer>(tok)
{
// we alter the if_stmt only
this->if_stmt
= this->if_stmt.copy() >> -(tok.else_ >> this->block)
;
}
};
///////////////////////////////////////////////////////////////////////////////
int main()
{
// iterator type used to expose the underlying input stream
typedef std::string::iterator base_iterator_type;
// This is the lexer token type to use. The second template parameter lists
// all attribute types used for token_def's during token definition (see
// example5_base_tokens<> above). Here we use the predefined lexertl token
// type, but any compatible token type may be used instead.
//
// If you don't list any token attribute types in the following declaration
// (or just use the default token type: lexertl_token<base_iterator_type>)
// it will compile and work just fine, just a bit less efficient. This is
// because the token attribute will be generated from the matched input
// sequence every time it is requested. But as soon as you specify at
// least one token attribute type you'll have to list all attribute types
// used for token_def<> declarations in the token definition class above,
// otherwise compilation errors will occur.
typedef lex::lexertl::token<
base_iterator_type, boost::mpl::vector<unsigned int, std::string>
> token_type;
// Here we use the lexertl based lexer engine.
typedef lex::lexertl::lexer<token_type> lexer_type;
// This is the token definition type (derived from the given lexer type).
typedef example5_tokens<lexer_type> example5_tokens;
// this is the iterator type exposed by the lexer
typedef example5_tokens::iterator_type iterator_type;
// this is the type of the grammar to parse
typedef example5_grammar<iterator_type, example5_tokens::lexer_def> example5_grammar;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
example5_tokens tokens; // Our lexer
example5_grammar calc(tokens); // Our parser
std::string str (read_from_file("example5.input"));
// At this point we generate the iterator pair used to expose the
// tokenized input stream.
std::string::iterator it = str.begin();
iterator_type iter = tokens.begin(it, str.end());
iterator_type end = tokens.end();
// Parsing is done based on the token stream, not the character
// stream read from the input.
// Note how we use the lexer defined above as the skip parser. It must
// be explicitly wrapped inside a state directive, switching the lexer
// state for the duration of skipping whitespace.
std::string ws("WS");
bool r = qi::phrase_parse(iter, end, calc, qi::in_state(ws)[tokens.self]);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,16 @@
/* example5.input */
{
if (variable) { a = b ; }
while (10) {
if (2) { b = 10 ; }
if (x) { c = x ; } else { d = 10 ; }
}
variable = 42;
if (variable) { a = b ; } else { }
}

View File

@@ -0,0 +1,248 @@
// Copyright (c) 2001-2010 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)
// This example shows how to create a simple lexer recognizing a couple of
// different tokens aimed at a simple language and how to use this lexer with
// a grammar. It shows how to associate attributes to tokens and how to access the
// token attributes from inside the grammar.
//
// Additionally, this example demonstrates, how to define a token set usable
// as the skip parser during parsing, allowing to define several tokens to be
// ignored.
//
// The example demonstrates how to use the add(...)(...) syntax to associate
// token definitions with the lexer and how token ids can be used in the
// parser to refer to a token, without having to directly reference its
// definition.
//
// This example recognizes a very simple programming language having
// assignment statements and if and while control structures. Look at the file
// example6.input for an example.
//
// This example is essentially identical to example4.cpp. The only difference
// is that we use the self.add() syntax to define tokens and to associate them
// with the lexer.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/phoenix/operator.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include "example.hpp"
using namespace boost::spirit;
using boost::phoenix::val;
///////////////////////////////////////////////////////////////////////////////
// Token id definitions
///////////////////////////////////////////////////////////////////////////////
enum token_ids
{
ID_CONSTANT = 1000,
ID_IF,
ID_ELSE,
ID_WHILE,
ID_IDENTIFIER
};
///////////////////////////////////////////////////////////////////////////////
// Token definitions
///////////////////////////////////////////////////////////////////////////////
template <typename Lexer>
struct example6_tokens : lex::lexer<Lexer>
{
example6_tokens()
{
// define the tokens to match
identifier = "[a-zA-Z_][a-zA-Z0-9_]*";
constant = "[0-9]+";
// associate the tokens and the token set with the lexer
this->self = lex::token_def<>('(') | ')' | '{' | '}' | '=' | ';';
// Token definitions can be added by using some special syntactic
// construct as shown below.
// Note, that the token definitions added this way expose the iterator
// pair pointing to the matched input stream as their attribute.
this->self.add
(constant, ID_CONSTANT)
("if", ID_IF)
("else", ID_ELSE)
("while", ID_WHILE)
(identifier, ID_IDENTIFIER)
;
// define the whitespace to ignore (spaces, tabs, newlines and C-style
// comments) and add those to another lexer state (here: "WS")
this->self("WS")
= lex::token_def<>("[ \\t\\n]+")
| "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/"
;
}
// The following two tokens have an associated attribute type, identifier
// carries a string (the identifier name) and constant carries the matched
// integer value.
//
// Note: any token attribute type explicitly specified in a token_def<>
// declaration needs to be listed during token type definition as
// well (see the typedef for the token_type below).
//
// The conversion of the matched input to an instance of this type occurs
// once (on first access), which makes token attributes as efficient as
// possible. Moreover, token instances are constructed once by the lexer
// library. From this point on tokens are passed by reference only,
// avoiding them being copied around.
lex::token_def<std::string> identifier;
lex::token_def<unsigned int> constant;
};
///////////////////////////////////////////////////////////////////////////////
// Grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator, typename Lexer>
struct example6_grammar
: qi::grammar<Iterator, qi::in_state_skipper<Lexer> >
{
template <typename TokenDef>
example6_grammar(TokenDef const& tok)
: example6_grammar::base_type(program)
{
using boost::spirit::_val;
program
= +block
;
block
= '{' >> *statement >> '}'
;
statement
= assignment
| if_stmt
| while_stmt
;
assignment
= (tok.identifier >> '=' >> expression >> ';')
[
std::cout << val("assignment statement to: ")
<< _1 << "\n"
]
;
if_stmt
= ( token(ID_IF) >> '(' >> expression >> ')' >> block
>> -(token(ID_ELSE) >> block)
)
[
std::cout << val("if expression: ")
<< _2 << "\n"
]
;
while_stmt
= (token(ID_WHILE) >> '(' >> expression >> ')' >> block)
[
std::cout << val("while expression: ")
<< _2 << "\n"
]
;
// since expression has a variant return type accommodating for
// std::string and unsigned integer, both possible values may be
// returned to the calling rule
expression
= tok.identifier [ _val = _1 ]
| tok.constant [ _val = _1 ]
;
}
typedef boost::variant<unsigned int, std::string> expression_type;
qi::rule<Iterator, qi::in_state_skipper<Lexer> > program, block, statement;
qi::rule<Iterator, qi::in_state_skipper<Lexer> > assignment, if_stmt;
qi::rule<Iterator, qi::in_state_skipper<Lexer> > while_stmt;
// the expression is the only rule having a return value
qi::rule<Iterator, expression_type(), qi::in_state_skipper<Lexer> > expression;
};
///////////////////////////////////////////////////////////////////////////////
int main()
{
// iterator type used to expose the underlying input stream
typedef std::string::iterator base_iterator_type;
// This is the lexer token type to use. The second template parameter lists
// all attribute types used for token_def's during token definition (see
// calculator_tokens<> above). Here we use the predefined lexertl token
// type, but any compatible token type may be used instead.
//
// If you don't list any token attribute types in the following declaration
// (or just use the default token type: lexertl_token<base_iterator_type>)
// it will compile and work just fine, just a bit less efficient. This is
// because the token attribute will be generated from the matched input
// sequence every time it is requested. But as soon as you specify at
// least one token attribute type you'll have to list all attribute types
// used for token_def<> declarations in the token definition class above,
// otherwise compilation errors will occur.
typedef lex::lexertl::token<
base_iterator_type, boost::mpl::vector<unsigned int, std::string>
> token_type;
// Here we use the lexertl based lexer engine.
typedef lex::lexertl::lexer<token_type> lexer_type;
// This is the token definition type (derived from the given lexer type).
typedef example6_tokens<lexer_type> example6_tokens;
// this is the iterator type exposed by the lexer
typedef example6_tokens::iterator_type iterator_type;
// this is the type of the grammar to parse
typedef example6_grammar<iterator_type, example6_tokens::lexer_def> example6_grammar;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
example6_tokens tokens; // Our lexer
example6_grammar calc(tokens); // Our parser
std::string str (read_from_file("example6.input"));
// At this point we generate the iterator pair used to expose the
// tokenized input stream.
std::string::iterator it = str.begin();
iterator_type iter = tokens.begin(it, str.end());
iterator_type end = tokens.end();
// Parsing is done based on the token stream, not the character
// stream read from the input.
// Note how we use the lexer defined above as the skip parser. It must
// be explicitly wrapped inside a state directive, switching the lexer
// state for the duration of skipping whitespace.
std::string ws("WS");
bool r = qi::phrase_parse(iter, end, calc, qi::in_state(ws)[tokens.self]);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,17 @@
/* example6.input */
{
if (variable) { a = b ; }
while (10) {
if (2) { b = 10 ; }
if (x) { c = x ; } else { d = 10 ; }
}
variable = 42;
}

View File

@@ -0,0 +1,107 @@
// 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)
// #define BOOST_SPIRIT_LEXERTL_DEBUG 1
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/phoenix.hpp>
namespace lex = boost::spirit::lex;
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
///////////////////////////////////////////////////////////////////////////////
template <typename Lexer>
struct language_tokens : lex::lexer<Lexer>
{
language_tokens()
{
tok_float = "float";
tok_int = "int";
floatlit = "[0-9]+\\.[0-9]*";
intlit = "[0-9]+";
ws = "[ \t\n]+";
identifier = "[a-zA-Z_][a-zA-Z_0-9]*";
this->self = ws [lex::_pass = lex::pass_flags::pass_ignore];
this->self += tok_float | tok_int | floatlit | intlit | identifier;
this->self += lex::char_('=');
}
lex::token_def<> tok_float, tok_int;
lex::token_def<> ws;
lex::token_def<double> floatlit;
lex::token_def<int> intlit;
lex::token_def<> identifier;
};
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct language_grammar : qi::grammar<Iterator>
{
template <typename Lexer>
language_grammar(language_tokens<Lexer> const& tok)
: language_grammar::base_type(declarations)
{
declarations = +number;
number =
tok.tok_float >> tok.identifier >> '=' >> tok.floatlit
| tok.tok_int >> tok.identifier >> '=' >> tok.intlit
;
declarations.name("declarations");
number.name("number");
debug(declarations);
debug(number);
}
qi::rule<Iterator> declarations;
qi::rule<Iterator> number;
};
///////////////////////////////////////////////////////////////////////////////
int main()
{
// iterator type used to expose the underlying input stream
typedef std::string::iterator base_iterator_type;
// lexer type
typedef lex::lexertl::actor_lexer<
lex::lexertl::token<
base_iterator_type, boost::mpl::vector2<double, int>
> > lexer_type;
// iterator type exposed by the lexer
typedef language_tokens<lexer_type>::iterator_type iterator_type;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
language_tokens<lexer_type> tokenizer; // Our lexer
language_grammar<iterator_type> g (tokenizer); // Our parser
// Parsing is done based on the token stream, not the character
// stream read from the input.
std::string str ("float f = 3.4\nint i = 6\n");
base_iterator_type first = str.begin();
bool r = lex::tokenize_and_parse(first, str.end(), tokenizer, g);
if (r) {
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else {
std::string rest(first, str.end());
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \"" << rest << "\"\n";
std::cout << "-------------------------\n";
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,120 @@
// Copyright (c) 2001-2010 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)
// This example is the equivalent to the following lex program:
//
// %{
// #include <stdio.h>
// %}
// %%
// [0-9]+ { printf("%s\n", yytext); }
// .|\n ;
// %%
// main()
// {
// yylex();
// }
//
// Its purpose is to print all the (integer) numbers found in a file
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/phoenix/operator.hpp>
#include <iostream>
#include <string>
#include "example.hpp"
using namespace boost::spirit;
///////////////////////////////////////////////////////////////////////////////
// Token definition: We use the lexertl based lexer engine as the underlying
// lexer type.
///////////////////////////////////////////////////////////////////////////////
template <typename Lexer>
struct print_numbers_tokenids : lex::lexer<Lexer>
{
// define tokens and associate it with the lexer, we set the lexer flags
// not to match newlines while matching a dot, so we need to add the
// '\n' explicitly below
print_numbers_tokenids()
: print_numbers_tokenids::base_type(lex::match_flags::match_not_dot_newline)
{
this->self = lex::token_def<int>("[0-9]+") | ".|\n";
}
};
///////////////////////////////////////////////////////////////////////////////
// Grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct print_numbers_grammar : qi::grammar<Iterator>
{
print_numbers_grammar()
: print_numbers_grammar::base_type(start)
{
// we just know, that the token ids get assigned starting min_token_id
// so, "[0-9]+" gets the id 'min_token_id' and ".|\n" gets the id
// 'min_token_id+1'.
// this prints the token ids of the matched tokens
start = *( qi::tokenid(lex::min_token_id)
| qi::tokenid(lex::min_token_id+1)
)
[ std::cout << _1 << "\n" ]
;
}
qi::rule<Iterator> start;
};
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
// iterator type used to expose the underlying input stream
typedef std::string::iterator base_iterator_type;
// the token type to be used, 'int' is available as the type of the token
// attribute and no lexer state is supported
typedef lex::lexertl::token<base_iterator_type, boost::mpl::vector<int>
, boost::mpl::false_> token_type;
// lexer type
typedef lex::lexertl::lexer<token_type> lexer_type;
// iterator type exposed by the lexer
typedef print_numbers_tokenids<lexer_type>::iterator_type iterator_type;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
print_numbers_tokenids<lexer_type> print_tokens; // Our lexer
print_numbers_grammar<iterator_type> print; // Our parser
// Parsing is done based on the token stream, not the character
// stream read from the input.
std::string str (read_from_file(1 == argc ? "print_numbers.input" : argv[1]));
base_iterator_type first = str.begin();
bool r = lex::tokenize_and_parse(first, str.end(), print_tokens, print);
if (r) {
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else {
std::string rest(first, str.end());
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \"" << rest << "\"\n";
std::cout << "-------------------------\n";
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,117 @@
// Copyright (c) 2001-2010 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)
// This example is the equivalent to the following lex program:
//
// %{
// #include <stdio.h>
// %}
// %%
// [0-9]+ { printf("%s\n", yytext); }
// .|\n ;
// %%
// main()
// {
// yylex();
// }
//
// Its purpose is to print all the (integer) numbers found in a file
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/phoenix/operator.hpp>
#include <iostream>
#include <string>
#include "example.hpp"
using namespace boost::spirit;
///////////////////////////////////////////////////////////////////////////////
// Token definition: We use the lexertl based lexer engine as the underlying
// lexer type.
///////////////////////////////////////////////////////////////////////////////
template <typename Lexer>
struct print_numbers_tokens : lex::lexer<Lexer>
{
// define tokens and associate it with the lexer, we set the lexer flags
// not to match newlines while matching a dot, so we need to add the
// '\n' explicitly below
print_numbers_tokens()
: print_numbers_tokens::base_type(lex::match_flags::match_not_dot_newline)
{
this->self = lex::token_def<int>("[0-9]+") | ".|\n";
}
};
///////////////////////////////////////////////////////////////////////////////
// Grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct print_numbers_grammar : qi::grammar<Iterator>
{
print_numbers_grammar()
: print_numbers_grammar::base_type(start)
{
// we just know, that the token ids get assigned starting min_token_id
// so, "[0-9]+" gets the id 'min_token_id' and ".|\n" gets the id
// 'min_token_id+1'.
start = *( qi::token(lex::min_token_id) [ std::cout << _1 << "\n" ]
| qi::token(lex::min_token_id+1)
)
;
}
qi::rule<Iterator> start;
};
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
// iterator type used to expose the underlying input stream
typedef std::string::iterator base_iterator_type;
// the token type to be used, 'int' is available as the type of the token
// attribute and no lexer state is supported
typedef lex::lexertl::token<base_iterator_type, boost::mpl::vector<int>
, boost::mpl::false_> token_type;
// lexer type
typedef lex::lexertl::lexer<token_type> lexer_type;
// iterator type exposed by the lexer
typedef print_numbers_tokens<lexer_type>::iterator_type iterator_type;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
print_numbers_tokens<lexer_type> print_tokens; // Our lexer
print_numbers_grammar<iterator_type> print; // Our parser
// Parsing is done based on the token stream, not the character
// stream read from the input.
std::string str (read_from_file(1 == argc ? "print_numbers.input" : argv[1]));
base_iterator_type first = str.begin();
bool r = lex::tokenize_and_parse(first, str.end(), print_tokens, print);
if (r) {
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else {
std::string rest(first, str.end());
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \"" << rest << "\"\n";
std::cout << "-------------------------\n";
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,17 @@
/* example6.input */
{
if (variable) { a = b ; }
while (10) {
if (2) { b = 10 ; }
if (x) { c = x ; } else { d = 10 ; }
}
variable = 42;
}

View File

@@ -0,0 +1,30 @@
/*=============================================================================
Copyright (c) 2001-2011 Hartmut Kaiser
http://spirit.sourceforge.net/
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)
=============================================================================*/
//[reference_lex_includes
#include <boost/spirit/include/lex.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <iostream>
#include <string>
//]
//[reference_lex_test
//]
int main()
{
{
//[reference_lex_using_declarations_char
//]
//[reference_lex_char
//]
}
return 0;
}

View File

@@ -0,0 +1,15 @@
#==============================================================================
# Copyright (c) 2001-2009 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)
#==============================================================================
project spirit-static-lexer-example ;
exe word_count_generate : word_count_generate.cpp ;
exe word_count_static : word_count_static.cpp ;
exe word_count_lexer_generate : word_count_lexer_generate.cpp ;
exe word_count_lexer_static : word_count_lexer_static.cpp ;

View File

@@ -0,0 +1,7 @@
Our hiking boots are ready. So, let's pack!
Have you the plane tickets for there and back?
I do, I do. We're all ready to go. Grab my hand and be my beau.

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2001-2010 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)
// The purpose of this example is to show, how it is possible to use a lexer
// token definition for two purposes:
//
// . To generate C++ code implementing a static lexical analyzer allowing
// to recognize all defined tokens (this file)
// . To integrate the generated C++ lexer into the /Spirit/ framework.
// (see the file: word_count_static.cpp)
// #define BOOST_SPIRIT_LEXERTL_DEBUG
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/lex_generate_static_lexertl.hpp>
#include <fstream>
#include "word_count_tokens.hpp"
using namespace boost::spirit;
///////////////////////////////////////////////////////////////////////////////
//[wc_static_generate_main
int main(int argc, char* argv[])
{
// create the lexer object instance needed to invoke the generator
word_count_tokens<lex::lexertl::lexer<> > word_count; // the token definition
// open the output file, where the generated tokenizer function will be
// written to
std::ofstream out(argc < 2 ? "word_count_static.hpp" : argv[1]);
// invoke the generator, passing the token definition, the output stream
// and the name suffix of the tables and functions to be generated
//
// The suffix "wc" used below results in a type lexertl::static_::lexer_wc
// to be generated, which needs to be passed as a template parameter to the
// lexertl::static_lexer template (see word_count_static.cpp).
return lex::lexertl::generate_static_dfa(word_count, out, "wc") ? 0 : -1;
}
//]

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2001-2010 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)
// The purpose of this example is to show, how it is possible to use a lexer
// token definition for two purposes:
//
// . To generate C++ code implementing a static lexical analyzer allowing
// to recognize all defined tokens (this file)
// . To integrate the generated C++ lexer into the /Spirit/ framework.
// (see the file: word_count_lexer_static.cpp)
// #define BOOST_SPIRIT_LEXERTL_DEBUG
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/lex_generate_static_lexertl.hpp>
#include <fstream>
#include "word_count_lexer_tokens.hpp"
using namespace boost::spirit;
///////////////////////////////////////////////////////////////////////////////
//[wcl_static_generate_main
int main(int argc, char* argv[])
{
// create the lexer object instance needed to invoke the generator
word_count_lexer_tokens<lex::lexertl::actor_lexer<> > word_count; // the token definition
// open the output file, where the generated tokenizer function will be
// written to
std::ofstream out(argc < 2 ? "word_count_lexer_static.hpp" : argv[1]);
// invoke the generator, passing the token definition, the output stream
// and the name prefix of the tokenizing function to be generated
//
// The suffix "wcl" used below results in a type lexertl::static_::lexer_wcl
// to be generated, which needs to be passed as a template parameter to the
// lexertl::static_lexer template (see word_count_lexer_static.cpp).
return lex::lexertl::generate_static_dfa(word_count, out, "wcl") ? 0 : -1;
}
//]

View File

@@ -0,0 +1,83 @@
// Copyright (c) 2001-2010 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)
// The purpose of this example is to show, how it is possible to use a lexer
// token definition for two purposes:
//
// . To generate C++ code implementing a static lexical analyzer allowing
// to recognize all defined tokens
// . To integrate the generated C++ lexer into the /Spirit/ framework.
//
// #define BOOST_SPIRIT_DEBUG
// #define BOOST_SPIRIT_LEXERTL_DEBUG
#include <boost/spirit/include/lex_static_lexertl.hpp>
#include <iostream>
#include <string>
#include "../example.hpp"
#include "word_count_lexer_tokens.hpp" // token definition
#include "word_count_lexer_static.hpp" // generated tokenizer
using namespace boost::spirit;
///////////////////////////////////////////////////////////////////////////////
//[wcl_static_main
int main(int argc, char* argv[])
{
// read input from the given file
std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1]));
// Specifying 'omit' as the token attribute type generates a token class
// notholding any token attribute at all (not even the iterator_range of the
// matched input sequence), therefor optimizing the token, the lexer, and
// possibly the parser implementation as much as possible.
//
// Specifying mpl::false_ as the 3rd template parameter generates a token
// type and an iterator, both holding no lexer state, allowing for even more
// aggressive optimizations.
//
// As a result the token instances contain the token ids as the only data
// member.
typedef lex::lexertl::token<char const*, lex::omit, boost::mpl::false_> token_type;
// Define the lexer type to be used as the base class for our token
// definition.
//
// This is the only place where the code is different from an equivalent
// dynamic lexical analyzer. We use the `lexertl::static_lexer<>` instead of
// the `lexertl::lexer<>` as the base class for our token definition type.
//
// As we specified the suffix "wcl" while generating the static tables we
// need to pass the type lexertl::static_::lexer_wcl as the second template
// parameter below (see word_count_lexer_generate.cpp).
typedef lex::lexertl::static_actor_lexer<
token_type, lex::lexertl::static_::lexer_wcl
> lexer_type;
// create the lexer object instance needed to invoke the lexical analysis
word_count_lexer_tokens<lexer_type> word_count_lexer;
// tokenize the given string, all generated tokens are discarded
char const* first = str.c_str();
char const* last = &first[str.size()];
bool r = lex::tokenize(first, last, word_count_lexer);
if (r) {
std::cout << "lines: " << word_count_lexer.l
<< ", words: " << word_count_lexer.w
<< ", characters: " << word_count_lexer.c
<< "\n";
}
else {
std::string rest(first, last);
std::cout << "Lexical analysis failed\n" << "stopped at: \""
<< rest << "\"\n";
}
return 0;
}
//]

View File

@@ -0,0 +1,164 @@
// Copyright (c) 2008-2009 Ben Hanson
// Copyright (c) 2008-2010 Hartmut Kaiser
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file licence_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// Auto-generated by boost::lexer, do not edit
#if !defined(BOOST_SPIRIT_LEXER_NEXT_TOKEN_WCL_NOV_10_2009_17_20_29)
#define BOOST_SPIRIT_LEXER_NEXT_TOKEN_WCL_NOV_10_2009_17_20_29
#include <boost/spirit/home/support/detail/lexer/char_traits.hpp>
////////////////////////////////////////////////////////////////////////////////
// the generated table of state names and the tokenizer have to be
// defined in the boost::spirit::lex::lexertl::static_ namespace
namespace boost { namespace spirit { namespace lex { namespace lexertl { namespace static_ {
////////////////////////////////////////////////////////////////////////////////
// this table defines the names of the lexer states
char const* const lexer_state_names_wcl[1] =
{
"INITIAL"
};
////////////////////////////////////////////////////////////////////////////////
// this variable defines the number of lexer states
std::size_t const lexer_state_count_wcl = 1;
////////////////////////////////////////////////////////////////////////////////
// this function returns the next matched token
template<typename Iterator>
std::size_t next_token_wcl (std::size_t& /*start_state_*/, bool& /*bol_*/,
Iterator &start_token_, Iterator const& end_, std::size_t& unique_id_)
{
enum {end_state_index, id_index, unique_id_index, state_index, bol_index,
eol_index, dead_state_index, dfa_offset};
static const std::size_t npos = static_cast<std::size_t>(~0);
static const std::size_t lookup_[256] = {
9, 9, 9, 9, 9, 9, 9, 9,
9, 8, 7, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
8, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9 };
static const std::size_t dfa_alphabet_ = 10;
static const std::size_t dfa_[50] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 3, 4, 2, 1, 65536, 0, 0,
0, 0, 0, 0, 0, 2, 1, 65537,
1, 0, 0, 0, 0, 0, 0, 0,
1, 65538, 2, 0, 0, 0, 0, 0,
0, 0 };
if (start_token_ == end_)
{
unique_id_ = npos;
return 0;
}
std::size_t const* ptr_ = dfa_ + dfa_alphabet_;
Iterator curr_ = start_token_;
bool end_state_ = *ptr_ != 0;
std::size_t id_ = *(ptr_ + id_index);
std::size_t uid_ = *(ptr_ + unique_id_index);
Iterator end_token_ = start_token_;
while (curr_ != end_)
{
std::size_t const state_ =
ptr_[lookup_[static_cast<unsigned char>(*curr_++)]];
if (state_ == 0) break;
ptr_ = &dfa_[state_ * dfa_alphabet_];
if (*ptr_)
{
end_state_ = true;
id_ = *(ptr_ + id_index);
uid_ = *(ptr_ + unique_id_index);
end_token_ = curr_;
}
}
if (end_state_)
{
// return longest match
start_token_ = end_token_;
}
else
{
id_ = npos;
uid_ = npos;
}
unique_id_ = uid_;
return id_;
}
////////////////////////////////////////////////////////////////////////////////
// this defines a generic accessors for the information above
struct lexer_wcl
{
// version number and feature-set of compatible static lexer engine
enum
{
static_version = 65536,
supports_bol = false,
supports_eol = false
};
// return the number of lexer states
static std::size_t state_count()
{
return lexer_state_count_wcl;
}
// return the name of the lexer state as given by 'idx'
static char const* state_name(std::size_t idx)
{
return lexer_state_names_wcl[idx];
}
// return the next matched token
template<typename Iterator>
static std::size_t next(std::size_t &start_state_, bool& bol_
, Iterator &start_token_, Iterator const& end_, std::size_t& unique_id_)
{
return next_token_wcl(start_state_, bol_, start_token_, end_, unique_id_);
}
};
}}}}} // namespace boost::spirit::lex::lexertl::static_
#endif

View File

@@ -0,0 +1,62 @@
// Copyright (c) 2001-2010 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)
#if !defined(SPIRIT_LEXER_EXAMPLE_WORD_COUNT_LEXER_TOKENS_FEB_10_2008_0739PM)
#define SPIRIT_LEXER_EXAMPLE_WORD_COUNT_LEXER_TOKENS_FEB_10_2008_0739PM
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/statement.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/iterator/iterator_traits.hpp>
///////////////////////////////////////////////////////////////////////////////
// Token definition: We use the lexertl based lexer engine as the underlying
// lexer type.
//
// Note, the token definition type is derived from the 'lexertl_actor_lexer'
// template, which is a necessary to being able to use lexer semantic actions.
///////////////////////////////////////////////////////////////////////////////
struct distance_func
{
template <typename Iterator1, typename Iterator2>
struct result : boost::iterator_difference<Iterator1> {};
template <typename Iterator1, typename Iterator2>
typename result<Iterator1, Iterator2>::type
operator()(Iterator1& begin, Iterator2& end) const
{
return std::distance(begin, end);
}
};
boost::phoenix::function<distance_func> const distance = distance_func();
//[wcl_static_token_definition
template <typename Lexer>
struct word_count_lexer_tokens : boost::spirit::lex::lexer<Lexer>
{
word_count_lexer_tokens()
: c(0), w(0), l(0)
, word("[^ \t\n]+") // define tokens
, eol("\n")
, any(".")
{
using boost::spirit::lex::_start;
using boost::spirit::lex::_end;
using boost::phoenix::ref;
// associate tokens with the lexer
this->self
= word [++ref(w), ref(c) += distance(_start, _end)]
| eol [++ref(c), ++ref(l)]
| any [++ref(c)]
;
}
std::size_t c, w, l;
boost::spirit::lex::token_def<> word, eol, any;
};
//]
#endif

View File

@@ -0,0 +1,119 @@
// Copyright (c) 2001-2010 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)
// The purpose of this example is to show, how it is possible to use a lexer
// token definition for two purposes:
//
// . To generate C++ code implementing a static lexical analyzer allowing
// to recognize all defined tokens
// . To integrate the generated C++ lexer into the /Spirit/ framework.
//
// #define BOOST_SPIRIT_LEXERTL_DEBUG
#define BOOST_VARIANT_MINIMIZE_SIZE
#include <boost/spirit/include/qi.hpp>
//[wc_static_include
#include <boost/spirit/include/lex_static_lexertl.hpp>
//]
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/statement.hpp>
#include <boost/phoenix/stl/container.hpp>
#include <iostream>
#include <string>
#include "../example.hpp"
#include "word_count_tokens.hpp" // token definition
#include "word_count_static.hpp" // generated tokenizer
using namespace boost::spirit;
using namespace boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
// Grammar definition
///////////////////////////////////////////////////////////////////////////////
//[wc_static_grammar
// This is an ordinary grammar definition following the rules defined by
// Spirit.Qi. There is nothing specific about it, except it gets the token
// definition class instance passed to the constructor to allow accessing the
// embedded token_def<> instances.
template <typename Iterator>
struct word_count_grammar : qi::grammar<Iterator>
{
template <typename TokenDef>
word_count_grammar(TokenDef const& tok)
: word_count_grammar::base_type(start)
, c(0), w(0), l(0)
{
using boost::phoenix::ref;
using boost::phoenix::size;
// associate the defined tokens with the lexer, at the same time
// defining the actions to be executed
start = *( tok.word [ ++ref(w), ref(c) += size(_1) ]
| lit('\n') [ ++ref(l), ++ref(c) ]
| qi::token(IDANY) [ ++ref(c) ]
)
;
}
std::size_t c, w, l; // counter for characters, words, and lines
qi::rule<Iterator> start;
};
//]
///////////////////////////////////////////////////////////////////////////////
//[wc_static_main
int main(int argc, char* argv[])
{
// Define the token type to be used: 'std::string' is available as the type
// of the token value.
typedef lex::lexertl::token<
char const*, boost::mpl::vector<std::string>
> token_type;
// Define the lexer type to be used as the base class for our token
// definition.
//
// This is the only place where the code is different from an equivalent
// dynamic lexical analyzer. We use the `lexertl::static_lexer<>` instead of
// the `lexertl::lexer<>` as the base class for our token definition type.
//
// As we specified the suffix "wc" while generating the static tables we
// need to pass the type lexertl::static_::lexer_wc as the second template
// parameter below (see word_count_generate.cpp).
typedef lex::lexertl::static_lexer<
token_type, lex::lexertl::static_::lexer_wc
> lexer_type;
// Define the iterator type exposed by the lexer.
typedef word_count_tokens<lexer_type>::iterator_type iterator_type;
// Now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process.
word_count_tokens<lexer_type> word_count; // Our lexer
word_count_grammar<iterator_type> g (word_count); // Our parser
// Read in the file into memory.
std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1]));
char const* first = str.c_str();
char const* last = &first[str.size()];
// Parsing is done based on the token stream, not the character stream.
bool r = lex::tokenize_and_parse(first, last, word_count, g);
if (r) { // success
std::cout << "lines: " << g.l << ", words: " << g.w
<< ", characters: " << g.c << "\n";
}
else {
std::string rest(first, last);
std::cerr << "Parsing failed\n" << "stopped at: \""
<< rest << "\"\n";
}
return 0;
}
//]

View File

@@ -0,0 +1,164 @@
// Copyright (c) 2008-2009 Ben Hanson
// Copyright (c) 2008-2010 Hartmut Kaiser
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file licence_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// Auto-generated by boost::lexer, do not edit
#if !defined(BOOST_SPIRIT_LEXER_NEXT_TOKEN_WC_NOV_10_2009_17_20_04)
#define BOOST_SPIRIT_LEXER_NEXT_TOKEN_WC_NOV_10_2009_17_20_04
#include <boost/spirit/home/support/detail/lexer/char_traits.hpp>
////////////////////////////////////////////////////////////////////////////////
// the generated table of state names and the tokenizer have to be
// defined in the boost::spirit::lex::lexertl::static_ namespace
namespace boost { namespace spirit { namespace lex { namespace lexertl { namespace static_ {
////////////////////////////////////////////////////////////////////////////////
// this table defines the names of the lexer states
char const* const lexer_state_names_wc[1] =
{
"INITIAL"
};
////////////////////////////////////////////////////////////////////////////////
// this variable defines the number of lexer states
std::size_t const lexer_state_count_wc = 1;
////////////////////////////////////////////////////////////////////////////////
// this function returns the next matched token
template<typename Iterator>
std::size_t next_token_wc (std::size_t& /*start_state_*/, bool& /*bol_*/,
Iterator &start_token_, Iterator const& end_, std::size_t& unique_id_)
{
enum {end_state_index, id_index, unique_id_index, state_index, bol_index,
eol_index, dead_state_index, dfa_offset};
static const std::size_t npos = static_cast<std::size_t>(~0);
static const std::size_t lookup_[256] = {
8, 8, 8, 8, 8, 8, 8, 8,
8, 9, 7, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
9, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8 };
static const std::size_t dfa_alphabet_ = 10;
static const std::size_t dfa_[50] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 3, 2, 4, 1, 65536, 0, 0,
0, 0, 0, 0, 2, 0, 1, 10,
1, 0, 0, 0, 0, 0, 0, 0,
1, 65537, 2, 0, 0, 0, 0, 0,
0, 0 };
if (start_token_ == end_)
{
unique_id_ = npos;
return 0;
}
std::size_t const* ptr_ = dfa_ + dfa_alphabet_;
Iterator curr_ = start_token_;
bool end_state_ = *ptr_ != 0;
std::size_t id_ = *(ptr_ + id_index);
std::size_t uid_ = *(ptr_ + unique_id_index);
Iterator end_token_ = start_token_;
while (curr_ != end_)
{
std::size_t const state_ =
ptr_[lookup_[static_cast<unsigned char>(*curr_++)]];
if (state_ == 0) break;
ptr_ = &dfa_[state_ * dfa_alphabet_];
if (*ptr_)
{
end_state_ = true;
id_ = *(ptr_ + id_index);
uid_ = *(ptr_ + unique_id_index);
end_token_ = curr_;
}
}
if (end_state_)
{
// return longest match
start_token_ = end_token_;
}
else
{
id_ = npos;
uid_ = npos;
}
unique_id_ = uid_;
return id_;
}
////////////////////////////////////////////////////////////////////////////////
// this defines a generic accessors for the information above
struct lexer_wc
{
// version number and feature-set of compatible static lexer engine
enum
{
static_version = 65536,
supports_bol = false,
supports_eol = false
};
// return the number of lexer states
static std::size_t state_count()
{
return lexer_state_count_wc;
}
// return the name of the lexer state as given by 'idx'
static char const* state_name(std::size_t idx)
{
return lexer_state_names_wc[idx];
}
// return the next matched token
template<typename Iterator>
static std::size_t next(std::size_t &start_state_, bool& bol_
, Iterator &start_token_, Iterator const& end_, std::size_t& unique_id_)
{
return next_token_wc(start_state_, bol_, start_token_, end_, unique_id_);
}
};
}}}}} // namespace boost::spirit::lex::lexertl::static_
#endif

View File

@@ -0,0 +1,41 @@
// Copyright (c) 2001-2010 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)
#if !defined(SPIRIT_LEXER_EXAMPLE_WORD_COUNT_TOKENS_FEB_10_2008_0739PM)
#define SPIRIT_LEXER_EXAMPLE_WORD_COUNT_TOKENS_FEB_10_2008_0739PM
///////////////////////////////////////////////////////////////////////////////
// Token definition: We keep the base class for the token definition as a
// template parameter to allow this class to be used for
// both: the code generation and the lexical analysis
///////////////////////////////////////////////////////////////////////////////
//[wc_static_tokenids
enum tokenids
{
IDANY = boost::spirit::lex::min_token_id + 1,
};
//]
//[wc_static_tokendef
// This token definition class can be used without any change for all three
// possible use cases: a dynamic lexical analyzer, a code generator, and a
// static lexical analyzer.
template <typename BaseLexer>
struct word_count_tokens : boost::spirit::lex::lexer<BaseLexer>
{
word_count_tokens()
: word_count_tokens::base_type(
boost::spirit::lex::match_flags::match_not_dot_newline)
{
// define tokens and associate them with the lexer
word = "[^ \t\n]+";
this->self = word | '\n' | boost::spirit::lex::token_def<>(".", IDANY);
}
boost::spirit::lex::token_def<std::string> word;
};
//]
#endif

View File

@@ -0,0 +1,162 @@
// Copyright (c) 2001-2010 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)
// This example is the equivalent to the following lex program:
//
// %{
// /* INITIAL is the default start state. COMMENT is our new */
// /* state where we remove comments. */
// %}
//
// %s COMMENT
// %%
// <INITIAL>"//".* ;
// <INITIAL>"/*" BEGIN COMMENT;
// <INITIAL>. ECHO;
// <INITIAL>[\n] ECHO;
// <COMMENT>"*/" BEGIN INITIAL;
// <COMMENT>. ;
// <COMMENT>[\n] ;
// %%
//
// main()
// {
// yylex();
// }
//
// Its purpose is to strip comments out of C code.
//
// Additionally this example demonstrates the use of lexer states to structure
// the lexer definition.
// #define BOOST_SPIRIT_LEXERTL_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/stl/container.hpp>
#include <iostream>
#include <string>
#include "example.hpp"
using namespace boost::spirit;
///////////////////////////////////////////////////////////////////////////////
// Token definition: We use the lexertl based lexer engine as the underlying
// lexer type.
///////////////////////////////////////////////////////////////////////////////
enum tokenids
{
IDANY = lex::min_token_id + 10
};
template <typename Lexer>
struct strip_comments_tokens : lex::lexer<Lexer>
{
strip_comments_tokens()
: strip_comments_tokens::base_type(lex::match_flags::match_default)
{
// define tokens and associate them with the lexer
cppcomment = "\"//\"[^\n]*"; // '//[^\n]*'
ccomment = "\"/*\""; // '/*'
endcomment = "\"*/\""; // '*/'
// The following tokens are associated with the default lexer state
// (the "INITIAL" state). Specifying 'INITIAL' as a lexer state is
// strictly optional.
this->self.add
(cppcomment) // no explicit token id is associated
(ccomment)
(".", IDANY) // IDANY is the token id associated with this token
// definition
;
// The following tokens are associated with the lexer state "COMMENT".
// We switch lexer states from inside the parsing process using the
// in_state("COMMENT")[] parser component as shown below.
this->self("COMMENT").add
(endcomment)
(".", IDANY)
;
}
lex::token_def<> cppcomment, ccomment, endcomment;
};
///////////////////////////////////////////////////////////////////////////////
// Grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct strip_comments_grammar : qi::grammar<Iterator>
{
template <typename TokenDef>
strip_comments_grammar(TokenDef const& tok)
: strip_comments_grammar::base_type(start)
{
// The in_state("COMMENT")[...] parser component switches the lexer
// state to be 'COMMENT' during the matching of the embedded parser.
start = *( tok.ccomment
>> qi::in_state("COMMENT")
[
// the lexer is in the 'COMMENT' state during
// matching of the following parser components
*token(IDANY) >> tok.endcomment
]
| tok.cppcomment
| qi::token(IDANY) [ std::cout << _1 ]
)
;
}
qi::rule<Iterator> start;
};
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
// iterator type used to expose the underlying input stream
typedef std::string::iterator base_iterator_type;
// lexer type
typedef
lex::lexertl::lexer<lex::lexertl::token<base_iterator_type> >
lexer_type;
// iterator type exposed by the lexer
typedef strip_comments_tokens<lexer_type>::iterator_type iterator_type;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
strip_comments_tokens<lexer_type> strip_comments; // Our lexer
strip_comments_grammar<iterator_type> g (strip_comments); // Our parser
// Parsing is done based on the token stream, not the character
// stream read from the input.
std::string str (read_from_file(1 == argc ? "strip_comments.input" : argv[1]));
base_iterator_type first = str.begin();
bool r = lex::tokenize_and_parse(first, str.end(), strip_comments, g);
if (r) {
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else {
std::string rest(first, str.end());
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \"" << rest << "\"\n";
std::cout << "-------------------------\n";
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,161 @@
// Copyright (c) 2001-2009 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)
// This example is the equivalent to the following lex program:
//
// %{
// /* INITIAL is the default start state. COMMENT is our new */
// /* state where we remove comments. */
// %}
//
// %s COMMENT
// %%
// <INITIAL>"//".* ;
// <INITIAL>"/*" BEGIN COMMENT;
// <INITIAL>. ECHO;
// <INITIAL>[\n] ECHO;
// <COMMENT>"*/" BEGIN INITIAL;
// <COMMENT>. ;
// <COMMENT>[\n] ;
// %%
//
// main()
// {
// yylex();
// }
//
// Its purpose is to strip comments out of C code.
//
// Additionally this example demonstrates the use of lexer states to structure
// the lexer definition.
// #define BOOST_SPIRIT_LEXERTL_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexer_lexertl.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_container.hpp>
#include <iostream>
#include <string>
#include "example.hpp"
using namespace boost::spirit;
using namespace boost::spirit::qi;
using namespace boost::spirit::lex;
///////////////////////////////////////////////////////////////////////////////
// Token definition: We use the lexertl based lexer engine as the underlying
// lexer type.
///////////////////////////////////////////////////////////////////////////////
enum tokenids
{
IDANY = lex::min_token_id + 10
};
template <typename Lexer>
struct strip_comments_tokens : lexer<Lexer>
{
strip_comments_tokens()
{
// define tokens and associate them with the lexer
cppcomment = "//[^\n]*";
ccomment = "/\\*";
endcomment = "\\*/";
// The following tokens are associated with the default lexer state
// (the "INITIAL" state). Specifying 'INITIAL' as a lexer state is
// strictly optional.
this->self.add
(cppcomment) // no explicit token id is associated
(ccomment)
(".", IDANY) // IDANY is the token id associated with this token
// definition
;
// The following tokens are associated with the lexer state "COMMENT".
// We switch lexer states from inside the parsing process using the
// in_state("COMMENT")[] parser component as shown below.
this->self("COMMENT").add
(endcomment)
(".", IDANY)
;
}
token_def<> cppcomment, ccomment, endcomment;
};
///////////////////////////////////////////////////////////////////////////////
// Grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct strip_comments_grammar : grammar<Iterator>
{
template <typename TokenDef>
strip_comments_grammar(TokenDef const& tok)
: strip_comments_grammar::base_type(start)
{
// The in_state("COMMENT")[...] parser component switches the lexer
// state to be 'COMMENT' during the matching of the embedded parser.
start = *( tok.ccomment
>> in_state("COMMENT")
[
// the lexer is in the 'COMMENT' state during
// matching of the following parser components
*token(IDANY) >> tok.endcomment
]
| tok.cppcomment
| token(IDANY) [ std::cout << _1 ]
)
;
}
rule<Iterator> start;
};
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
// iterator type used to expose the underlying input stream
typedef std::string::iterator base_iterator_type;
// lexer type
typedef lexertl::lexer<lexertl::token<base_iterator_type> > lexer_type;
// iterator type exposed by the lexer
typedef strip_comments_tokens<lexer_type>::iterator_type iterator_type;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
strip_comments_tokens<lexer_type> strip_comments; // Our lexer
strip_comments_grammar<iterator_type> g (strip_comments); // Our grammar
// Parsing is done based on the token stream, not the character
// stream read from the input.
std::string str (read_from_file(1 == argc ? "strip_comments.input" : argv[1]));
base_iterator_type first = str.begin();
bool r = tokenize_and_parse(first, str.end(), strip_comments, g);
if (r) {
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else {
std::string rest(first, str.end());
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \"" << rest << "\"\n";
std::cout << "-------------------------\n";
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,171 @@
// Copyright (c) 2001-2010 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)
// This example is the equivalent to the following lex program:
//
// %{
// /* INITIAL is the default start state. COMMENT is our new */
// /* state where we remove comments. */
// %}
//
// %s COMMENT
// %%
// <INITIAL>"//".* ;
// <INITIAL>"/*" BEGIN COMMENT;
// <INITIAL>. ECHO;
// <INITIAL>[\n] ECHO;
// <COMMENT>"*/" BEGIN INITIAL;
// <COMMENT>. ;
// <COMMENT>[\n] ;
// %%
//
// main()
// {
// yylex();
// }
//
// Its purpose is to strip comments out of C code.
//
// Additionally this example demonstrates the use of lexer states to structure
// the lexer definition.
// #define BOOST_SPIRIT_LEXERTL_DEBUG
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/statement.hpp>
#include <boost/phoenix/core.hpp>
#include <iostream>
#include <string>
#include "example.hpp"
using namespace boost::spirit;
///////////////////////////////////////////////////////////////////////////////
// Token definition: We use the lexertl based lexer engine as the underlying
// lexer type.
///////////////////////////////////////////////////////////////////////////////
enum tokenids
{
IDANY = lex::min_token_id + 10,
IDEOL = lex::min_token_id + 11
};
///////////////////////////////////////////////////////////////////////////////
// Simple custom semantic action function object used to print the matched
// input sequence for a particular token
template <typename Char, typename Traits>
struct echo_input_functor
{
echo_input_functor (std::basic_ostream<Char, Traits>& os_)
: os(os_) {}
// This is called by the semantic action handling code during the lexing
template <typename Iterator, typename Context>
void operator()(Iterator const& b, Iterator const& e
, BOOST_SCOPED_ENUM(boost::spirit::lex::pass_flags)&
, std::size_t&, Context&) const
{
os << std::string(b, e);
}
std::basic_ostream<Char, Traits>& os;
};
template <typename Char, typename Traits>
inline echo_input_functor<Char, Traits>
echo_input(std::basic_ostream<Char, Traits>& os)
{
return echo_input_functor<Char, Traits>(os);
}
///////////////////////////////////////////////////////////////////////////////
// Another simple custom semantic action function object used to switch the
// state of the lexer
struct set_lexer_state
{
set_lexer_state(char const* state_)
: state(state_) {}
// This is called by the semantic action handling code during the lexing
template <typename Iterator, typename Context>
void operator()(Iterator const&, Iterator const&
, BOOST_SCOPED_ENUM(boost::spirit::lex::pass_flags)&
, std::size_t&, Context& ctx) const
{
ctx.set_state_name(state.c_str());
}
std::string state;
};
///////////////////////////////////////////////////////////////////////////////
template <typename Lexer>
struct strip_comments_tokens : lex::lexer<Lexer>
{
strip_comments_tokens()
: strip_comments_tokens::base_type(lex::match_flags::match_default)
{
// define tokens and associate them with the lexer
cppcomment = "\"//\"[^\n]*"; // '//[^\n]*'
ccomment = "\"/*\""; // '/*'
endcomment = "\"*/\""; // '*/'
any = std::string(".");
eol = "\n";
// The following tokens are associated with the default lexer state
// (the "INITIAL" state). Specifying 'INITIAL' as a lexer state is
// strictly optional.
this->self
= cppcomment
| ccomment [ set_lexer_state("COMMENT") ]
| eol [ echo_input(std::cout) ]
| any [ echo_input(std::cout) ]
;
// The following tokens are associated with the lexer state 'COMMENT'.
this->self("COMMENT")
= endcomment [ set_lexer_state("INITIAL") ]
| "\n"
| std::string(".")
;
}
lex::token_def<> cppcomment, ccomment, endcomment, any, eol;
};
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
// iterator type used to expose the underlying input stream
typedef std::string::iterator base_iterator_type;
// lexer type
typedef
lex::lexertl::actor_lexer<lex::lexertl::token<base_iterator_type> >
lexer_type;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
strip_comments_tokens<lexer_type> strip_comments; // Our lexer
// No parsing is done altogether, everything happens in the lexer semantic
// actions.
std::string str (read_from_file(1 == argc ? "strip_comments.input" : argv[1]));
base_iterator_type first = str.begin();
bool r = lex::tokenize(first, str.end(), strip_comments);
if (!r) {
std::string rest(first, str.end());
std::cerr << "Lexical analysis failed\n" << "stopped at: \""
<< rest << "\"\n";
}
return 0;
}

View File

@@ -0,0 +1,165 @@
// Copyright (c) 2001-2010 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)
// This example is the equivalent to the following lex program:
/*
//[wcp_flex_version
%{
int c = 0, w = 0, l = 0;
%}
word [^ \t\n]+
eol \n
%%
{word} { ++w; c += yyleng; }
{eol} { ++c; ++l; }
. { ++c; }
%%
main()
{
yylex();
printf("%d %d %d\n", l, w, c);
}
//]
*/
// Its purpose is to do the word count function of the wc command in UNIX. It
// prints the number of lines, words and characters in a file.
//
// The example additionally demonstrates how to use the add_pattern(...)(...)
// syntax to define lexer patterns. These patterns are essentially parameter-
// less 'macros' for regular expressions, allowing to simplify their
// definition.
// #define BOOST_SPIRIT_LEXERTL_DEBUG
#define BOOST_VARIANT_MINIMIZE_SIZE
//[wcp_includes
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/statement.hpp>
#include <boost/phoenix/stl/container.hpp>
//]
#include <iostream>
#include <string>
#include "example.hpp"
//[wcp_namespaces
using namespace boost::spirit;
using namespace boost::spirit::ascii;
//]
///////////////////////////////////////////////////////////////////////////////
// Token definition: We use the lexertl based lexer engine as the underlying
// lexer type.
///////////////////////////////////////////////////////////////////////////////
//[wcp_token_ids
enum tokenids
{
IDANY = lex::min_token_id + 10
};
//]
//[wcp_token_definition
template <typename Lexer>
struct word_count_tokens : lex::lexer<Lexer>
{
word_count_tokens()
{
// define patterns (lexer macros) to be used during token definition
// below
this->self.add_pattern
("WORD", "[^ \t\n]+")
;
// define tokens and associate them with the lexer
word = "{WORD}"; // reference the pattern 'WORD' as defined above
// this lexer will recognize 3 token types: words, newlines, and
// everything else
this->self.add
(word) // no token id is needed here
('\n') // characters are usable as tokens as well
(".", IDANY) // string literals will not be escaped by the library
;
}
// the token 'word' exposes the matched string as its parser attribute
lex::token_def<std::string> word;
};
//]
///////////////////////////////////////////////////////////////////////////////
// Grammar definition
///////////////////////////////////////////////////////////////////////////////
//[wcp_grammar_definition
template <typename Iterator>
struct word_count_grammar : qi::grammar<Iterator>
{
template <typename TokenDef>
word_count_grammar(TokenDef const& tok)
: word_count_grammar::base_type(start)
, c(0), w(0), l(0)
{
using boost::phoenix::ref;
using boost::phoenix::size;
start = *( tok.word [++ref(w), ref(c) += size(_1)]
| lit('\n') [++ref(c), ++ref(l)]
| qi::token(IDANY) [++ref(c)]
)
;
}
std::size_t c, w, l;
qi::rule<Iterator> start;
};
//]
///////////////////////////////////////////////////////////////////////////////
//[wcp_main
int main(int argc, char* argv[])
{
/*< Define the token type to be used: `std::string` is available as the
type of the token attribute
>*/ typedef lex::lexertl::token<
char const*, boost::mpl::vector<std::string>
> token_type;
/*< Define the lexer type to use implementing the state machine
>*/ typedef lex::lexertl::lexer<token_type> lexer_type;
/*< Define the iterator type exposed by the lexer type
>*/ typedef word_count_tokens<lexer_type>::iterator_type iterator_type;
// now we use the types defined above to create the lexer and grammar
// object instances needed to invoke the parsing process
word_count_tokens<lexer_type> word_count; // Our lexer
word_count_grammar<iterator_type> g (word_count); // Our parser
// read in the file int memory
std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1]));
char const* first = str.c_str();
char const* last = &first[str.size()];
/*< Parsing is done based on the token stream, not the character
stream read from the input. The function `tokenize_and_parse()` wraps
the passed iterator range `[first, last)` by the lexical analyzer and
uses its exposed iterators to parse the token stream.
>*/ bool r = lex::tokenize_and_parse(first, last, word_count, g);
if (r) {
std::cout << "lines: " << g.l << ", words: " << g.w
<< ", characters: " << g.c << "\n";
}
else {
std::string rest(first, last);
std::cerr << "Parsing failed\n" << "stopped at: \""
<< rest << "\"\n";
}
return 0;
}
//]

View File

@@ -0,0 +1,7 @@
Our hiking boots are ready. So, let's pack!
Have you the plane tickets for there and back?
I do, I do. We're all ready to go. Grab my hand and be my beau.

View File

@@ -0,0 +1,183 @@
// Copyright (c) 2001-2010 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)
// This example is the equivalent to the following flex program:
/*
//[wcf_flex_version
%{
#define ID_WORD 1000
#define ID_EOL 1001
#define ID_CHAR 1002
int c = 0, w = 0, l = 0;
%}
%%
[^ \t\n]+ { return ID_WORD; }
\n { return ID_EOL; }
. { return ID_CHAR; }
%%
bool count(int tok)
{
switch (tok) {
case ID_WORD: ++w; c += yyleng; break;
case ID_EOL: ++l; ++c; break;
case ID_CHAR: ++c; break;
default:
return false;
}
return true;
}
void main()
{
int tok = EOF;
do {
tok = yylex();
if (!count(tok))
break;
} while (EOF != tok);
printf("%d %d %d\n", l, w, c);
}
//]
*/
// Its purpose is to do the word count function of the wc command in UNIX. It
// prints the number of lines, words and characters in a file.
//
// This examples shows how to use the tokenize() function together with a
// simple functor, which gets executed whenever a token got matched in the
// input sequence.
// #define BOOST_SPIRIT_LEXERTL_DEBUG
//[wcf_includes
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/bind/bind.hpp>
#include <boost/ref.hpp>
//]
#include <iostream>
#include <string>
#include "example.hpp"
//[wcf_namespaces
namespace lex = boost::spirit::lex;
//]
///////////////////////////////////////////////////////////////////////////////
// Token id definitions
///////////////////////////////////////////////////////////////////////////////
//[wcf_token_ids
enum token_ids
{
ID_WORD = 1000,
ID_EOL,
ID_CHAR
};
//]
//[wcf_token_definition
/*` The template `word_count_tokens` defines three different tokens:
`ID_WORD`, `ID_EOL`, and `ID_CHAR`, representing a word (anything except
a whitespace or a newline), a newline character, and any other character
(`ID_WORD`, `ID_EOL`, and `ID_CHAR` are enum values representing the token
ids, but could be anything else convertible to an integer as well).
The direct base class of any token definition class needs to be the
template `lex::lexer<>`, where the corresponding template parameter (here:
`lex::lexertl::lexer<BaseIterator>`) defines which underlying lexer engine has
to be used to provide the required state machine functionality. In this
example we use the Lexertl based lexer engine as the underlying lexer type.
*/
template <typename Lexer>
struct word_count_tokens : lex::lexer<Lexer>
{
word_count_tokens()
{
// define tokens (the regular expression to match and the corresponding
// token id) and add them to the lexer
this->self.add
("[^ \t\n]+", ID_WORD) // words (anything except ' ', '\t' or '\n')
("\n", ID_EOL) // newline characters
(".", ID_CHAR) // anything else is a plain character
;
}
};
//]
//[wcf_functor
/*` In this example the struct 'counter' is used as a functor counting the
characters, words and lines in the analyzed input sequence by identifying
the matched tokens as passed from the /Spirit.Lex/ library.
*/
struct counter
{
//<- this is an implementation detail specific to boost::bind and doesn't show
// up in the documentation
typedef bool result_type;
//->
// the function operator gets called for each of the matched tokens
// c, l, w are references to the counters used to keep track of the numbers
template <typename Token>
bool operator()(Token const& t, std::size_t& c, std::size_t& w, std::size_t& l) const
{
switch (t.id()) {
case ID_WORD: // matched a word
// since we're using a default token type in this example, every
// token instance contains a `iterator_range<BaseIterator>` as its token
// attribute pointing to the matched character sequence in the input
++w; c += t.value().size();
break;
case ID_EOL: // matched a newline character
++l; ++c;
break;
case ID_CHAR: // matched something else
++c;
break;
}
return true; // always continue to tokenize
}
};
//]
///////////////////////////////////////////////////////////////////////////////
//[wcf_main
/*` The main function simply loads the given file into memory (as a
`std::string`), instantiates an instance of the token definition template
using the correct iterator type (`word_count_tokens<char const*>`),
and finally calls `lex::tokenize`, passing an instance of the counter function
object. The return value of `lex::tokenize()` will be `true` if the
whole input sequence has been successfully tokenized, and `false` otherwise.
*/
int main(int argc, char* argv[])
{
// these variables are used to count characters, words and lines
std::size_t c = 0, w = 0, l = 0;
// read input from the given file
std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1]));
// create the token definition instance needed to invoke the lexical analyzer
word_count_tokens<lex::lexertl::lexer<> > word_count_functor;
// tokenize the given string, the bound functor gets invoked for each of
// the matched tokens
using boost::placeholders::_1;
char const* first = str.c_str();
char const* last = &first[str.size()];
bool r = lex::tokenize(first, last, word_count_functor,
boost::bind(counter(), _1, boost::ref(c), boost::ref(w), boost::ref(l)));
// print results
if (r) {
std::cout << "lines: " << l << ", words: " << w
<< ", characters: " << c << "\n";
}
else {
std::string rest(first, last);
std::cout << "Lexical analysis failed\n" << "stopped at: \""
<< rest << "\"\n";
}
return 0;
}
//]

View File

@@ -0,0 +1,59 @@
%{
// Copyright (c) 2001-2009 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)
#include <boost/timer.hpp>
#if defined(_WIN32)
#include <io.h>
#endif
#define ID_WORD 1000
#define ID_EOL 1001
#define ID_CHAR 1002
%}
%%
[^ \t\n]+ { return ID_WORD; }
\n { return ID_EOL; }
. { return ID_CHAR; }
%%
bool count(int tok, int* c, int* w, int* l)
{
switch (tok) {
case ID_WORD: ++*w; *c += yyleng; break;
case ID_EOL: ++*l; ++*c; break;
case ID_CHAR: ++*c; break;
default:
return false;
}
return true;
}
int main(int argc, char* argv[])
{
int tok = EOF;
int c = 0, w = 0, l = 0;
yyin = fopen(1 == argc ? "word_count.input" : argv[1], "r");
if (NULL == yyin) {
fprintf(stderr, "Couldn't open input file!\n");
exit(-1);
}
boost::timer tim;
do {
tok = yylex();
if (!count(tok, &c, &w, &l))
break;
} while (EOF != tok);
printf("lines: %d, words: %d, characters: %d\n", l, w, c);
fclose(yyin);
return 0;
}
extern "C" int yywrap()
{
return 1;
}

View File

@@ -0,0 +1,151 @@
// Copyright (c) 2001-2010 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)
// This example is the equivalent to the following lex program:
/*
//[wcl_flex_version
%{
int c = 0, w = 0, l = 0;
%}
%%
[^ \t\n]+ { ++w; c += yyleng; }
\n { ++c; ++l; }
. { ++c; }
%%
main()
{
yylex();
printf("%d %d %d\n", l, w, c);
}
//]
*/
// Its purpose is to do the word count function of the wc command in UNIX. It
// prints the number of lines, words and characters in a file.
//
// This examples shows how to use semantic actions associated with token
// definitions to directly attach actions to tokens. These get executed
// whenever the corresponding token got matched in the input sequence. Note,
// how this example implements all functionality directly in the lexer
// definition without any need for a parser.
// #define BOOST_SPIRIT_LEXERTL_DEBUG
//[wcl_includes
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/statement.hpp>
#include <boost/phoenix/stl/algorithm.hpp>
#include <boost/phoenix/core.hpp>
//]
#include <iostream>
#include <string>
#include "example.hpp"
//[wcl_namespaces
namespace lex = boost::spirit::lex;
//]
///////////////////////////////////////////////////////////////////////////////
// Token definition: We use the lexertl based lexer engine as the underlying
// lexer type.
//
// Note, the token definition type is derived from the 'lexertl_actor_lexer'
// template, which is a necessary to being able to use lexer semantic actions.
///////////////////////////////////////////////////////////////////////////////
struct distance_func
{
template <typename Iterator1, typename Iterator2>
struct result : boost::iterator_difference<Iterator1> {};
template <typename Iterator1, typename Iterator2>
typename result<Iterator1, Iterator2>::type
operator()(Iterator1 const& begin, Iterator2 const& end) const
{
return std::distance(begin, end);
}
};
boost::phoenix::function<distance_func> const distance = distance_func();
//[wcl_token_definition
template <typename Lexer>
struct word_count_tokens : lex::lexer<Lexer>
{
word_count_tokens()
: c(0), w(0), l(0)
, word("[^ \t\n]+") // define tokens
, eol("\n")
, any(".")
{
using boost::spirit::lex::_start;
using boost::spirit::lex::_end;
using boost::phoenix::ref;
// associate tokens with the lexer
this->self
= word [++ref(w), ref(c) += distance(_start, _end)]
| eol [++ref(c), ++ref(l)]
| any [++ref(c)]
;
}
std::size_t c, w, l;
lex::token_def<> word, eol, any;
};
//]
///////////////////////////////////////////////////////////////////////////////
//[wcl_main
int main(int argc, char* argv[])
{
/*< Specifying `omit` as the token attribute type generates a token class
not holding any token attribute at all (not even the iterator range of the
matched input sequence), therefore optimizing the token, the lexer, and
possibly the parser implementation as much as possible. Specifying
`mpl::false_` as the 3rd template parameter generates a token
type and an iterator, both holding no lexer state, allowing for even more
aggressive optimizations. As a result the token instances contain the token
ids as the only data member.
>*/ typedef
lex::lexertl::token<char const*, lex::omit, boost::mpl::false_>
token_type;
/*< This defines the lexer type to use
>*/ typedef lex::lexertl::actor_lexer<token_type> lexer_type;
/*< Create the lexer object instance needed to invoke the lexical analysis
>*/ word_count_tokens<lexer_type> word_count_lexer;
/*< Read input from the given file, tokenize all the input, while discarding
all generated tokens
>*/ std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1]));
char const* first = str.c_str();
char const* last = &first[str.size()];
/*< Create a pair of iterators returning the sequence of generated tokens
>*/ lexer_type::iterator_type iter = word_count_lexer.begin(first, last);
lexer_type::iterator_type end = word_count_lexer.end();
/*< Here we simply iterate over all tokens, making sure to break the loop
if an invalid token gets returned from the lexer
>*/ while (iter != end && token_is_valid(*iter))
++iter;
if (iter == end) {
std::cout << "lines: " << word_count_lexer.l
<< ", words: " << word_count_lexer.w
<< ", characters: " << word_count_lexer.c
<< "\n";
}
else {
std::string rest(first, last);
std::cout << "Lexical analysis failed\n" << "stopped at: \""
<< rest << "\"\n";
}
return 0;
}
//]

View File

@@ -0,0 +1,48 @@
#==============================================================================
# Copyright (c) 2001-2007 Joel de Guzman
#
# 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)
#==============================================================================
project spirit-qi-example
: requirements
<c++-template-depth>300
:
:
;
exe actions_ : actions.cpp ;
exe sum : sum.cpp ;
exe complex_number : complex_number.cpp ;
exe employee : employee.cpp ;
exe roman : roman.cpp ;
exe reference : reference.cpp ;
exe mini_xml1 : mini_xml1.cpp ;
exe mini_xml2 : mini_xml2.cpp ;
exe mini_xml3 : mini_xml3.cpp ;
exe num_list1 : num_list1.cpp ;
exe num_list2 : num_list2.cpp ;
exe num_list3 : num_list3.cpp ;
exe num_list4 : num_list4.cpp ;
exe reorder_struct : reorder_struct.cpp ;
exe parse_date : parse_date.cpp ;
exe expect : expect.cpp ;
exe key_value_sequence : key_value_sequence.cpp ;
exe key_value_sequence_ordered : key_value_sequence_ordered.cpp ;
exe key_value_sequence_empty_value : key_value_sequence_empty_value.cpp ;
exe iter_pos_parser : iter_pos_parser.cpp ;
exe boost_array : boost_array.cpp ;
exe display_attribute_type : display_attribute_type.cpp ;
exe adapt_template_struct : adapt_template_struct.cpp ;
exe unescaped_string : unescaped_string.cpp ;
exe calc_utree_naive : calc_utree_naive.cpp ;
exe calc_utree_ast : calc_utree_ast.cpp ;
exe calc_utree : calc_utree.cpp ;
exe nabialek : nabialek.cpp ;
exe typeof : typeof.cpp ;

View File

@@ -0,0 +1,109 @@
/*=============================================================================
Copyright (c) 2001-2010 Joel de Guzman
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)
=============================================================================*/
#include <boost/spirit/include/qi.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/bind/bind.hpp>
#include <iostream>
// Presented are various ways to attach semantic actions
// * Using plain function pointer
// * Using simple function object
// * Using boost.bind with a plain function
// * Using boost.bind with a member function
// * Using boost.lambda
//[tutorial_semantic_action_functions
namespace client
{
namespace qi = boost::spirit::qi;
// A plain function
void print(int const& i)
{
std::cout << i << std::endl;
}
// A member function
struct writer
{
void print(int const& i) const
{
std::cout << i << std::endl;
}
};
// A function object
struct print_action
{
void operator()(int const& i, qi::unused_type, qi::unused_type) const
{
std::cout << i << std::endl;
}
};
}
//]
int main()
{
using boost::spirit::qi::int_;
using boost::spirit::qi::parse;
using client::print;
using client::writer;
using client::print_action;
{ // example using plain function
char const *first = "{42}", *last = first + std::strlen(first);
//[tutorial_attach_actions1
parse(first, last, '{' >> int_[&print] >> '}');
//]
}
{ // example using simple function object
char const *first = "{43}", *last = first + std::strlen(first);
//[tutorial_attach_actions2
parse(first, last, '{' >> int_[print_action()] >> '}');
//]
}
{ // example using boost.bind with a plain function
char const *first = "{44}", *last = first + std::strlen(first);
using boost::placeholders::_1;
//[tutorial_attach_actions3
parse(first, last, '{' >> int_[boost::bind(&print, _1)] >> '}');
//]
}
{ // example using boost.bind with a member function
char const *first = "{44}", *last = first + std::strlen(first);
using boost::placeholders::_1;
//[tutorial_attach_actions4
writer w;
parse(first, last, '{' >> int_[boost::bind(&writer::print, &w, _1)] >> '}');
//]
}
{ // example using boost.lambda
namespace lambda = boost::lambda;
char const *first = "{45}", *last = first + std::strlen(first);
using lambda::_1;
//[tutorial_attach_actions5
parse(first, last, '{' >> int_[std::cout << _1 << '\n'] >> '}');
//]
}
return 0;
}

View File

@@ -0,0 +1,92 @@
// Copyright (c) 2001-2010 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)
// This example demonstrates a trick allowing to adapt a template data
// structure as a Fusion sequence in order to use is for direct attribute
// propagation. For more information see
// http://boost-spirit.com/home/2010/02/08/how-to-adapt-templates-as-a-fusion-sequence
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace fusion = boost::fusion;
namespace client
{
template <typename A, typename B>
struct data
{
A a;
B b;
};
template <typename Iterator, typename A, typename B>
struct data_grammar : qi::grammar<Iterator, data<A, B>()>
{
data_grammar() : data_grammar::base_type(start)
{
start = real_start;
real_start = qi::auto_ >> ',' >> qi::auto_;
}
qi::rule<Iterator, data<A, B>()> start;
qi::rule<Iterator, fusion::vector<A&, B&>()> real_start;
};
}
namespace boost { namespace spirit { namespace traits
{
template <typename A, typename B>
struct transform_attribute<client::data<A, B>, fusion::vector<A&, B&>, qi::domain>
{
typedef fusion::vector<A&, B&> type;
static type pre(client::data<A, B>& val) { return type(val.a, val.b); }
static void post(client::data<A, B>&, fusion::vector<A&, B&> const&) {}
static void fail(client::data<A, B>&) {}
};
}}}
///////////////////////////////////////////////////////////////////////////////
int main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "\t\tA parser for Spirit utilizing an adapted template ...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Give me two comma separated integers:\n";
std::cout << "Type [q or Q] to quit\n\n";
std::string str;
client::data_grammar<std::string::const_iterator, long, int> g; // Our grammar
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
client::data<long, int> d;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
bool r = phrase_parse(iter, end, g, qi::space, d);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "got: " << d.a << "," << d.b << std::endl;
std::cout << "\n-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
return 0;
}

View File

@@ -0,0 +1,119 @@
// Copyright (c) 2009 Erik Bryan
// Copyright (c) 2007-2010 Hartmut Kaiser
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file licence_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <string>
#include <vector>
#include <boost/array.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
// create a wrapper holding the boost::array and a current insertion point
namespace client
{
namespace detail
{
template <typename T>
struct adapt_array;
template <typename T, std::size_t N>
struct adapt_array<boost::array<T, N> >
{
typedef boost::array<T, N> array_type;
adapt_array(array_type& arr)
: arr_(arr), current_(0) {}
// expose a push_back function compatible with std containers
bool push_back(typename array_type::value_type const& val)
{
// if the array is full, we need to bail out
// returning false will fail the parse
if (current_ >= N)
return false;
arr_[current_++] = val;
return true;
}
array_type& arr_;
std::size_t current_;
};
}
namespace result_of
{
template <typename T>
struct adapt_array;
template <typename T, std::size_t N>
struct adapt_array<boost::array<T, N> >
{
typedef detail::adapt_array<boost::array<T, N> > type;
};
}
template <typename T, std::size_t N>
inline detail::adapt_array<boost::array<T, N> >
adapt_array(boost::array<T, N>& arr)
{
return detail::adapt_array<boost::array<T, N> >(arr);
}
}
///////////////////////////////////////////////////////////////////////////////
// specialize Spirit's container specific customization points for our adaptor
namespace boost { namespace spirit { namespace traits
{
template <typename T, std::size_t N>
struct is_container<client::detail::adapt_array<boost::array<T, N> > >
: boost::mpl::true_
{};
template <typename T, std::size_t N>
struct container_value<client::detail::adapt_array<boost::array<T, N> > >
{
typedef T type; // value type of container
};
template <typename T, std::size_t N>
struct push_back_container<
client::detail::adapt_array<boost::array<T, N> >, T>
{
static bool call(client::detail::adapt_array<boost::array<T, N> >& c
, T const& val)
{
return c.push_back(val);
}
};
}}}
int main()
{
typedef std::string::const_iterator iterator_type;
typedef boost::array<int, 2> array_type;
typedef client::result_of::adapt_array<array_type>::type adapted_type;
array_type arr;
std::string str = "1 2";
iterator_type iter = str.begin();
iterator_type end = str.end();
qi::rule<iterator_type, adapted_type(), ascii::space_type> r = *qi::int_;
adapted_type attr = client::adapt_array(arr);
bool result = qi::phrase_parse(iter, end, r, ascii::space, attr);
if (result)
std::cout << "Parsed: " << arr[0] << ", " << arr[1] << std::endl;
return 0;
}

View File

@@ -0,0 +1,165 @@
/*=============================================================================
Copyright (c) 2001-2011 Hartmut Kaiser
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// Plain calculator example demonstrating the grammar. The parser is a
// syntax checker only and does not do any semantic evaluation.
//
// [ JDG May 10, 2002 ] spirit1
// [ JDG March 4, 2007 ] spirit2
// [ HK November 30, 2010 ] spirit2/utree
//
///////////////////////////////////////////////////////////////////////////////
// #define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_utree.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/function.hpp>
#include <iostream>
#include <string>
#if BOOST_PHOENIX_VERSION == 0x2000
namespace boost { namespace phoenix
{
// There's a bug in the Phoenix V2 type deduction mechanism that prevents
// correct return type deduction for the math operations below. Newer
// versions of Phoenix will be switching to BOOST_TYPEOF. In the meantime,
// we will use the specializations helping with return type deduction
// below:
template <>
struct result_of_plus<spirit::utree&, spirit::utree&>
{
typedef spirit::utree type;
};
template <>
struct result_of_minus<spirit::utree&, spirit::utree&>
{
typedef spirit::utree type;
};
template <>
struct result_of_multiplies<spirit::utree&, spirit::utree&>
{
typedef spirit::utree type;
};
template <>
struct result_of_divides<spirit::utree&, spirit::utree&>
{
typedef spirit::utree type;
};
template <>
struct result_of_negate<spirit::utree&>
{
typedef spirit::utree type;
};
}}
#endif
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace spirit = boost::spirit;
///////////////////////////////////////////////////////////////////////////////
// Our calculator grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct calculator : qi::grammar<Iterator, ascii::space_type, spirit::utree()>
{
calculator() : calculator::base_type(expression)
{
using qi::uint_;
using qi::_val;
using qi::_1;
expression =
term [_val = _1]
>> *( ('+' >> term [_val = _val + _1])
| ('-' >> term [_val = _val - _1])
)
;
term =
factor [_val = _1]
>> *( ('*' >> factor [_val = _val * _1])
| ('/' >> factor [_val = _val / _1])
)
;
factor =
uint_ [_val = _1]
| '(' >> expression [_val = _1] >> ')'
| ('-' >> factor [_val = -_1])
| ('+' >> factor [_val = _1])
;
BOOST_SPIRIT_DEBUG_NODE(expression);
BOOST_SPIRIT_DEBUG_NODE(term);
BOOST_SPIRIT_DEBUG_NODE(factor);
}
qi::rule<Iterator, ascii::space_type, spirit::utree()> expression, term, factor;
};
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Expression parser...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
using boost::spirit::ascii::space;
using boost::spirit::utree;
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
calculator calc; // Our grammar
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
utree ut;
bool r = phrase_parse(iter, end, calc, space, ut);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded: " << ut << "\n";
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \": " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,163 @@
/*=============================================================================
Copyright (c) 2001-2011 Hartmut Kaiser
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// Plain calculator example demonstrating the grammar. The parser is a
// syntax checker only and does not do any semantic evaluation.
//
// [ JDG May 10, 2002 ] spirit1
// [ JDG March 4, 2007 ] spirit2
// [ HK November 30, 2010 ] spirit2/utree
//
///////////////////////////////////////////////////////////////////////////////
// #define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/support_utree.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/function.hpp>
#include <iostream>
#include <string>
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace spirit = boost::spirit;
struct expr
{
template <typename T1, typename T2 = void>
struct result { typedef void type; };
expr(char op) : op(op) {}
void operator()(spirit::utree& expr, spirit::utree const& rhs) const
{
spirit::utree lhs;
lhs.swap(expr);
expr.push_back(spirit::utf8_symbol_range_type(&op, &op+1));
expr.push_back(lhs);
expr.push_back(rhs);
}
char const op;
};
boost::phoenix::function<expr> const plus = expr('+');
boost::phoenix::function<expr> const minus = expr('-');
boost::phoenix::function<expr> const times = expr('*');
boost::phoenix::function<expr> const divide = expr('/');
struct negate_expr
{
template <typename T1, typename T2 = void>
struct result { typedef void type; };
void operator()(spirit::utree& expr, spirit::utree const& rhs) const
{
char const op = '-';
expr.clear();
expr.push_back(spirit::utf8_symbol_range_type(&op, &op+1));
expr.push_back(rhs);
}
};
boost::phoenix::function<negate_expr> neg;
///////////////////////////////////////////////////////////////////////////////
// Our calculator grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct calculator : qi::grammar<Iterator, ascii::space_type, spirit::utree()>
{
calculator() : calculator::base_type(expression)
{
using qi::uint_;
using qi::_val;
using qi::_1;
expression =
term [_val = _1]
>> *( ('+' >> term [plus(_val, _1)])
| ('-' >> term [minus(_val, _1)])
)
;
term =
factor [_val = _1]
>> *( ('*' >> factor [times(_val, _1)])
| ('/' >> factor [divide(_val, _1)])
)
;
factor =
uint_ [_val = _1]
| '(' >> expression [_val = _1] >> ')'
| ('-' >> factor [neg(_val, _1)])
| ('+' >> factor [_val = _1])
;
BOOST_SPIRIT_DEBUG_NODE(expression);
BOOST_SPIRIT_DEBUG_NODE(term);
BOOST_SPIRIT_DEBUG_NODE(factor);
}
qi::rule<Iterator, ascii::space_type, spirit::utree()> expression, term, factor;
};
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Expression parser...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
using boost::spirit::ascii::space;
using boost::spirit::utree;
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
calculator calc; // Our grammar
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
utree ut;
bool r = phrase_parse(iter, end, calc, space, ut);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded: " << ut << "\n";
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \": " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,133 @@
/*=============================================================================
Copyright (c) 2001-2011 Hartmut Kaiser
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// Plain calculator example demonstrating the grammar. The parser is a
// syntax checker only and does not do any semantic evaluation.
//
// [ JDG May 10, 2002 ] spirit1
// [ JDG March 4, 2007 ] spirit2
// [ HK November 30, 2010 ] spirit2/utree
//
///////////////////////////////////////////////////////////////////////////////
// This rather naive example demonstrates that you can pass an instance of a
// utree as the attribute for almost any grammar. As the result the utree will
// be filled with the parse tree as generated during the parsing. This is most
// of the time not what's desired, but is usually a good first step in order to
// prepare your grammar to generate a customized AST. See the calc_utree_ast
// example for a modified version of this grammar filling the attribute with a
// AST (abstract syntax tree) representing the math expression as matched from
// the input.
// #define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_utree.hpp>
#include <iostream>
#include <string>
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace spirit = boost::spirit;
///////////////////////////////////////////////////////////////////////////////
// Our calculator grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct calculator : qi::grammar<Iterator, ascii::space_type, spirit::utree()>
{
calculator() : calculator::base_type(expression)
{
using qi::uint_;
using qi::char_;
expression =
term
>> *( (char_('+') >> term)
| (char_('-') >> term)
)
;
term =
factor
>> *( (char_('*') >> factor)
| (char_('/') >> factor)
)
;
factor =
uint_
| '(' >> expression >> ')'
| (char_('-') >> factor)
| (char_('+') >> factor)
;
BOOST_SPIRIT_DEBUG_NODE(expression);
BOOST_SPIRIT_DEBUG_NODE(term);
BOOST_SPIRIT_DEBUG_NODE(factor);
}
qi::rule<Iterator, ascii::space_type, spirit::utree()> expression;
qi::rule<Iterator, ascii::space_type, spirit::utree::list_type()> term;
qi::rule<Iterator, ascii::space_type, spirit::utree::list_type()> factor;
};
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Expression parser...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
using boost::spirit::ascii::space;
using boost::spirit::utree;
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
calculator calc; // Our grammar
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
utree ut;
bool r = phrase_parse(iter, end, calc, space, ut);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded: " << ut << "\n";
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \": " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,132 @@
#==============================================================================
# Copyright (c) 2001-2011 Joel de Guzman
#
# 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)
#==============================================================================
project spirit-qi-compiler_tutorial
: requirements
<c++-template-depth>300
:
;
import modules ;
exe calc1 : calc1.cpp ;
exe calc2 : calc2.cpp ;
exe calc3 : calc3.cpp ;
exe calc4 : calc4.cpp ;
exe calc5 : calc5.cpp ;
exe calc6 : calc6.cpp ;
exe calc7 :
calc7/vm.cpp
calc7/compiler.cpp
calc7/expression.cpp
calc7/statement.cpp
calc7/main.cpp
;
exe calc8 :
calc8/vm.cpp
calc8/compiler.cpp
calc8/expression.cpp
calc8/statement.cpp
calc8/main.cpp
;
exe mini_c :
mini_c/vm.cpp
mini_c/compiler.cpp
mini_c/expression.cpp
mini_c/statement.cpp
mini_c/function.cpp
mini_c/main.cpp
;
exe conjure1 :
conjure1/vm.cpp
conjure1/compiler.cpp
conjure1/expression.cpp
conjure1/statement.cpp
conjure1/function.cpp
conjure1/main.cpp
;
exe conjure2 :
conjure2/compiler.cpp
conjure2/expression.cpp
conjure2/function.cpp
conjure2/lexer.cpp
conjure2/main.cpp
conjure2/statement.cpp
conjure2/vm.cpp
;
#==============================================================================
# conjure3 and above require LLVM. Make sure you provide the
# LLVM_PATH in your bjam invocation. E.g.:
#
# bjam -sLLVM_PATH=C:/dev/llvm conjure3
#
#==============================================================================
if [ modules.peek : LLVM_PATH ]
{
LLVM_PATH = [ modules.peek : LLVM_PATH ] ;
}
if $(LLVM_PATH)
{
path-constant LLVM_LIB_DEBUG_PATH : $(LLVM_PATH)/lib/Debug ;
path-constant LLVM_LIB_RELEASE_PATH : $(LLVM_PATH)/lib/Release ;
llvm_linker_flags =
"advapi32.lib"
"shell32.lib"
;
llvm_debug_libs = [ glob $(LLVM_LIB_DEBUG_PATH)/LLVM*.lib ] ;
llvm_release_libs = [ glob $(LLVM_LIB_RELEASE_PATH)/LLVM*.lib ] ;
rule build_exe_1 ( target-name : sources + : requirements * )
{
local llvm_lib ;
if <variant>debug in $(requirements)
{
llvm_lib = $(llvm_debug_libs) ;
}
else
{
llvm_lib = $(llvm_release_libs) ;
}
exe $(target-name)
: $(sources)
$(llvm_lib)
: $(requirements)
<toolset>msvc
<include>$(LLVM_PATH)/include
<linkflags>$(llvm_linker_flags)
;
}
rule build_exe ( target-name : sources + )
{
build_exe_1 $(target-name) : $(sources) : <variant>debug ;
build_exe_1 $(target-name) : $(sources) : <variant>release ;
}
build_exe conjure3 :
conjure3/compiler.cpp
conjure3/expression.cpp
conjure3/function.cpp
conjure3/lexer.cpp
conjure3/main.cpp
conjure3/statement.cpp
conjure3/vm.cpp
;
}

View File

@@ -0,0 +1,117 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// Plain calculator example demonstrating the grammar. The parser is a
// syntax checker only and does not do any semantic evaluation.
//
// [ JDG May 10, 2002 ] spirit1
// [ JDG March 4, 2007 ] spirit2
// [ JDG February 21, 2011 ] spirit2.5
//
///////////////////////////////////////////////////////////////////////////////
// Spirit v2.5 allows you to suppress automatic generation
// of predefined terminals to speed up complation. With
// BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are
// responsible in creating instances of the terminals that
// you need (e.g. see qi::uint_type uint_ below).
#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
// Our calculator grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct calculator : qi::grammar<Iterator, ascii::space_type>
{
calculator() : calculator::base_type(expression)
{
qi::uint_type uint_;
expression =
term
>> *( ('+' >> term)
| ('-' >> term)
)
;
term =
factor
>> *( ('*' >> factor)
| ('/' >> factor)
)
;
factor =
uint_
| '(' >> expression >> ')'
| ('-' >> factor)
| ('+' >> factor)
;
}
qi::rule<Iterator, ascii::space_type> expression, term, factor;
};
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Expression parser...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
boost::spirit::ascii::space_type space; // Our skipper
calculator calc; // Our grammar
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
bool r = phrase_parse(iter, end, calc, space);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \" " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,130 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// A Calculator example demonstrating the grammar and semantic actions
// using plain functions. The parser prints code suitable for a stack
// based virtual machine.
//
// [ JDG May 10, 2002 ] spirit1
// [ JDG March 4, 2007 ] spirit2
// [ JDG February 21, 2011 ] spirit2.5
//
///////////////////////////////////////////////////////////////////////////////
// Spirit v2.5 allows you to suppress automatic generation
// of predefined terminals to speed up complation. With
// BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are
// responsible in creating instances of the terminals that
// you need (e.g. see qi::uint_type uint_ below).
#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
// Semantic actions
////////////////////////////////////////////////////////1///////////////////////
namespace
{
void do_int(int n) { std::cout << "push " << n << std::endl; }
void do_add() { std::cout << "add\n"; }
void do_subt() { std::cout << "subtract\n"; }
void do_mult() { std::cout << "mult\n"; }
void do_div() { std::cout << "divide\n"; }
void do_neg() { std::cout << "negate\n"; }
}
///////////////////////////////////////////////////////////////////////////////
// Our calculator grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct calculator : qi::grammar<Iterator, ascii::space_type>
{
calculator() : calculator::base_type(expression)
{
qi::uint_type uint_;
expression =
term
>> *( ('+' >> term [&do_add])
| ('-' >> term [&do_subt])
)
;
term =
factor
>> *( ('*' >> factor [&do_mult])
| ('/' >> factor [&do_div])
)
;
factor =
uint_ [&do_int]
| '(' >> expression >> ')'
| ('-' >> factor [&do_neg])
| ('+' >> factor)
;
}
qi::rule<Iterator, ascii::space_type> expression, term, factor;
};
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Expression parser...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
boost::spirit::ascii::space_type space; // Our skipper
calculator calc; // Our grammar
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
bool r = phrase_parse(iter, end, calc, space);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \" " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,123 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// A calculator example demonstrating the grammar and semantic actions
// using phoenix to do the actual expression evaluation. The parser is
// essentially an "interpreter" that evaluates expressions on the fly.
//
// [ JDG June 29, 2002 ] spirit1
// [ JDG March 5, 2007 ] spirit2
//
///////////////////////////////////////////////////////////////////////////////
// Spirit v2.5 allows you to suppress automatic generation
// of predefined terminals to speed up complation. With
// BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are
// responsible in creating instances of the terminals that
// you need (e.g. see qi::uint_type uint_ below).
#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
#include <boost/spirit/include/qi.hpp>
#include <boost/phoenix/operator.hpp>
#include <iostream>
#include <string>
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////
// Our calculator grammar
///////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct calculator : qi::grammar<Iterator, int(), ascii::space_type>
{
calculator() : calculator::base_type(expression)
{
qi::_val_type _val;
qi::_1_type _1;
qi::uint_type uint_;
expression =
term [_val = _1]
>> *( ('+' >> term [_val += _1])
| ('-' >> term [_val -= _1])
)
;
term =
factor [_val = _1]
>> *( ('*' >> factor [_val *= _1])
| ('/' >> factor [_val /= _1])
)
;
factor =
uint_ [_val = _1]
| '(' >> expression [_val = _1] >> ')'
| ('-' >> factor [_val = -_1])
| ('+' >> factor [_val = _1])
;
}
qi::rule<Iterator, int(), ascii::space_type> expression, term, factor;
};
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Expression parser...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
boost::spirit::ascii::space_type space; // Our skipper
calculator calc; // Our grammar
std::string str;
int result;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
bool r = phrase_parse(iter, end, calc, space, result);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "result = " << result << std::endl;
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \" " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,284 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// A Calculator example demonstrating generation of AST. The AST,
// once created, is traversed, 1) To print its contents and
// 2) To evaluate the result.
//
// [ JDG April 28, 2008 ] For BoostCon 2008
// [ JDG February 18, 2011 ] Pure attributes. No semantic actions.
//
///////////////////////////////////////////////////////////////////////////////
// Spirit v2.5 allows you to suppress automatic generation
// of predefined terminals to speed up complation. With
// BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are
// responsible in creating instances of the terminals that
// you need (e.g. see qi::uint_type uint_ below).
#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
#if defined(_MSC_VER)
# pragma warning(disable: 4345)
#endif
#include <boost/spirit/include/qi.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <string>
namespace client { namespace ast
{
///////////////////////////////////////////////////////////////////////////
// The AST
///////////////////////////////////////////////////////////////////////////
struct nil {};
struct signed_;
struct program;
typedef boost::variant<
nil
, unsigned int
, boost::recursive_wrapper<signed_>
, boost::recursive_wrapper<program>
>
operand;
struct signed_
{
char sign;
operand operand_;
};
struct operation
{
char operator_;
operand operand_;
};
struct program
{
operand first;
std::list<operation> rest;
};
}}
BOOST_FUSION_ADAPT_STRUCT(
client::ast::signed_,
(char, sign)
(client::ast::operand, operand_)
)
BOOST_FUSION_ADAPT_STRUCT(
client::ast::operation,
(char, operator_)
(client::ast::operand, operand_)
)
BOOST_FUSION_ADAPT_STRUCT(
client::ast::program,
(client::ast::operand, first)
(std::list<client::ast::operation>, rest)
)
namespace client { namespace ast
{
///////////////////////////////////////////////////////////////////////////
// The AST Printer
///////////////////////////////////////////////////////////////////////////
struct printer
{
typedef void result_type;
void operator()(nil) const {}
void operator()(unsigned int n) const { std::cout << n; }
void operator()(operation const& x) const
{
boost::apply_visitor(*this, x.operand_);
switch (x.operator_)
{
case '+': std::cout << " add"; break;
case '-': std::cout << " subt"; break;
case '*': std::cout << " mult"; break;
case '/': std::cout << " div"; break;
}
}
void operator()(signed_ const& x) const
{
boost::apply_visitor(*this, x.operand_);
switch (x.sign)
{
case '-': std::cout << " neg"; break;
case '+': std::cout << " pos"; break;
}
}
void operator()(program const& x) const
{
boost::apply_visitor(*this, x.first);
BOOST_FOREACH(operation const& oper, x.rest)
{
std::cout << ' ';
(*this)(oper);
}
}
};
///////////////////////////////////////////////////////////////////////////
// The AST evaluator
///////////////////////////////////////////////////////////////////////////
struct eval
{
typedef int result_type;
int operator()(nil) const { BOOST_ASSERT(0); return 0; }
int operator()(unsigned int n) const { return n; }
int operator()(operation const& x, int lhs) const
{
int rhs = boost::apply_visitor(*this, x.operand_);
switch (x.operator_)
{
case '+': return lhs + rhs;
case '-': return lhs - rhs;
case '*': return lhs * rhs;
case '/': return lhs / rhs;
}
BOOST_ASSERT(0);
return 0;
}
int operator()(signed_ const& x) const
{
int rhs = boost::apply_visitor(*this, x.operand_);
switch (x.sign)
{
case '-': return -rhs;
case '+': return +rhs;
}
BOOST_ASSERT(0);
return 0;
}
int operator()(program const& x) const
{
int state = boost::apply_visitor(*this, x.first);
BOOST_FOREACH(operation const& oper, x.rest)
{
state = (*this)(oper, state);
}
return state;
}
};
}}
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
// The calculator grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct calculator : qi::grammar<Iterator, ast::program(), ascii::space_type>
{
calculator() : calculator::base_type(expression)
{
qi::uint_type uint_;
qi::char_type char_;
expression =
term
>> *( (char_('+') >> term)
| (char_('-') >> term)
)
;
term =
factor
>> *( (char_('*') >> factor)
| (char_('/') >> factor)
)
;
factor =
uint_
| '(' >> expression >> ')'
| (char_('-') >> factor)
| (char_('+') >> factor)
;
}
qi::rule<Iterator, ast::program(), ascii::space_type> expression;
qi::rule<Iterator, ast::program(), ascii::space_type> term;
qi::rule<Iterator, ast::operand(), ascii::space_type> factor;
};
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Expression parser...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
typedef client::ast::program ast_program;
typedef client::ast::printer ast_print;
typedef client::ast::eval ast_eval;
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
calculator calc; // Our grammar
ast_program program; // Our program (AST)
ast_print print; // Prints the program
ast_eval eval; // Evaluates the program
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
boost::spirit::ascii::space_type space;
bool r = phrase_parse(iter, end, calc, space, program);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
print(program);
std::cout << "\nResult: " << eval(program) << std::endl;
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \" " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,338 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// Same as Calc4, this time, we'll incorporate debugging support,
// plus error handling and reporting.
//
// [ JDG April 28, 2008 ] For BoostCon 2008
// [ JDG February 18, 2011 ] Pure attributes. No semantic actions.
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Spirit v2.5 allows you to suppress automatic generation
// of predefined terminals to speed up complation. With
// BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are
// responsible in creating instances of the terminals that
// you need (e.g. see qi::uint_type uint_ below).
#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Define this to enable debugging
#define BOOST_SPIRIT_QI_DEBUG
///////////////////////////////////////////////////////////////////////////////
// Uncomment this if you want to enable debugging
//#define BOOST_SPIRIT_QI_DEBUG
///////////////////////////////////////////////////////////////////////////////
#if defined(_MSC_VER)
# pragma warning(disable: 4345)
#endif
#include <boost/spirit/include/qi.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/phoenix/function.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <string>
namespace client { namespace ast
{
///////////////////////////////////////////////////////////////////////////
// The AST
///////////////////////////////////////////////////////////////////////////
struct nil {};
struct signed_;
struct program;
typedef boost::variant<
nil
, unsigned int
, boost::recursive_wrapper<signed_>
, boost::recursive_wrapper<program>
>
operand;
struct signed_
{
char sign;
operand operand_;
};
struct operation
{
char operator_;
operand operand_;
};
struct program
{
operand first;
std::list<operation> rest;
};
// print function for debugging
inline std::ostream& operator<<(std::ostream& out, nil) { out << "nil"; return out; }
}}
BOOST_FUSION_ADAPT_STRUCT(
client::ast::signed_,
(char, sign)
(client::ast::operand, operand_)
)
BOOST_FUSION_ADAPT_STRUCT(
client::ast::operation,
(char, operator_)
(client::ast::operand, operand_)
)
BOOST_FUSION_ADAPT_STRUCT(
client::ast::program,
(client::ast::operand, first)
(std::list<client::ast::operation>, rest)
)
namespace client { namespace ast
{
///////////////////////////////////////////////////////////////////////////
// The AST Printer
///////////////////////////////////////////////////////////////////////////
struct printer
{
typedef void result_type;
void operator()(nil) const {}
void operator()(unsigned int n) const { std::cout << n; }
void operator()(operation const& x) const
{
boost::apply_visitor(*this, x.operand_);
switch (x.operator_)
{
case '+': std::cout << " add"; break;
case '-': std::cout << " subt"; break;
case '*': std::cout << " mult"; break;
case '/': std::cout << " div"; break;
}
}
void operator()(signed_ const& x) const
{
boost::apply_visitor(*this, x.operand_);
switch (x.sign)
{
case '-': std::cout << " neg"; break;
case '+': std::cout << " pos"; break;
}
}
void operator()(program const& x) const
{
boost::apply_visitor(*this, x.first);
BOOST_FOREACH(operation const& oper, x.rest)
{
std::cout << ' ';
(*this)(oper);
}
}
};
///////////////////////////////////////////////////////////////////////////
// The AST evaluator
///////////////////////////////////////////////////////////////////////////
struct eval
{
typedef int result_type;
int operator()(nil) const { BOOST_ASSERT(0); return 0; }
int operator()(unsigned int n) const { return n; }
int operator()(operation const& x, int lhs) const
{
int rhs = boost::apply_visitor(*this, x.operand_);
switch (x.operator_)
{
case '+': return lhs + rhs;
case '-': return lhs - rhs;
case '*': return lhs * rhs;
case '/': return lhs / rhs;
}
BOOST_ASSERT(0);
return 0;
}
int operator()(signed_ const& x) const
{
int rhs = boost::apply_visitor(*this, x.operand_);
switch (x.sign)
{
case '-': return -rhs;
case '+': return +rhs;
}
BOOST_ASSERT(0);
return 0;
}
int operator()(program const& x) const
{
int state = boost::apply_visitor(*this, x.first);
BOOST_FOREACH(operation const& oper, x.rest)
{
state = (*this)(oper, state);
}
return state;
}
};
}}
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using boost::phoenix::function;
///////////////////////////////////////////////////////////////////////////////
// Our error handler
///////////////////////////////////////////////////////////////////////////////
struct error_handler_
{
template <typename, typename, typename>
struct result { typedef void type; };
template <typename Iterator>
void operator()(
qi::info const& what
, Iterator err_pos, Iterator last) const
{
std::cout
<< "Error! Expecting "
<< what // what failed?
<< " here: \""
<< std::string(err_pos, last) // iterators to error-pos, end
<< "\""
<< std::endl
;
}
};
function<error_handler_> const error_handler = error_handler_();
///////////////////////////////////////////////////////////////////////////////
// Our calculator grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct calculator : qi::grammar<Iterator, ast::program(), ascii::space_type>
{
calculator() : calculator::base_type(expression)
{
qi::char_type char_;
qi::uint_type uint_;
qi::_2_type _2;
qi::_3_type _3;
qi::_4_type _4;
using qi::on_error;
using qi::fail;
expression =
term
>> *( (char_('+') > term)
| (char_('-') > term)
)
;
term =
factor
>> *( (char_('*') > factor)
| (char_('/') > factor)
)
;
factor =
uint_
| '(' > expression > ')'
| (char_('-') > factor)
| (char_('+') > factor)
;
// Debugging and error handling and reporting support.
BOOST_SPIRIT_DEBUG_NODE(expression);
BOOST_SPIRIT_DEBUG_NODE(term);
BOOST_SPIRIT_DEBUG_NODE(factor);
// Error handling
on_error<fail>(expression, error_handler(_4, _3, _2));
}
qi::rule<Iterator, ast::program(), ascii::space_type> expression;
qi::rule<Iterator, ast::program(), ascii::space_type> term;
qi::rule<Iterator, ast::operand(), ascii::space_type> factor;
};
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Expression parser...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
typedef client::ast::program ast_program;
typedef client::ast::printer ast_print;
typedef client::ast::eval ast_eval;
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
calculator calc; // Our grammar
ast_program program; // Our program (AST)
ast_print print; // Prints the program
ast_eval eval; // Evaluates the program
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
boost::spirit::ascii::space_type space;
bool r = phrase_parse(iter, end, calc, space, program);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
print(program);
std::cout << "\nResult: " << eval(program) << std::endl;
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,372 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// Yet another calculator example! This time, we will compile to a simple
// virtual machine. This is actually one of the very first Spirit example
// circa 2000. Now, it's ported to Spirit2.
//
// [ JDG Sometime 2000 ] pre-boost
// [ JDG September 18, 2002 ] spirit1
// [ JDG April 8, 2007 ] spirit2
// [ JDG February 18, 2011 ] Pure attributes. No semantic actions.
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Spirit v2.5 allows you to suppress automatic generation
// of predefined terminals to speed up complation. With
// BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are
// responsible in creating instances of the terminals that
// you need (e.g. see qi::uint_type uint_ below).
#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Define this to enable debugging
//#define BOOST_SPIRIT_QI_DEBUG
///////////////////////////////////////////////////////////////////////////////
// Uncomment this if you want to enable debugging
//#define BOOST_SPIRIT_QI_DEBUG
///////////////////////////////////////////////////////////////////////////////
#if defined(_MSC_VER)
# pragma warning(disable: 4345)
#endif
#include <boost/spirit/include/qi.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/phoenix/function.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <string>
namespace client { namespace ast
{
///////////////////////////////////////////////////////////////////////////
// The AST
///////////////////////////////////////////////////////////////////////////
struct nil {};
struct signed_;
struct expression;
typedef boost::variant<
nil
, unsigned int
, boost::recursive_wrapper<signed_>
, boost::recursive_wrapper<expression>
>
operand;
struct signed_
{
char sign;
operand operand_;
};
struct operation
{
char operator_;
operand operand_;
};
struct expression
{
operand first;
std::list<operation> rest;
};
// print function for debugging
inline std::ostream& operator<<(std::ostream& out, nil) { out << "nil"; return out; }
}}
BOOST_FUSION_ADAPT_STRUCT(
client::ast::signed_,
(char, sign)
(client::ast::operand, operand_)
)
BOOST_FUSION_ADAPT_STRUCT(
client::ast::operation,
(char, operator_)
(client::ast::operand, operand_)
)
BOOST_FUSION_ADAPT_STRUCT(
client::ast::expression,
(client::ast::operand, first)
(std::list<client::ast::operation>, rest)
)
namespace client
{
///////////////////////////////////////////////////////////////////////////
// The Virtual Machine
///////////////////////////////////////////////////////////////////////////
enum byte_code
{
op_neg, // negate the top stack entry
op_add, // add top two stack entries
op_sub, // subtract top two stack entries
op_mul, // multiply top two stack entries
op_div, // divide top two stack entries
op_int, // push constant integer into the stack
};
class vmachine
{
public:
vmachine(unsigned stackSize = 4096)
: stack(stackSize)
, stack_ptr(stack.begin())
{
}
int top() const { return stack_ptr[-1]; };
void execute(std::vector<int> const& code);
private:
std::vector<int> stack;
std::vector<int>::iterator stack_ptr;
};
void vmachine::execute(std::vector<int> const& code)
{
std::vector<int>::const_iterator pc = code.begin();
stack_ptr = stack.begin();
while (pc != code.end())
{
switch (*pc++)
{
case op_neg:
stack_ptr[-1] = -stack_ptr[-1];
break;
case op_add:
--stack_ptr;
stack_ptr[-1] += stack_ptr[0];
break;
case op_sub:
--stack_ptr;
stack_ptr[-1] -= stack_ptr[0];
break;
case op_mul:
--stack_ptr;
stack_ptr[-1] *= stack_ptr[0];
break;
case op_div:
--stack_ptr;
stack_ptr[-1] /= stack_ptr[0];
break;
case op_int:
*stack_ptr++ = *pc++;
break;
}
}
}
///////////////////////////////////////////////////////////////////////////
// The Compiler
///////////////////////////////////////////////////////////////////////////
struct compiler
{
typedef void result_type;
std::vector<int>& code;
compiler(std::vector<int>& code)
: code(code) {}
void operator()(ast::nil) const { BOOST_ASSERT(0); }
void operator()(unsigned int n) const
{
code.push_back(op_int);
code.push_back(n);
}
void operator()(ast::operation const& x) const
{
boost::apply_visitor(*this, x.operand_);
switch (x.operator_)
{
case '+': code.push_back(op_add); break;
case '-': code.push_back(op_sub); break;
case '*': code.push_back(op_mul); break;
case '/': code.push_back(op_div); break;
default: BOOST_ASSERT(0); break;
}
}
void operator()(ast::signed_ const& x) const
{
boost::apply_visitor(*this, x.operand_);
switch (x.sign)
{
case '-': code.push_back(op_neg); break;
case '+': break;
default: BOOST_ASSERT(0); break;
}
}
void operator()(ast::expression const& x) const
{
boost::apply_visitor(*this, x.first);
BOOST_FOREACH(ast::operation const& oper, x.rest)
{
(*this)(oper);
}
}
};
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using boost::phoenix::function;
///////////////////////////////////////////////////////////////////////////////
// The error handler
///////////////////////////////////////////////////////////////////////////////
struct error_handler_
{
template <typename, typename, typename>
struct result { typedef void type; };
template <typename Iterator>
void operator()(
qi::info const& what
, Iterator err_pos, Iterator last) const
{
std::cout
<< "Error! Expecting "
<< what // what failed?
<< " here: \""
<< std::string(err_pos, last) // iterators to error-pos, end
<< "\""
<< std::endl
;
}
};
function<error_handler_> const error_handler = error_handler_();
///////////////////////////////////////////////////////////////////////////////
// The calculator grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct calculator : qi::grammar<Iterator, ast::expression(), ascii::space_type>
{
calculator() : calculator::base_type(expression)
{
qi::char_type char_;
qi::uint_type uint_;
qi::_2_type _2;
qi::_3_type _3;
qi::_4_type _4;
using qi::on_error;
using qi::fail;
expression =
term
>> *( (char_('+') > term)
| (char_('-') > term)
)
;
term =
factor
>> *( (char_('*') > factor)
| (char_('/') > factor)
)
;
factor =
uint_
| '(' > expression > ')'
| (char_('-') > factor)
| (char_('+') > factor)
;
// Debugging and error handling and reporting support.
BOOST_SPIRIT_DEBUG_NODES(
(expression)(term)(factor));
// Error handling
on_error<fail>(expression, error_handler(_4, _3, _2));
}
qi::rule<Iterator, ast::expression(), ascii::space_type> expression;
qi::rule<Iterator, ast::expression(), ascii::space_type> term;
qi::rule<Iterator, ast::operand(), ascii::space_type> factor;
};
}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Expression parser...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
typedef client::ast::expression ast_expression;
typedef client::compiler compiler;
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
client::vmachine mach; // Our virtual machine
std::vector<int> code; // Our VM code
calculator calc; // Our grammar
ast_expression expression; // Our program (AST)
compiler compile(code); // Compiles the program
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
boost::spirit::ascii::space_type space;
bool r = phrase_parse(iter, end, calc, space, expression);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
compile(expression);
mach.execute(code);
std::cout << "\nResult: " << mach.top() << std::endl;
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}

View File

@@ -0,0 +1,78 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
#if !defined(BOOST_SPIRIT_CALC7_ANNOTATION_HPP)
#define BOOST_SPIRIT_CALC7_ANNOTATION_HPP
#include <map>
#include <boost/variant/apply_visitor.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/mpl/bool.hpp>
#include "ast.hpp"
namespace client
{
///////////////////////////////////////////////////////////////////////////////
// The annotation handler links the AST to a map of iterator positions
// for the purpose of subsequent semantic error handling when the
// program is being compiled.
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct annotation
{
template <typename, typename>
struct result { typedef void type; };
std::vector<Iterator>& iters;
annotation(std::vector<Iterator>& iters)
: iters(iters) {}
struct set_id
{
typedef void result_type;
int id;
set_id(int id) : id(id) {}
template <typename T>
void operator()(T& x) const
{
this->dispatch(x, boost::is_base_of<ast::tagged, T>());
}
// This will catch all nodes except those inheriting from ast::tagged
template <typename T>
void dispatch(T& x, boost::mpl::false_) const
{
// (no-op) no need for tags
}
// This will catch all nodes inheriting from ast::tagged
template <typename T>
void dispatch(T& x, boost::mpl::true_) const
{
x.id = id;
}
};
void operator()(ast::operand& ast, Iterator pos) const
{
int id = iters.size();
iters.push_back(pos);
boost::apply_visitor(set_id(id), ast);
}
void operator()(ast::assignment& ast, Iterator pos) const
{
int id = iters.size();
iters.push_back(pos);
ast.lhs.id = id;
}
};
}
#endif

View File

@@ -0,0 +1,116 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
#if !defined(BOOST_SPIRIT_CALC7_AST_HPP)
#define BOOST_SPIRIT_CALC7_AST_HPP
#include <boost/variant/recursive_variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <list>
namespace client { namespace ast
{
///////////////////////////////////////////////////////////////////////////
// The AST
///////////////////////////////////////////////////////////////////////////
struct tagged
{
int id; // Used to annotate the AST with the iterator position.
// This id is used as a key to a map<int, Iterator>
// (not really part of the AST.)
};
struct nil {};
struct signed_;
struct expression;
struct variable : tagged
{
variable(std::string const& name = "") : name(name) {}
std::string name;
};
typedef boost::variant<
nil
, unsigned int
, variable
, boost::recursive_wrapper<signed_>
, boost::recursive_wrapper<expression>
>
operand;
struct signed_
{
char sign;
operand operand_;
};
struct operation
{
char operator_;
operand operand_;
};
struct expression
{
operand first;
std::list<operation> rest;
};
struct assignment
{
variable lhs;
expression rhs;
};
struct variable_declaration
{
assignment assign;
};
typedef boost::variant<
variable_declaration
, assignment>
statement;
typedef std::list<statement> statement_list;
// print functions for debugging
inline std::ostream& operator<<(std::ostream& out, nil) { out << "nil"; return out; }
inline std::ostream& operator<<(std::ostream& out, variable const& var) { out << var.name; return out; }
}}
BOOST_FUSION_ADAPT_STRUCT(
client::ast::signed_,
(char, sign)
(client::ast::operand, operand_)
)
BOOST_FUSION_ADAPT_STRUCT(
client::ast::operation,
(char, operator_)
(client::ast::operand, operand_)
)
BOOST_FUSION_ADAPT_STRUCT(
client::ast::expression,
(client::ast::operand, first)
(std::list<client::ast::operation>, rest)
)
BOOST_FUSION_ADAPT_STRUCT(
client::ast::variable_declaration,
(client::ast::assignment, assign)
)
BOOST_FUSION_ADAPT_STRUCT(
client::ast::assignment,
(client::ast::variable, lhs)
(client::ast::expression, rhs)
)
#endif

View File

@@ -0,0 +1,222 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
#include "compiler.hpp"
#include "vm.hpp"
#include <boost/foreach.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/assert.hpp>
namespace client { namespace code_gen
{
void program::op(int a)
{
code.push_back(a);
}
void program::op(int a, int b)
{
code.push_back(a);
code.push_back(b);
}
void program::op(int a, int b, int c)
{
code.push_back(a);
code.push_back(b);
code.push_back(c);
}
int const* program::find_var(std::string const& name) const
{
std::map<std::string, int>::const_iterator i = variables.find(name);
if (i == variables.end())
return 0;
return &i->second;
}
void program::add_var(std::string const& name)
{
std::size_t n = variables.size();
variables[name] = n;
}
void program::print_variables(std::vector<int> const& stack) const
{
typedef std::pair<std::string, int> pair;
BOOST_FOREACH(pair const& p, variables)
{
std::cout << " " << p.first << ": " << stack[p.second] << std::endl;
}
}
void program::print_assembler() const
{
std::vector<int>::const_iterator pc = code.begin();
std::vector<std::string> locals(variables.size());
typedef std::pair<std::string, int> pair;
BOOST_FOREACH(pair const& p, variables)
{
locals[p.second] = p.first;
std::cout << "local "
<< p.first << ", @" << p.second << std::endl;
}
while (pc != code.end())
{
switch (*pc++)
{
case op_neg:
std::cout << "op_neg" << std::endl;
break;
case op_add:
std::cout << "op_add" << std::endl;
break;
case op_sub:
std::cout << "op_sub" << std::endl;
break;
case op_mul:
std::cout << "op_mul" << std::endl;
break;
case op_div:
std::cout << "op_div" << std::endl;
break;
case op_load:
std::cout << "op_load " << locals[*pc++] << std::endl;
break;
case op_store:
std::cout << "op_store " << locals[*pc++] << std::endl;
break;
case op_int:
std::cout << "op_int " << *pc++ << std::endl;
break;
case op_stk_adj:
std::cout << "op_stk_adj " << *pc++ << std::endl;
break;
}
}
}
bool compiler::operator()(unsigned int x) const
{
program.op(op_int, x);
return true;
}
bool compiler::operator()(ast::variable const& x) const
{
int const* p = program.find_var(x.name);
if (p == 0)
{
std::cout << x.id << std::endl;
error_handler(x.id, "Undeclared variable: " + x.name);
return false;
}
program.op(op_load, *p);
return true;
}
bool compiler::operator()(ast::operation const& x) const
{
if (!boost::apply_visitor(*this, x.operand_))
return false;
switch (x.operator_)
{
case '+': program.op(op_add); break;
case '-': program.op(op_sub); break;
case '*': program.op(op_mul); break;
case '/': program.op(op_div); break;
default: BOOST_ASSERT(0); return false;
}
return true;
}
bool compiler::operator()(ast::signed_ const& x) const
{
if (!boost::apply_visitor(*this, x.operand_))
return false;
switch (x.sign)
{
case '-': program.op(op_neg); break;
case '+': break;
default: BOOST_ASSERT(0); return false;
}
return true;
}
bool compiler::operator()(ast::expression const& x) const
{
if (!boost::apply_visitor(*this, x.first))
return false;
BOOST_FOREACH(ast::operation const& oper, x.rest)
{
if (!(*this)(oper))
return false;
}
return true;
}
bool compiler::operator()(ast::assignment const& x) const
{
if (!(*this)(x.rhs))
return false;
int const* p = program.find_var(x.lhs.name);
if (p == 0)
{
std::cout << x.lhs.id << std::endl;
error_handler(x.lhs.id, "Undeclared variable: " + x.lhs.name);
return false;
}
program.op(op_store, *p);
return true;
}
bool compiler::operator()(ast::variable_declaration const& x) const
{
int const* p = program.find_var(x.assign.lhs.name);
if (p != 0)
{
std::cout << x.assign.lhs.id << std::endl;
error_handler(x.assign.lhs.id, "Duplicate variable: " + x.assign.lhs.name);
return false;
}
bool r = (*this)(x.assign.rhs);
if (r) // don't add the variable if the RHS fails
{
program.add_var(x.assign.lhs.name);
program.op(op_store, *program.find_var(x.assign.lhs.name));
}
return r;
}
bool compiler::operator()(ast::statement_list const& x) const
{
program.clear();
// op_stk_adj 0 for now. we'll know how many variables we'll have later
program.op(op_stk_adj, 0);
BOOST_FOREACH(ast::statement const& s, x)
{
if (!boost::apply_visitor(*this, s))
{
program.clear();
return false;
}
}
program[1] = program.nvars(); // now store the actual number of variables
return true;
}
}}

View File

@@ -0,0 +1,85 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
#if !defined(BOOST_SPIRIT_CALC7_COMPILER_HPP)
#define BOOST_SPIRIT_CALC7_COMPILER_HPP
#include "ast.hpp"
#include "error_handler.hpp"
#include <vector>
#include <map>
#include <boost/function.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/function.hpp>
#include <boost/phoenix/operator.hpp>
namespace client { namespace code_gen
{
///////////////////////////////////////////////////////////////////////////
// The Program
///////////////////////////////////////////////////////////////////////////
struct program
{
void op(int a);
void op(int a, int b);
void op(int a, int b, int c);
int& operator[](std::size_t i) { return code[i]; }
int const& operator[](std::size_t i) const { return code[i]; }
void clear() { code.clear(); variables.clear(); }
std::vector<int> const& operator()() const { return code; }
int nvars() const { return variables.size(); }
int const* find_var(std::string const& name) const;
void add_var(std::string const& name);
void print_variables(std::vector<int> const& stack) const;
void print_assembler() const;
private:
std::map<std::string, int> variables;
std::vector<int> code;
};
///////////////////////////////////////////////////////////////////////////
// The Compiler
///////////////////////////////////////////////////////////////////////////
struct compiler
{
typedef bool result_type;
template <typename ErrorHandler>
compiler(client::code_gen::program& program, ErrorHandler& error_handler_)
: program(program)
{
using namespace boost::phoenix::arg_names;
namespace phx = boost::phoenix;
using boost::phoenix::function;
error_handler = function<ErrorHandler>(error_handler_)(
"Error! ", _2, phx::cref(error_handler_.iters)[_1]);
}
bool operator()(ast::nil) const { BOOST_ASSERT(0); return false; }
bool operator()(unsigned int x) const;
bool operator()(ast::variable const& x) const;
bool operator()(ast::operation const& x) const;
bool operator()(ast::signed_ const& x) const;
bool operator()(ast::expression const& x) const;
bool operator()(ast::assignment const& x) const;
bool operator()(ast::variable_declaration const& x) const;
bool operator()(ast::statement_list const& x) const;
client::code_gen::program& program;
boost::function<
void(int tag, std::string const& what)>
error_handler;
};
}}
#endif

View File

@@ -0,0 +1,93 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
#if !defined(BOOST_SPIRIT_CALC7_ERROR_HANDLER_HPP)
#define BOOST_SPIRIT_CALC7_ERROR_HANDLER_HPP
#include <iostream>
#include <string>
#include <vector>
namespace client
{
///////////////////////////////////////////////////////////////////////////////
// The error handler
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct error_handler
{
template <typename, typename, typename>
struct result { typedef void type; };
error_handler(Iterator first, Iterator last)
: first(first), last(last) {}
template <typename Message, typename What>
void operator()(
Message const& message,
What const& what,
Iterator err_pos) const
{
int line;
Iterator line_start = get_pos(err_pos, line);
if (err_pos != last)
{
std::cout << message << what << " line " << line << ':' << std::endl;
std::cout << get_line(line_start) << std::endl;
for (; line_start != err_pos; ++line_start)
std::cout << ' ';
std::cout << '^' << std::endl;
}
else
{
std::cout << "Unexpected end of file. ";
std::cout << message << what << " line " << line << std::endl;
}
}
Iterator get_pos(Iterator err_pos, int& line) const
{
line = 1;
Iterator i = first;
Iterator line_start = first;
while (i != err_pos)
{
bool eol = false;
if (i != err_pos && *i == '\r') // CR
{
eol = true;
line_start = ++i;
}
if (i != err_pos && *i == '\n') // LF
{
eol = true;
line_start = ++i;
}
if (eol)
++line;
else
++i;
}
return line_start;
}
std::string get_line(Iterator err_pos) const
{
Iterator i = err_pos;
// position i to the next EOL
while (i != last && (*i != '\r' && *i != '\n'))
++i;
return std::string(err_pos, i);
}
Iterator first;
Iterator last;
std::vector<Iterator> iters;
};
}
#endif

View File

@@ -0,0 +1,14 @@
/*=============================================================================
Copyright (c) 2001-2010 Joel de Guzman
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)
=============================================================================*/
#if defined(_MSC_VER)
# pragma warning(disable: 4345)
#endif
#include "expression_def.hpp"
typedef std::string::const_iterator iterator_type;
template struct client::parser::expression<iterator_type>;

View File

@@ -0,0 +1,53 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
#if !defined(BOOST_SPIRIT_CALC7_EXPRESSION_HPP)
#define BOOST_SPIRIT_CALC7_EXPRESSION_HPP
///////////////////////////////////////////////////////////////////////////////
// Spirit v2.5 allows you to suppress automatic generation
// of predefined terminals to speed up complation. With
// BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are
// responsible in creating instances of the terminals that
// you need (e.g. see qi::uint_type uint_ below).
#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment this if you want to enable debugging
// #define BOOST_SPIRIT_QI_DEBUG
///////////////////////////////////////////////////////////////////////////////
#include <boost/spirit/include/qi.hpp>
#include "ast.hpp"
#include "error_handler.hpp"
#include <vector>
namespace client { namespace parser
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
// The expression grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct expression : qi::grammar<Iterator, ast::expression(), ascii::space_type>
{
expression(error_handler<Iterator>& error_handler);
qi::rule<Iterator, ast::expression(), ascii::space_type> expr;
qi::rule<Iterator, ast::expression(), ascii::space_type> additive_expr;
qi::rule<Iterator, ast::expression(), ascii::space_type> multiplicative_expr;
qi::rule<Iterator, ast::operand(), ascii::space_type> unary_expr;
qi::rule<Iterator, ast::operand(), ascii::space_type> primary_expr;
qi::rule<Iterator, std::string(), ascii::space_type> identifier;
};
}}
#endif

View File

@@ -0,0 +1,94 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
#include "expression.hpp"
#include "error_handler.hpp"
#include "annotation.hpp"
#include <boost/phoenix/function.hpp>
namespace client { namespace parser
{
template <typename Iterator>
expression<Iterator>::expression(error_handler<Iterator>& error_handler)
: expression::base_type(expr)
{
qi::_1_type _1;
qi::_2_type _2;
qi::_3_type _3;
qi::_4_type _4;
qi::char_type char_;
qi::uint_type uint_;
qi::_val_type _val;
qi::raw_type raw;
qi::lexeme_type lexeme;
qi::alpha_type alpha;
qi::alnum_type alnum;
using qi::on_error;
using qi::on_success;
using qi::fail;
using boost::phoenix::function;
typedef function<client::error_handler<Iterator> > error_handler_function;
typedef function<client::annotation<Iterator> > annotation_function;
expr =
additive_expr.alias()
;
additive_expr =
multiplicative_expr
>> *( (char_('+') > multiplicative_expr)
| (char_('-') > multiplicative_expr)
)
;
multiplicative_expr =
unary_expr
>> *( (char_('*') > unary_expr)
| (char_('/') > unary_expr)
)
;
unary_expr =
primary_expr
| (char_('-') > primary_expr)
| (char_('+') > primary_expr)
;
primary_expr =
uint_
| identifier
| '(' > expr > ')'
;
identifier =
raw[lexeme[(alpha | '_') >> *(alnum | '_')]]
;
// Debugging and error handling and reporting support.
BOOST_SPIRIT_DEBUG_NODES(
(expr)
(additive_expr)
(multiplicative_expr)
(unary_expr)
(primary_expr)
(identifier)
);
// Error handling: on error in expr, call error_handler.
on_error<fail>(expr,
error_handler_function(error_handler)(
"Error! Expecting ", _4, _3));
// Annotation: on success in primary_expr, call annotation.
on_success(primary_expr,
annotation_function(error_handler.iters)(_val, _1));
}
}}

View File

@@ -0,0 +1,101 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// Now we'll introduce variables and assignment. This time, we'll also
// be renaming some of the rules -- a strategy for a grander scheme
// to come ;-)
//
// This version also shows off grammar modularization. Here you will
// see how expressions and statements are built as modular grammars.
//
// [ JDG April 9, 2007 ] spirit2
// [ JDG February 18, 2011 ] Pure attributes. No semantic actions.
//
///////////////////////////////////////////////////////////////////////////////
#include "statement.hpp"
#include "vm.hpp"
#include "compiler.hpp"
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Statement parser...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Type some statements... ";
std::cout << "An empty line ends input, compiles, runs and prints results\n\n";
std::cout << "Example:\n\n";
std::cout << " var a = 123;\n";
std::cout << " var b = 456;\n";
std::cout << " var c = a + b * 2;\n\n";
std::cout << "-------------------------\n";
std::string str;
std::string source;
while (std::getline(std::cin, str))
{
if (str.empty())
break;
source += str + '\n';
}
typedef std::string::const_iterator iterator_type;
iterator_type iter = source.begin();
iterator_type end = source.end();
client::vmachine vm; // Our virtual machine
client::code_gen::program program; // Our VM program
client::ast::statement_list ast; // Our AST
client::error_handler<iterator_type>
error_handler(iter, end); // Our error handler
client::parser::statement<iterator_type>
parser(error_handler); // Our parser
client::code_gen::compiler
compile(program, error_handler); // Our compiler
boost::spirit::ascii::space_type space;
bool success = phrase_parse(iter, end, parser, space, ast);
std::cout << "-------------------------\n";
if (success && iter == end)
{
if (compile(ast))
{
std::cout << "Success\n";
std::cout << "-------------------------\n";
vm.execute(program());
std::cout << "-------------------------\n";
std::cout << "Assembler----------------\n\n";
program.print_assembler();
std::cout << "-------------------------\n";
std::cout << "Results------------------\n\n";
program.print_variables(vm.get_stack());
}
else
{
std::cout << "Compile failure\n";
}
}
else
{
std::cout << "Parse failure\n";
}
std::cout << "-------------------------\n\n";
return 0;
}

View File

@@ -0,0 +1,14 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
#if defined(_MSC_VER)
# pragma warning(disable: 4345)
#endif
#include "statement_def.hpp"
typedef std::string::const_iterator iterator_type;
template struct client::parser::statement<iterator_type>;

View File

@@ -0,0 +1,32 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
#if !defined(BOOST_SPIRIT_CALC7_STATEMENT_HPP)
#define BOOST_SPIRIT_CALC7_STATEMENT_HPP
#include "expression.hpp"
namespace client { namespace parser
{
///////////////////////////////////////////////////////////////////////////////
// The statement grammar
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct statement : qi::grammar<Iterator, ast::statement_list(), ascii::space_type>
{
statement(error_handler<Iterator>& error_handler);
expression<Iterator> expr;
qi::rule<Iterator, ast::statement_list(), ascii::space_type> statement_list;
qi::rule<Iterator, ast::variable_declaration(), ascii::space_type> variable_declaration;
qi::rule<Iterator, ast::assignment(), ascii::space_type> assignment;
qi::rule<Iterator, std::string(), ascii::space_type> identifier;
};
}}
#endif

View File

@@ -0,0 +1,75 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
#include "statement.hpp"
#include "error_handler.hpp"
#include "annotation.hpp"
namespace client { namespace parser
{
template <typename Iterator>
statement<Iterator>::statement(error_handler<Iterator>& error_handler)
: statement::base_type(statement_list), expr(error_handler)
{
qi::_1_type _1;
qi::_2_type _2;
qi::_3_type _3;
qi::_4_type _4;
qi::_val_type _val;
qi::raw_type raw;
qi::lexeme_type lexeme;
qi::alpha_type alpha;
qi::alnum_type alnum;
using qi::on_error;
using qi::on_success;
using qi::fail;
using boost::phoenix::function;
typedef function<client::error_handler<Iterator> > error_handler_function;
typedef function<client::annotation<Iterator> > annotation_function;
statement_list =
+(variable_declaration | assignment)
;
identifier =
raw[lexeme[(alpha | '_') >> *(alnum | '_')]]
;
variable_declaration =
lexeme["var" >> !(alnum | '_')] // make sure we have whole words
> assignment
;
assignment =
identifier
> '='
> expr
> ';'
;
// Debugging and error handling and reporting support.
BOOST_SPIRIT_DEBUG_NODES(
(statement_list)
(identifier)
(variable_declaration)
(assignment)
);
// Error handling: on error in statement_list, call error_handler.
on_error<fail>(statement_list,
error_handler_function(error_handler)(
"Error! Expecting ", _4, _3));
// Annotation: on success in assignment, call annotation.
on_success(assignment,
annotation_function(error_handler.iters)(_val, _1));
}
}}

View File

@@ -0,0 +1,66 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
#include "vm.hpp"
namespace client
{
void vmachine::execute(std::vector<int> const& code)
{
std::vector<int>::const_iterator pc = code.begin();
std::vector<int>::iterator locals = stack.begin();
stack_ptr = stack.begin();
while (pc != code.end())
{
switch (*pc++)
{
case op_neg:
stack_ptr[-1] = -stack_ptr[-1];
break;
case op_add:
--stack_ptr;
stack_ptr[-1] += stack_ptr[0];
break;
case op_sub:
--stack_ptr;
stack_ptr[-1] -= stack_ptr[0];
break;
case op_mul:
--stack_ptr;
stack_ptr[-1] *= stack_ptr[0];
break;
case op_div:
--stack_ptr;
stack_ptr[-1] /= stack_ptr[0];
break;
case op_load:
*stack_ptr++ = locals[*pc++];
break;
case op_store:
--stack_ptr;
locals[*pc++] = stack_ptr[0];
break;
case op_int:
*stack_ptr++ = *pc++;
break;
case op_stk_adj:
stack_ptr = stack.begin() + *pc++;
break;
}
}
}
}

View File

@@ -0,0 +1,52 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
#if !defined(BOOST_SPIRIT_CALC7_VM_HPP)
#define BOOST_SPIRIT_CALC7_VM_HPP
#include <vector>
namespace client
{
///////////////////////////////////////////////////////////////////////////
// The Virtual Machine
///////////////////////////////////////////////////////////////////////////
enum byte_code
{
op_neg, // negate the top stack entry
op_add, // add top two stack entries
op_sub, // subtract top two stack entries
op_mul, // multiply top two stack entries
op_div, // divide top two stack entries
op_load, // load a variable
op_store, // store a variable
op_int, // push constant integer into the stack
op_stk_adj // adjust the stack for local variables
};
class vmachine
{
public:
vmachine(unsigned stackSize = 4096)
: stack(stackSize)
, stack_ptr(stack.begin())
{
}
void execute(std::vector<int> const& code);
std::vector<int> const& get_stack() const { return stack; };
private:
std::vector<int> stack;
std::vector<int>::iterator stack_ptr;
};
}
#endif

View File

@@ -0,0 +1,78 @@
/*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman
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)
=============================================================================*/
#if !defined(BOOST_SPIRIT_CALC8_ANNOTATION_HPP)
#define BOOST_SPIRIT_CALC8_ANNOTATION_HPP
#include <map>
#include <boost/variant/apply_visitor.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/mpl/bool.hpp>
#include "ast.hpp"
namespace client
{
///////////////////////////////////////////////////////////////////////////////
// The annotation handler links the AST to a map of iterator positions
// for the purpose of subsequent semantic error handling when the
// program is being compiled.
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct annotation
{
template <typename, typename>
struct result { typedef void type; };
std::vector<Iterator>& iters;
annotation(std::vector<Iterator>& iters)
: iters(iters) {}
struct set_id
{
typedef void result_type;
int id;
set_id(int id) : id(id) {}
template <typename T>
void operator()(T& x) const
{
this->dispatch(x, boost::is_base_of<ast::tagged, T>());
}
// This will catch all nodes except those inheriting from ast::tagged
template <typename T>
void dispatch(T& x, boost::mpl::false_) const
{
// (no-op) no need for tags
}
// This will catch all nodes inheriting from ast::tagged
template <typename T>
void dispatch(T& x, boost::mpl::true_) const
{
x.id = id;
}
};
void operator()(ast::operand& ast, Iterator pos) const
{
int id = iters.size();
iters.push_back(pos);
boost::apply_visitor(set_id(id), ast);
}
void operator()(ast::assignment& ast, Iterator pos) const
{
int id = iters.size();
iters.push_back(pos);
ast.lhs.id = id;
}
};
}
#endif

Some files were not shown because too many files have changed in this diff Show More