Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r69202 - trunk/libs/spirit/example/qi/compiler_tutorial
From: joel_at_[hidden]
Date: 2011-02-23 02:21:41


Author: djowel
Date: 2011-02-23 02:21:33 EST (Wed, 23 Feb 2011)
New Revision: 69202
URL: http://svn.boost.org/trac/boost/changeset/69202

Log:
the compiler tutorial
Added:
   trunk/libs/spirit/example/qi/compiler_tutorial/
   trunk/libs/spirit/example/qi/compiler_tutorial/calc1.cpp (contents, props changed)
   trunk/libs/spirit/example/qi/compiler_tutorial/calc2.cpp (contents, props changed)
   trunk/libs/spirit/example/qi/compiler_tutorial/calc3.cpp (contents, props changed)
   trunk/libs/spirit/example/qi/compiler_tutorial/calc4.cpp (contents, props changed)
   trunk/libs/spirit/example/qi/compiler_tutorial/calc5.cpp (contents, props changed)

Added: trunk/libs/spirit/example/qi/compiler_tutorial/calc1.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/spirit/example/qi/compiler_tutorial/calc1.cpp 2011-02-23 02:21:33 EST (Wed, 23 Feb 2011)
@@ -0,0 +1,118 @@
+/*=============================================================================
+ 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/config/warning_disable.hpp>
+#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;
+}
+
+

Added: trunk/libs/spirit/example/qi/compiler_tutorial/calc2.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/spirit/example/qi/compiler_tutorial/calc2.cpp 2011-02-23 02:21:33 EST (Wed, 23 Feb 2011)
@@ -0,0 +1,133 @@
+/*=============================================================================
+ 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/config/warning_disable.hpp>
+#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;
+}
+
+

Added: trunk/libs/spirit/example/qi/compiler_tutorial/calc3.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/spirit/example/qi/compiler_tutorial/calc3.cpp 2011-02-23 02:21:33 EST (Wed, 23 Feb 2011)
@@ -0,0 +1,124 @@
+/*=============================================================================
+ 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/config/warning_disable.hpp>
+#include <boost/spirit/include/qi.hpp>
+#include <boost/spirit/include/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;
+}
+
+

Added: trunk/libs/spirit/example/qi/compiler_tutorial/calc4.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/spirit/example/qi/compiler_tutorial/calc4.cpp 2011-02-23 02:21:33 EST (Wed, 23 Feb 2011)
@@ -0,0 +1,272 @@
+/*=============================================================================
+ 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
+
+#include <boost/config/warning_disable.hpp>
+#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
+{
+ 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
+{
+ 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);
+ }
+ }
+ };
+
+ 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;
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Our 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 printer; // 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";
+ printer(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;
+}
+
+

Added: trunk/libs/spirit/example/qi/compiler_tutorial/calc5.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/spirit/example/qi/compiler_tutorial/calc5.cpp 2011-02-23 02:21:33 EST (Wed, 23 Feb 2011)
@@ -0,0 +1,319 @@
+/*=============================================================================
+ 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
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment this if you want to enable debugging
+//#define BOOST_SPIRIT_QI_DEBUG
+///////////////////////////////////////////////////////////////////////////////
+
+#include <boost/config/warning_disable.hpp>
+#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/spirit/include/phoenix_function.hpp>
+#include <boost/foreach.hpp>
+
+#include <iostream>
+#include <string>
+
+namespace client { namespace 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
+{
+ 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);
+ }
+ }
+ };
+
+ 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 printer; // 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";
+ printer(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;
+}
+
+


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk