diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
commit | 483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch) | |
tree | e5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/boost/libs/spirit/example | |
parent | Initial commit. (diff) | |
download | ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.tar.xz ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.zip |
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/boost/libs/spirit/example')
344 files changed, 41226 insertions, 0 deletions
diff --git a/src/boost/libs/spirit/example/Jamfile b/src/boost/libs/spirit/example/Jamfile new file mode 100644 index 00000000..4aa2c3dd --- /dev/null +++ b/src/boost/libs/spirit/example/Jamfile @@ -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) ; + } +} + diff --git a/src/boost/libs/spirit/example/karma/Jamfile b/src/boost/libs/spirit/example/karma/Jamfile new file mode 100644 index 00000000..a93cbb7a --- /dev/null +++ b/src/boost/libs/spirit/example/karma/Jamfile @@ -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 ; + diff --git a/src/boost/libs/spirit/example/karma/actions.cpp b/src/boost/libs/spirit/example/karma/actions.cpp new file mode 100644 index 00000000..d5b5efe2 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/actions.cpp @@ -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/config/warning_disable.hpp> +#include <boost/spirit/include/karma.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/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 + 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 + 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; +} + diff --git a/src/boost/libs/spirit/example/karma/auto_facilities.cpp b/src/boost/libs/spirit/example/karma/auto_facilities.cpp new file mode 100644 index 00000000..b094f40a --- /dev/null +++ b/src/boost/libs/spirit/example/karma/auto_facilities.cpp @@ -0,0 +1,244 @@ +// 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 <boost/config/warning_disable.hpp> + +#include <iostream> +#include <string> +#include <vector> +#include <list> +#include <map> +#include <algorithm> +#include <cstdlib> + +#include <boost/range.hpp> +#include <boost/array.hpp> +#include <boost/fusion/include/std_pair.hpp> +#include <boost/fusion/include/array.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; +} + diff --git a/src/boost/libs/spirit/example/karma/basic_facilities.cpp b/src/boost/libs/spirit/example/karma/basic_facilities.cpp new file mode 100644 index 00000000..63514032 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/basic_facilities.cpp @@ -0,0 +1,229 @@ +// 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 <boost/config/warning_disable.hpp> + +#include <iostream> +#include <string> +#include <vector> +#include <list> +#include <map> +#include <algorithm> +#include <cstdlib> + +#include <boost/range.hpp> +#include <boost/array.hpp> +#include <boost/date_time/gregorian/gregorian.hpp> +#include <boost/fusion/include/std_pair.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; +} + diff --git a/src/boost/libs/spirit/example/karma/calc2_ast.hpp b/src/boost/libs/spirit/example/karma/calc2_ast.hpp new file mode 100644 index 00000000..d1ee92e8 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/calc2_ast.hpp @@ -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/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_function.hpp> +#include <boost/spirit/include/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 diff --git a/src/boost/libs/spirit/example/karma/calc2_ast_dump.cpp b/src/boost/libs/spirit/example/karma/calc2_ast_dump.cpp new file mode 100644 index 00000000..ac15c7ca --- /dev/null +++ b/src/boost/libs/spirit/example/karma/calc2_ast_dump.cpp @@ -0,0 +1,170 @@ +/*============================================================================= + 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 <boost/config/warning_disable.hpp> + +#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; +} + + diff --git a/src/boost/libs/spirit/example/karma/calc2_ast_rpn.cpp b/src/boost/libs/spirit/example/karma/calc2_ast_rpn.cpp new file mode 100644 index 00000000..955f2467 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/calc2_ast_rpn.cpp @@ -0,0 +1,172 @@ +/*============================================================================= + 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 <boost/config/warning_disable.hpp> + +#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; +} + + diff --git a/src/boost/libs/spirit/example/karma/calc2_ast_vm.cpp b/src/boost/libs/spirit/example/karma/calc2_ast_vm.cpp new file mode 100644 index 00000000..77fab096 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/calc2_ast_vm.cpp @@ -0,0 +1,248 @@ +/*============================================================================= + 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 <boost/config/warning_disable.hpp> + +#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; +} + + diff --git a/src/boost/libs/spirit/example/karma/calc2_ast_vm.hpp b/src/boost/libs/spirit/example/karma/calc2_ast_vm.hpp new file mode 100644 index 00000000..b3a51149 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/calc2_ast_vm.hpp @@ -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/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_function.hpp> +#include <boost/spirit/include/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 diff --git a/src/boost/libs/spirit/example/karma/classify_char.cpp b/src/boost/libs/spirit/example/karma/classify_char.cpp new file mode 100644 index 00000000..df113f33 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/classify_char.cpp @@ -0,0 +1,103 @@ +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/karma.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/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; +} + + diff --git a/src/boost/libs/spirit/example/karma/complex_number.cpp b/src/boost/libs/spirit/example/karma/complex_number.cpp new file mode 100644 index 00000000..d832553b --- /dev/null +++ b/src/boost/libs/spirit/example/karma/complex_number.cpp @@ -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. +// +// [ HK July 7, 2009 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/karma.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/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; +} + + diff --git a/src/boost/libs/spirit/example/karma/complex_number_adapt.cpp b/src/boost/libs/spirit/example/karma/complex_number_adapt.cpp new file mode 100644 index 00000000..25135e08 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/complex_number_adapt.cpp @@ -0,0 +1,153 @@ +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/karma.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/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_ + ), + // 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; +} + + diff --git a/src/boost/libs/spirit/example/karma/complex_number_easier.cpp b/src/boost/libs/spirit/example/karma/complex_number_easier.cpp new file mode 100644 index 00000000..0fe05149 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/complex_number_easier.cpp @@ -0,0 +1,133 @@ +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/karma.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/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_ + ), + // 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; +} + + diff --git a/src/boost/libs/spirit/example/karma/customize_counter.cpp b/src/boost/libs/spirit/example/karma/customize_counter.cpp new file mode 100644 index 00000000..62506928 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/customize_counter.cpp @@ -0,0 +1,127 @@ +/*============================================================================= + 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) +=============================================================================*/ +#include <boost/config/warning_disable.hpp> + +//[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; +} + diff --git a/src/boost/libs/spirit/example/karma/customize_embedded_container.cpp b/src/boost/libs/spirit/example/karma/customize_embedded_container.cpp new file mode 100644 index 00000000..c0c071b4 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/customize_embedded_container.cpp @@ -0,0 +1,108 @@ +/*============================================================================= + 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) +=============================================================================*/ +#include <boost/config/warning_disable.hpp> + +//[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; +} + diff --git a/src/boost/libs/spirit/example/karma/customize_use_as_container.cpp b/src/boost/libs/spirit/example/karma/customize_use_as_container.cpp new file mode 100644 index 00000000..c0116c8a --- /dev/null +++ b/src/boost/libs/spirit/example/karma/customize_use_as_container.cpp @@ -0,0 +1,164 @@ +/*============================================================================= + 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) +=============================================================================*/ +#include <boost/config/warning_disable.hpp> + +//[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; +} + diff --git a/src/boost/libs/spirit/example/karma/escaped_string.cpp b/src/boost/libs/spirit/example/karma/escaped_string.cpp new file mode 100644 index 00000000..f3c2fe6f --- /dev/null +++ b/src/boost/libs/spirit/example/karma/escaped_string.cpp @@ -0,0 +1,66 @@ +// 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/config/warning_disable.hpp> +#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; +} diff --git a/src/boost/libs/spirit/example/karma/generate_code.cpp b/src/boost/libs/spirit/example/karma/generate_code.cpp new file mode 100644 index 00000000..3a409a77 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/generate_code.cpp @@ -0,0 +1,80 @@ +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/karma.hpp> +#include <boost/spirit/include/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; +} diff --git a/src/boost/libs/spirit/example/karma/key_value_sequence.cpp b/src/boost/libs/spirit/example/karma/key_value_sequence.cpp new file mode 100644 index 00000000..db2de2cc --- /dev/null +++ b/src/boost/libs/spirit/example/karma/key_value_sequence.cpp @@ -0,0 +1,74 @@ +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/karma.hpp> +#include <boost/spirit/include/karma_stream.hpp> +#include <boost/spirit/include/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; +} + diff --git a/src/boost/libs/spirit/example/karma/mini_xml_karma.cpp b/src/boost/libs/spirit/example/karma/mini_xml_karma.cpp new file mode 100644 index 00000000..596fd78c --- /dev/null +++ b/src/boost/libs/spirit/example/karma/mini_xml_karma.cpp @@ -0,0 +1,222 @@ +/*============================================================================= + 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/config/warning_disable.hpp> + +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/karma.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_fusion.hpp> +#include <boost/spirit/include/phoenix_function.hpp> +#include <boost/spirit/include/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; + } +} + + diff --git a/src/boost/libs/spirit/example/karma/num_list1.cpp b/src/boost/libs/spirit/example/karma/num_list1.cpp new file mode 100644 index 00000000..7b079b04 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/num_list1.cpp @@ -0,0 +1,121 @@ +/*============================================================================= + 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/config/warning_disable.hpp> +#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; +} + + diff --git a/src/boost/libs/spirit/example/karma/num_list2.cpp b/src/boost/libs/spirit/example/karma/num_list2.cpp new file mode 100644 index 00000000..ce16fafe --- /dev/null +++ b/src/boost/libs/spirit/example/karma/num_list2.cpp @@ -0,0 +1,117 @@ +/*============================================================================= + 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/config/warning_disable.hpp> +#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; +} + + diff --git a/src/boost/libs/spirit/example/karma/num_list3.cpp b/src/boost/libs/spirit/example/karma/num_list3.cpp new file mode 100644 index 00000000..253de1b4 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/num_list3.cpp @@ -0,0 +1,150 @@ +/*============================================================================= + 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/config/warning_disable.hpp> +#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; +} + + diff --git a/src/boost/libs/spirit/example/karma/num_matrix.cpp b/src/boost/libs/spirit/example/karma/num_matrix.cpp new file mode 100644 index 00000000..0622e096 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/num_matrix.cpp @@ -0,0 +1,114 @@ +/*============================================================================= + 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/config/warning_disable.hpp> +#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; +} + + diff --git a/src/boost/libs/spirit/example/karma/printf_style_double_format.cpp b/src/boost/libs/spirit/example/karma/printf_style_double_format.cpp new file mode 100644 index 00000000..b1c094da --- /dev/null +++ b/src/boost/libs/spirit/example/karma/printf_style_double_format.cpp @@ -0,0 +1,282 @@ +// 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/config/warning_disable.hpp> +#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; +} + diff --git a/src/boost/libs/spirit/example/karma/quick_start1.cpp b/src/boost/libs/spirit/example/karma/quick_start1.cpp new file mode 100644 index 00000000..4a99b95c --- /dev/null +++ b/src/boost/libs/spirit/example/karma/quick_start1.cpp @@ -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 main purpose of this example is to show how a single container type can +// be formatted using different output grammars. + +#include <boost/config/warning_disable.hpp> +#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; +} + diff --git a/src/boost/libs/spirit/example/karma/quoted_strings.cpp b/src/boost/libs/spirit/example/karma/quoted_strings.cpp new file mode 100644 index 00000000..ecbdd969 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/quoted_strings.cpp @@ -0,0 +1,70 @@ +// 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 <boost/config/warning_disable.hpp> + +#include <string> +#include <vector> + +#include <boost/spirit/include/karma.hpp> +#include <boost/spirit/include/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; +} + diff --git a/src/boost/libs/spirit/example/karma/reference.cpp b/src/boost/libs/spirit/example/karma/reference.cpp new file mode 100644 index 00000000..dd11f26e --- /dev/null +++ b/src/boost/libs/spirit/example/karma/reference.cpp @@ -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) +=============================================================================*/ +#include <boost/config/warning_disable.hpp> + +//[reference_karma_includes +#include <boost/spirit/include/karma.hpp> +#include <boost/spirit/include/support_utree.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/fusion/include/std_pair.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; +} diff --git a/src/boost/libs/spirit/example/karma/reorder_struct.cpp b/src/boost/libs/spirit/example/karma/reorder_struct.cpp new file mode 100644 index 00000000..8e5b0149 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/reorder_struct.cpp @@ -0,0 +1,105 @@ +// 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/config/warning_disable.hpp> +#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; +} diff --git a/src/boost/libs/spirit/example/karma/simple_columns_directive.cpp b/src/boost/libs/spirit/example/karma/simple_columns_directive.cpp new file mode 100644 index 00000000..3a5348af --- /dev/null +++ b/src/boost/libs/spirit/example/karma/simple_columns_directive.cpp @@ -0,0 +1,49 @@ +// 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 <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; +} diff --git a/src/boost/libs/spirit/example/karma/simple_columns_directive.hpp b/src/boost/libs/spirit/example/karma/simple_columns_directive.hpp new file mode 100644 index 00000000..56403f33 --- /dev/null +++ b/src/boost/libs/spirit/example/karma/simple_columns_directive.hpp @@ -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 diff --git a/src/boost/libs/spirit/example/lex/Jamfile b/src/boost/libs/spirit/example/lex/Jamfile new file mode 100644 index 00000000..79887762 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/Jamfile @@ -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 ; diff --git a/src/boost/libs/spirit/example/lex/custom_token_attribute.cpp b/src/boost/libs/spirit/example/lex/custom_token_attribute.cpp new file mode 100644 index 00000000..e29bbcee --- /dev/null +++ b/src/boost/libs/spirit/example/lex/custom_token_attribute.cpp @@ -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; +} + diff --git a/src/boost/libs/spirit/example/lex/example.hpp b/src/boost/libs/spirit/example/lex/example.hpp new file mode 100644 index 00000000..41399a5f --- /dev/null +++ b/src/boost/libs/spirit/example/lex/example.hpp @@ -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>()); +} + diff --git a/src/boost/libs/spirit/example/lex/example1.cpp b/src/boost/libs/spirit/example/lex/example1.cpp new file mode 100644 index 00000000..b157b082 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/example1.cpp @@ -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) + +// 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/config/warning_disable.hpp> +#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; +} diff --git a/src/boost/libs/spirit/example/lex/example1.input b/src/boost/libs/spirit/example/lex/example1.input new file mode 100644 index 00000000..e2aa2b62 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/example1.input @@ -0,0 +1,5 @@ +{ + hello world, + hello world, + goodbye +} diff --git a/src/boost/libs/spirit/example/lex/example2.cpp b/src/boost/libs/spirit/example/lex/example2.cpp new file mode 100644 index 00000000..68a47667 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/example2.cpp @@ -0,0 +1,169 @@ +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/spirit/include/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; +} diff --git a/src/boost/libs/spirit/example/lex/example2.input b/src/boost/libs/spirit/example/lex/example2.input new file mode 100644 index 00000000..2f768330 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/example2.input @@ -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. + + diff --git a/src/boost/libs/spirit/example/lex/example3.cpp b/src/boost/libs/spirit/example/lex/example3.cpp new file mode 100644 index 00000000..83800331 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/example3.cpp @@ -0,0 +1,150 @@ +// 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/config/warning_disable.hpp> +#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; +} diff --git a/src/boost/libs/spirit/example/lex/example3.input b/src/boost/libs/spirit/example/lex/example3.input new file mode 100644 index 00000000..497cab43 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/example3.input @@ -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))) ... + diff --git a/src/boost/libs/spirit/example/lex/example4.cpp b/src/boost/libs/spirit/example/lex/example4.cpp new file mode 100644 index 00000000..2970340a --- /dev/null +++ b/src/boost/libs/spirit/example/lex/example4.cpp @@ -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) + +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/spirit/include/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; +} diff --git a/src/boost/libs/spirit/example/lex/example4.input b/src/boost/libs/spirit/example/lex/example4.input new file mode 100644 index 00000000..7a5ff76d --- /dev/null +++ b/src/boost/libs/spirit/example/lex/example4.input @@ -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; + +} + + diff --git a/src/boost/libs/spirit/example/lex/example5.cpp b/src/boost/libs/spirit/example/lex/example5.cpp new file mode 100644 index 00000000..8083042c --- /dev/null +++ b/src/boost/libs/spirit/example/lex/example5.cpp @@ -0,0 +1,273 @@ +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/spirit/include/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; +} diff --git a/src/boost/libs/spirit/example/lex/example5.input b/src/boost/libs/spirit/example/lex/example5.input new file mode 100644 index 00000000..1cbecf52 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/example5.input @@ -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 { } +} + diff --git a/src/boost/libs/spirit/example/lex/example6.cpp b/src/boost/libs/spirit/example/lex/example6.cpp new file mode 100644 index 00000000..843b2d98 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/example6.cpp @@ -0,0 +1,249 @@ +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/spirit/include/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; +} diff --git a/src/boost/libs/spirit/example/lex/example6.input b/src/boost/libs/spirit/example/lex/example6.input new file mode 100644 index 00000000..992cbeb3 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/example6.input @@ -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; + +} + + diff --git a/src/boost/libs/spirit/example/lex/lexer_debug_support.cpp b/src/boost/libs/spirit/example/lex/lexer_debug_support.cpp new file mode 100644 index 00000000..c6d3f76d --- /dev/null +++ b/src/boost/libs/spirit/example/lex/lexer_debug_support.cpp @@ -0,0 +1,109 @@ +// 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/config/warning_disable.hpp> + +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/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; +} diff --git a/src/boost/libs/spirit/example/lex/print_number_tokenids.cpp b/src/boost/libs/spirit/example/lex/print_number_tokenids.cpp new file mode 100644 index 00000000..3789f0eb --- /dev/null +++ b/src/boost/libs/spirit/example/lex/print_number_tokenids.cpp @@ -0,0 +1,121 @@ +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/spirit/include/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; +} + + + diff --git a/src/boost/libs/spirit/example/lex/print_numbers.cpp b/src/boost/libs/spirit/example/lex/print_numbers.cpp new file mode 100644 index 00000000..e128af97 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/print_numbers.cpp @@ -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) + +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/spirit/include/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; +} + + + diff --git a/src/boost/libs/spirit/example/lex/print_numbers.input b/src/boost/libs/spirit/example/lex/print_numbers.input new file mode 100644 index 00000000..992cbeb3 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/print_numbers.input @@ -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; + +} + + diff --git a/src/boost/libs/spirit/example/lex/reference.cpp b/src/boost/libs/spirit/example/lex/reference.cpp new file mode 100644 index 00000000..0a878e24 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/reference.cpp @@ -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/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <iostream> +#include <string> +//] + +//[reference_lex_test +//] + +int main() +{ + { + //[reference_lex_using_declarations_char + //] + + //[reference_lex_char + //] + } + + return 0; +} diff --git a/src/boost/libs/spirit/example/lex/static_lexer/Jamfile b/src/boost/libs/spirit/example/lex/static_lexer/Jamfile new file mode 100644 index 00000000..25c30d0d --- /dev/null +++ b/src/boost/libs/spirit/example/lex/static_lexer/Jamfile @@ -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 ; + diff --git a/src/boost/libs/spirit/example/lex/static_lexer/word_count.input b/src/boost/libs/spirit/example/lex/static_lexer/word_count.input new file mode 100644 index 00000000..2f768330 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/static_lexer/word_count.input @@ -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. + + diff --git a/src/boost/libs/spirit/example/lex/static_lexer/word_count_generate.cpp b/src/boost/libs/spirit/example/lex/static_lexer/word_count_generate.cpp new file mode 100644 index 00000000..87f0527f --- /dev/null +++ b/src/boost/libs/spirit/example/lex/static_lexer/word_count_generate.cpp @@ -0,0 +1,45 @@ +// 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/config/warning_disable.hpp> +#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; +} +//] diff --git a/src/boost/libs/spirit/example/lex/static_lexer/word_count_lexer_generate.cpp b/src/boost/libs/spirit/example/lex/static_lexer/word_count_lexer_generate.cpp new file mode 100644 index 00000000..65593cda --- /dev/null +++ b/src/boost/libs/spirit/example/lex/static_lexer/word_count_lexer_generate.cpp @@ -0,0 +1,45 @@ +// 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/config/warning_disable.hpp> +#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; +} +//] diff --git a/src/boost/libs/spirit/example/lex/static_lexer/word_count_lexer_static.cpp b/src/boost/libs/spirit/example/lex/static_lexer/word_count_lexer_static.cpp new file mode 100644 index 00000000..2bda5adc --- /dev/null +++ b/src/boost/libs/spirit/example/lex/static_lexer/word_count_lexer_static.cpp @@ -0,0 +1,84 @@ +// 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/config/warning_disable.hpp> +#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 defintion 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; +} +//] diff --git a/src/boost/libs/spirit/example/lex/static_lexer/word_count_lexer_static.hpp b/src/boost/libs/spirit/example/lex/static_lexer/word_count_lexer_static.hpp new file mode 100644 index 00000000..e69b936e --- /dev/null +++ b/src/boost/libs/spirit/example/lex/static_lexer/word_count_lexer_static.hpp @@ -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 diff --git a/src/boost/libs/spirit/example/lex/static_lexer/word_count_lexer_tokens.hpp b/src/boost/libs/spirit/example/lex/static_lexer/word_count_lexer_tokens.hpp new file mode 100644 index 00000000..af52a209 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/static_lexer/word_count_lexer_tokens.hpp @@ -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/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_statement.hpp> +#include <boost/spirit/include/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 diff --git a/src/boost/libs/spirit/example/lex/static_lexer/word_count_static.cpp b/src/boost/libs/spirit/example/lex/static_lexer/word_count_static.cpp new file mode 100644 index 00000000..4ba3bb53 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/static_lexer/word_count_static.cpp @@ -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) + +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +//[wc_static_include +#include <boost/spirit/include/lex_static_lexertl.hpp> +//] +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_statement.hpp> +#include <boost/spirit/include/phoenix_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 defintion 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; +} +//] diff --git a/src/boost/libs/spirit/example/lex/static_lexer/word_count_static.hpp b/src/boost/libs/spirit/example/lex/static_lexer/word_count_static.hpp new file mode 100644 index 00000000..4a7aa3c6 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/static_lexer/word_count_static.hpp @@ -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 diff --git a/src/boost/libs/spirit/example/lex/static_lexer/word_count_tokens.hpp b/src/boost/libs/spirit/example/lex/static_lexer/word_count_tokens.hpp new file mode 100644 index 00000000..5828adde --- /dev/null +++ b/src/boost/libs/spirit/example/lex/static_lexer/word_count_tokens.hpp @@ -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 diff --git a/src/boost/libs/spirit/example/lex/strip_comments.cpp b/src/boost/libs/spirit/example/lex/strip_comments.cpp new file mode 100644 index 00000000..c4e0913a --- /dev/null +++ b/src/boost/libs/spirit/example/lex/strip_comments.cpp @@ -0,0 +1,163 @@ +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/lex_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; + +/////////////////////////////////////////////////////////////////////////////// +// 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; +} + + + diff --git a/src/boost/libs/spirit/example/lex/strip_comments.input b/src/boost/libs/spirit/example/lex/strip_comments.input new file mode 100644 index 00000000..bed0f071 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/strip_comments.input @@ -0,0 +1,162 @@ +// 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/config/warning_disable.hpp> +#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; +} + + + diff --git a/src/boost/libs/spirit/example/lex/strip_comments_lexer.cpp b/src/boost/libs/spirit/example/lex/strip_comments_lexer.cpp new file mode 100644 index 00000000..30e0b34b --- /dev/null +++ b/src/boost/libs/spirit/example/lex/strip_comments_lexer.cpp @@ -0,0 +1,172 @@ +// 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/config/warning_disable.hpp> +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_statement.hpp> +#include <boost/spirit/include/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 alltogether, 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; +} + + + diff --git a/src/boost/libs/spirit/example/lex/word_count.cpp b/src/boost/libs/spirit/example/lex/word_count.cpp new file mode 100644 index 00000000..c6b21d95 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/word_count.cpp @@ -0,0 +1,166 @@ +// 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 + +#include <boost/config/warning_disable.hpp> +//[wcp_includes +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_statement.hpp> +#include <boost/spirit/include/phoenix_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; +} +//] diff --git a/src/boost/libs/spirit/example/lex/word_count.input b/src/boost/libs/spirit/example/lex/word_count.input new file mode 100644 index 00000000..2f768330 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/word_count.input @@ -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. + + diff --git a/src/boost/libs/spirit/example/lex/word_count_functor.cpp b/src/boost/libs/spirit/example/lex/word_count_functor.cpp new file mode 100644 index 00000000..f1969aac --- /dev/null +++ b/src/boost/libs/spirit/example/lex/word_count_functor.cpp @@ -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 + +#include <boost/config/warning_disable.hpp> +//[wcf_includes +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/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 + 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; +} +//] + diff --git a/src/boost/libs/spirit/example/lex/word_count_functor.flex b/src/boost/libs/spirit/example/lex/word_count_functor.flex new file mode 100644 index 00000000..d9c00503 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/word_count_functor.flex @@ -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; +} + diff --git a/src/boost/libs/spirit/example/lex/word_count_functor_flex.cpp b/src/boost/libs/spirit/example/lex/word_count_functor_flex.cpp new file mode 100644 index 00000000..5fc9e734 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/word_count_functor_flex.cpp @@ -0,0 +1,1576 @@ +#line 2 "c:\\CVS\\boost\\libs\\spirit\\example\\lex\\word_count_functor_flex.cpp" +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + * $Header: /home/daffy/u0/vern/flex/RCS/flex.skl,v 2.91 96/09/10 16:58:48 vern Exp $ + */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + +#include <stdio.h> +#include <errno.h> + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include <stdlib.h> +#ifndef _WIN32 +#include <unistd.h> +#endif + +/* Use prototypes in function declarations. */ +#define YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include <io.h> +#include <stdlib.h> +#define YY_USE_CONST +#define YY_USE_PROTOS +#endif + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +extern int yyleng; +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + *yy_cp = yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int yy_size_t; + + +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart YY_PROTO(( FILE *input_file )); + +void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); +void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); +void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); +#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) + +YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size )); +YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str )); +YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len )); + +static void *yy_flex_alloc YY_PROTO(( yy_size_t )); +static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t )); +static void yy_flex_free YY_PROTO(( void * )); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (yy_current_buffer->yy_at_bol) + +typedef unsigned char YY_CHAR; +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; +typedef int yy_state_type; +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state YY_PROTO(( void )); +static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); +static int yy_get_next_buffer YY_PROTO(( void )); +static void yy_fatal_error YY_PROTO(( yyconst char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 4 +#define YY_END_OF_BUFFER 5 +static yyconst short int yy_accept[9] = + { 0, + 0, 0, 5, 1, 3, 2, 1, 0 + } ; + +static yyconst int yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst int yy_meta[4] = + { 0, + 1, 2, 2 + } ; + +static yyconst short int yy_base[10] = + { 0, + 0, 0, 5, 0, 6, 6, 0, 6, 3 + } ; + +static yyconst short int yy_def[10] = + { 0, + 8, 1, 8, 9, 8, 8, 9, 0, 8 + } ; + +static yyconst short int yy_nxt[10] = + { 0, + 4, 5, 6, 7, 8, 3, 8, 8, 8 + } ; + +static yyconst short int yy_chk[10] = + { 0, + 1, 1, 1, 9, 3, 8, 8, 8, 8 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "c:\\CVS\\boost\\libs\\spirit\\example\\lex\\word_count_functor.flex" +#define INITIAL 0 +#line 2 "c:\\CVS\\boost\\libs\\spirit\\example\\lex\\word_count_functor.flex" +// 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) + +#include <boost/timer.hpp> +#if defined(_WIN32) + #include <io.h> +#endif + #define ID_WORD 1000 + #define ID_EOL 1001 + #define ID_CHAR 1002 +#line 380 "c:\\CVS\\boost\\libs\\spirit\\example\\lex\\word_count_functor_flex.cpp" + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap YY_PROTO(( void )); +#else +extern int yywrap YY_PROTO(( void )); +#endif +#endif + +#ifndef YY_NO_UNPUT +static void yyunput YY_PROTO(( int c, char *buf_ptr )); +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int )); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen YY_PROTO(( yyconst char * )); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif +#endif + +#if YY_STACK_USED +static int yy_start_stack_ptr = 0; +static int yy_start_stack_depth = 0; +static int *yy_start_stack = 0; +#ifndef YY_NO_PUSH_STATE +static void yy_push_state YY_PROTO(( int new_state )); +#endif +#ifndef YY_NO_POP_STATE +static void yy_pop_state YY_PROTO(( void )); +#endif +#ifndef YY_NO_TOP_STATE +static int yy_top_state YY_PROTO(( void )); +#endif + +#else +#define YY_NO_PUSH_STATE 1 +#define YY_NO_POP_STATE 1 +#define YY_NO_TOP_STATE 1 +#endif + +#ifdef YY_MALLOC_DECL +YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include <stdlib.h> +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( yy_current_buffer->yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + } +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL int yylex YY_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +YY_DECL + { + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 16 "c:\\CVS\\boost\\libs\\spirit\\example\\lex\\word_count_functor.flex" + +#line 544 "c:\\CVS\\boost\\libs\\spirit\\example\\lex\\word_count_functor_flex.cpp" + + if ( yy_init ) + { + yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! yy_current_buffer ) + yy_current_buffer = + yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 9 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 6 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 17 "c:\\CVS\\boost\\libs\\spirit\\example\\lex\\word_count_functor.flex" +{ return ID_WORD; } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 18 "c:\\CVS\\boost\\libs\\spirit\\example\\lex\\word_count_functor.flex" +{ return ID_EOL; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 19 "c:\\CVS\\boost\\libs\\spirit\\example\\lex\\word_count_functor.flex" +{ return ID_CHAR; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 20 "c:\\CVS\\boost\\libs\\spirit\\example\\lex\\word_count_functor.flex" +ECHO; + YY_BREAK +#line 647 "c:\\CVS\\boost\\libs\\spirit\\example\\lex\\word_count_functor_flex.cpp" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yy_n_chars = yy_current_buffer->yy_n_chars; + yy_current_buffer->yy_input_file = yyin; + yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = + yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of yylex */ + + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + { + register char *dest = yy_current_buffer->yy_ch_buf; + register char *source = yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( yy_current_buffer->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_current_buffer->yy_n_chars = yy_n_chars = 0; + + else + { + int num_to_read = + yy_current_buffer->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = yy_current_buffer; + + int yy_c_buf_p_offset = + (int) (yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yy_flex_realloc( (void *) b->yy_ch_buf, + b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = yy_current_buffer->yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; + + return ret_val; + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +static yy_state_type yy_get_previous_state() + { + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = yy_start; + + for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 9 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) +#else +static yy_state_type yy_try_NUL_trans( yy_current_state ) +yy_state_type yy_current_state; +#endif + { + register int yy_is_jam; + register char *yy_cp = yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 9 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 8); + + return yy_is_jam ? 0 : yy_current_state; + } + + +#ifndef YY_NO_UNPUT +#ifdef YY_USE_PROTOS +static void yyunput( int c, register char *yy_bp ) +#else +static void yyunput( c, yy_bp ) +int c; +register char *yy_bp; +#endif + { + register char *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yy_n_chars + 2; + register char *dest = &yy_current_buffer->yy_ch_buf[ + yy_current_buffer->yy_buf_size + 2]; + register char *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + yy_current_buffer->yy_n_chars = + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + + yytext_ptr = yy_bp; + yy_hold_char = *yy_cp; + yy_c_buf_p = yy_cp; + } +#endif /* ifndef YY_NO_UNPUT */ + + +#ifdef __cplusplus +static int yyinput() +#else +static int input() +#endif + { + int c; + + *yy_c_buf_p = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* This was really a NUL. */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = yy_c_buf_p - yytext_ptr; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /* fall through */ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + return EOF; + + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ + *yy_c_buf_p = '\0'; /* preserve yytext */ + yy_hold_char = *++yy_c_buf_p; + + + return c; + } + + +#ifdef YY_USE_PROTOS +void yyrestart( FILE *input_file ) +#else +void yyrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! yy_current_buffer ) + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void yy_load_buffer_state( void ) +#else +void yy_load_buffer_state() +#endif + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; + } + + +#ifdef YY_USE_PROTOS +void yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yy_flex_free( (void *) b->yy_ch_buf ); + + yy_flex_free( (void *) b ); + } + + +#ifndef _WIN32 +#include <unistd.h> +#else +#ifndef YY_ALWAYS_INTERACTIVE +#ifndef YY_NEVER_INTERACTIVE +extern int isatty YY_PROTO(( int )); +#endif +#endif +#endif + +#ifdef YY_USE_PROTOS +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + + + { + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + +#if YY_ALWAYS_INTERACTIVE + b->yy_is_interactive = 1; +#else +#if YY_NEVER_INTERACTIVE + b->yy_is_interactive = 0; +#else + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef YY_USE_PROTOS +void yy_flush_buffer( YY_BUFFER_STATE b ) +#else +void yy_flush_buffer( b ) +YY_BUFFER_STATE b; +#endif + + { + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == yy_current_buffer ) + yy_load_buffer_state(); + } + + +#ifndef YY_NO_SCAN_BUFFER +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size ) +#else +YY_BUFFER_STATE yy_scan_buffer( base, size ) +char *base; +yy_size_t size; +#endif + { + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef YY_NO_SCAN_STRING +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str ) +#else +YY_BUFFER_STATE yy_scan_string( yy_str ) +yyconst char *yy_str; +#endif + { + int len; + for ( len = 0; yy_str[len]; ++len ) + ; + + return yy_scan_bytes( yy_str, len ); + } +#endif + + +#ifndef YY_NO_SCAN_BYTES +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len ) +#else +YY_BUFFER_STATE yy_scan_bytes( bytes, len ) +yyconst char *bytes; +int len; +#endif + { + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) yy_flex_alloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef YY_NO_PUSH_STATE +#ifdef YY_USE_PROTOS +static void yy_push_state( int new_state ) +#else +static void yy_push_state( new_state ) +int new_state; +#endif + { + if ( yy_start_stack_ptr >= yy_start_stack_depth ) + { + yy_size_t new_size; + + yy_start_stack_depth += YY_START_STACK_INCR; + new_size = yy_start_stack_depth * sizeof( int ); + + if ( ! yy_start_stack ) + yy_start_stack = (int *) yy_flex_alloc( new_size ); + + else + yy_start_stack = (int *) yy_flex_realloc( + (void *) yy_start_stack, new_size ); + + if ( ! yy_start_stack ) + YY_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + yy_start_stack[yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); + } +#endif + + +#ifndef YY_NO_POP_STATE +static void yy_pop_state() + { + if ( --yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yy_start_stack[yy_start_stack_ptr]); + } +#endif + + +#ifndef YY_NO_TOP_STATE +static int yy_top_state() + { + return yy_start_stack[yy_start_stack_ptr - 1]; + } +#endif + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +#ifdef YY_USE_PROTOS +static void yy_fatal_error( yyconst char msg[] ) +#else +static void yy_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); + } + + + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yytext[yyleng] = yy_hold_char; \ + yy_c_buf_p = yytext + n; \ + yy_hold_char = *yy_c_buf_p; \ + *yy_c_buf_p = '\0'; \ + yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef yytext_ptr +#ifdef YY_USE_PROTOS +static void yy_flex_strncpy( char *s1, yyconst char *s2, int n ) +#else +static void yy_flex_strncpy( s1, s2, n ) +char *s1; +yyconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef YY_NEED_STRLEN +#ifdef YY_USE_PROTOS +static int yy_flex_strlen( yyconst char *s ) +#else +static int yy_flex_strlen( s ) +yyconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef YY_USE_PROTOS +static void *yy_flex_alloc( yy_size_t size ) +#else +static void *yy_flex_alloc( size ) +yy_size_t size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef YY_USE_PROTOS +static void *yy_flex_realloc( void *ptr, yy_size_t size ) +#else +static void *yy_flex_realloc( ptr, size ) +void *ptr; +yy_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); + } + +#ifdef YY_USE_PROTOS +static void yy_flex_free( void *ptr ) +#else +static void yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ptr ); + } + +#if YY_MAIN +int main() + { + yylex(); + return 0; + } +#endif +#line 20 "c:\\CVS\\boost\\libs\\spirit\\example\\lex\\word_count_functor.flex" + + +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; +} + diff --git a/src/boost/libs/spirit/example/lex/word_count_lexer.cpp b/src/boost/libs/spirit/example/lex/word_count_lexer.cpp new file mode 100644 index 00000000..57225e40 --- /dev/null +++ b/src/boost/libs/spirit/example/lex/word_count_lexer.cpp @@ -0,0 +1,152 @@ +// 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 + +#include <boost/config/warning_disable.hpp> +//[wcl_includes +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_statement.hpp> +#include <boost/spirit/include/phoenix_algorithm.hpp> +#include <boost/spirit/include/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; +} +//] diff --git a/src/boost/libs/spirit/example/qi/Jamfile b/src/boost/libs/spirit/example/qi/Jamfile new file mode 100644 index 00000000..a071c0d2 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/Jamfile @@ -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 ; + diff --git a/src/boost/libs/spirit/example/qi/actions.cpp b/src/boost/libs/spirit/example/qi/actions.cpp new file mode 100644 index 00000000..c5379703 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/actions.cpp @@ -0,0 +1,108 @@ +/*============================================================================= + 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/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); + //[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); + //[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; +} + + + + diff --git a/src/boost/libs/spirit/example/qi/adapt_template_struct.cpp b/src/boost/libs/spirit/example/qi/adapt_template_struct.cpp new file mode 100644 index 00000000..360707d1 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/adapt_template_struct.cpp @@ -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; +} + + diff --git a/src/boost/libs/spirit/example/qi/boost_array.cpp b/src/boost/libs/spirit/example/qi/boost_array.cpp new file mode 100644 index 00000000..66485b97 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/boost_array.cpp @@ -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; +} diff --git a/src/boost/libs/spirit/example/qi/calc_utree.cpp b/src/boost/libs/spirit/example/qi/calc_utree.cpp new file mode 100644 index 00000000..00293df3 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/calc_utree.cpp @@ -0,0 +1,166 @@ +/*============================================================================= + 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/support_utree.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/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; +} + + diff --git a/src/boost/libs/spirit/example/qi/calc_utree_ast.cpp b/src/boost/libs/spirit/example/qi/calc_utree_ast.cpp new file mode 100644 index 00000000..3465ba08 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/calc_utree_ast.cpp @@ -0,0 +1,164 @@ +/*============================================================================= + 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/config/warning_disable.hpp> +#include <boost/spirit/include/support_utree.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/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; +} + + diff --git a/src/boost/libs/spirit/example/qi/calc_utree_naive.cpp b/src/boost/libs/spirit/example/qi/calc_utree_naive.cpp new file mode 100644 index 00000000..c6ac3ddb --- /dev/null +++ b/src/boost/libs/spirit/example/qi/calc_utree_naive.cpp @@ -0,0 +1,134 @@ +/*============================================================================= + 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/config/warning_disable.hpp> +#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; +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/Jamfile b/src/boost/libs/spirit/example/qi/compiler_tutorial/Jamfile new file mode 100644 index 00000000..bda2fad4 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/Jamfile @@ -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 + ; +} + + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc1.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc1.cpp new file mode 100644 index 00000000..d070172f --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc1.cpp @@ -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; +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc2.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc2.cpp new file mode 100644 index 00000000..92e03f49 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc2.cpp @@ -0,0 +1,131 @@ +/*============================================================================= + 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; +} diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc3.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc3.cpp new file mode 100644 index 00000000..4f214e30 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc3.cpp @@ -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; +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc4.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc4.cpp new file mode 100644 index 00000000..645926f7 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc4.cpp @@ -0,0 +1,285 @@ +/*============================================================================= + 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/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 +{ + /////////////////////////////////////////////////////////////////////////// + // 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; +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc5.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc5.cpp new file mode 100644 index 00000000..8aa71342 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc5.cpp @@ -0,0 +1,339 @@ +/*============================================================================= + 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/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 +{ + /////////////////////////////////////////////////////////////////////////// + // 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; +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc6.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc6.cpp new file mode 100644 index 00000000..af362f3d --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc6.cpp @@ -0,0 +1,373 @@ +/*============================================================================= + 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/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 +{ + /////////////////////////////////////////////////////////////////////////// + // 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; +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/annotation.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/annotation.hpp new file mode 100644 index 00000000..37365283 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/annotation.hpp @@ -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 + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/ast.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/ast.hpp new file mode 100644 index 00000000..82fba496 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/ast.hpp @@ -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) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CALC7_AST_HPP) +#define BOOST_SPIRIT_CALC7_AST_HPP + +#include <boost/config/warning_disable.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 diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/compiler.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/compiler.cpp new file mode 100644 index 00000000..8aa23017 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/compiler.cpp @@ -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; + } +}} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/compiler.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/compiler.hpp new file mode 100644 index 00000000..179a315a --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/compiler.hpp @@ -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/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_function.hpp> +#include <boost/spirit/include/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 diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/error_handler.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/error_handler.hpp new file mode 100644 index 00000000..867e9ee7 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/error_handler.hpp @@ -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 + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/expression.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/expression.cpp new file mode 100644 index 00000000..32f44adc --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/expression.cpp @@ -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>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/expression.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/expression.hpp new file mode 100644 index 00000000..916e26e7 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/expression.hpp @@ -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 + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/expression_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/expression_def.hpp new file mode 100644 index 00000000..2770677c --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/expression_def.hpp @@ -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/spirit/include/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)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/main.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/main.cpp new file mode 100644 index 00000000..4e139eb2 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/main.cpp @@ -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; +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/statement.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/statement.cpp new file mode 100644 index 00000000..54bc9ac3 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/statement.cpp @@ -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>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/statement.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/statement.hpp new file mode 100644 index 00000000..12baf38f --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/statement.hpp @@ -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 + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/statement_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/statement_def.hpp new file mode 100644 index 00000000..408a2368 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/statement_def.hpp @@ -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)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/vm.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/vm.cpp new file mode 100644 index 00000000..c481f2b7 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/vm.cpp @@ -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; + } + } + } +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/vm.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/vm.hpp new file mode 100644 index 00000000..853aa9c7 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc7/vm.hpp @@ -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 + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/annotation.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/annotation.hpp new file mode 100644 index 00000000..8cd00973 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/annotation.hpp @@ -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 + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/ast.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/ast.hpp new file mode 100644 index 00000000..3e6e2d79 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/ast.hpp @@ -0,0 +1,172 @@ +/*============================================================================= + 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_AST_HPP) +#define BOOST_SPIRIT_CALC8_AST_HPP + +#include <boost/config/warning_disable.hpp> +#include <boost/variant/recursive_variant.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/fusion/include/io.hpp> +#include <boost/optional.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 unary; + struct expression; + + struct variable : tagged + { + variable(std::string const& name = "") : name(name) {} + std::string name; + }; + + typedef boost::variant< + nil + , bool + , unsigned int + , variable + , boost::recursive_wrapper<unary> + , boost::recursive_wrapper<expression> + > + operand; + + enum optoken + { + op_plus, + op_minus, + op_times, + op_divide, + op_positive, + op_negative, + op_not, + op_equal, + op_not_equal, + op_less, + op_less_equal, + op_greater, + op_greater_equal, + op_and, + op_or + }; + + struct unary + { + optoken operator_; + operand operand_; + }; + + struct operation + { + optoken operator_; + operand operand_; + }; + + struct expression + { + operand first; + std::list<operation> rest; + }; + + struct assignment + { + variable lhs; + expression rhs; + }; + + struct variable_declaration + { + assignment assign; + }; + + struct if_statement; + struct while_statement; + struct statement_list; + + typedef boost::variant< + variable_declaration + , assignment + , boost::recursive_wrapper<if_statement> + , boost::recursive_wrapper<while_statement> + , boost::recursive_wrapper<statement_list> + > + statement; + + struct statement_list : std::list<statement> {}; + + struct if_statement + { + expression condition; + statement then; + boost::optional<statement> else_; + }; + + struct while_statement + { + expression condition; + statement body; + }; + + // 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::unary, + (client::ast::optoken, operator_) + (client::ast::operand, operand_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::operation, + (client::ast::optoken, 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) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::if_statement, + (client::ast::expression, condition) + (client::ast::statement, then) + (boost::optional<client::ast::statement>, else_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::while_statement, + (client::ast::expression, condition) + (client::ast::statement, body) +) + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/compiler.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/compiler.cpp new file mode 100644 index 00000000..2d8b3eb1 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/compiler.cpp @@ -0,0 +1,382 @@ +/*============================================================================= + 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> +#include <boost/lexical_cast.hpp> +#include <set> + +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; + } + + std::map<std::size_t, std::string> lines; + std::set<std::size_t> jumps; + + while (pc != code.end()) + { + std::string line; + std::size_t address = pc - code.begin(); + + switch (*pc++) + { + case op_neg: + line += " op_neg"; + break; + + case op_not: + line += " op_not"; + break; + + case op_add: + line += " op_add"; + break; + + case op_sub: + line += " op_sub"; + break; + + case op_mul: + line += " op_mul"; + break; + + case op_div: + line += " op_div"; + break; + + case op_eq: + line += " op_eq"; + break; + + case op_neq: + line += " op_neq"; + break; + + case op_lt: + line += " op_lt"; + break; + + case op_lte: + line += " op_lte"; + break; + + case op_gt: + line += " op_gt"; + break; + + case op_gte: + line += " op_gte"; + break; + + case op_and: + line += " op_and"; + break; + + case op_or: + line += " op_or"; + break; + + case op_load: + line += " op_load "; + line += locals[*pc++]; + break; + + case op_store: + line += " op_store "; + line += locals[*pc++]; + break; + + case op_int: + line += " op_int "; + line += boost::lexical_cast<std::string>(*pc++); + break; + + case op_true: + line += " op_true"; + break; + + case op_false: + line += " op_false"; + break; + + case op_jump: + { + line += " op_jump "; + std::size_t pos = (pc - code.begin()) + *pc++; + if (pos == code.size()) + line += "end"; + else + line += boost::lexical_cast<std::string>(pos); + jumps.insert(pos); + } + break; + + case op_jump_if: + { + line += " op_jump_if "; + std::size_t pos = (pc - code.begin()) + *pc++; + if (pos == code.size()) + line += "end"; + else + line += boost::lexical_cast<std::string>(pos); + jumps.insert(pos); + } + break; + + case op_stk_adj: + line += " op_stk_adj "; + line += boost::lexical_cast<std::string>(*pc++); + break; + } + lines[address] = line; + } + + std::cout << "start:" << std::endl; + typedef std::pair<std::size_t, std::string> line_info; + BOOST_FOREACH(line_info const& l, lines) + { + std::size_t pos = l.first; + if (jumps.find(pos) != jumps.end()) + std::cout << pos << ':' << std::endl; + std::cout << l.second << std::endl; + } + + std::cout << "end:" << std::endl; + } + + bool compiler::operator()(unsigned int x) const + { + program.op(op_int, x); + return true; + } + + bool compiler::operator()(bool x) const + { + program.op(x ? op_true : op_false); + 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 ast::op_plus: program.op(op_add); break; + case ast::op_minus: program.op(op_sub); break; + case ast::op_times: program.op(op_mul); break; + case ast::op_divide: program.op(op_div); break; + + case ast::op_equal: program.op(op_eq); break; + case ast::op_not_equal: program.op(op_neq); break; + case ast::op_less: program.op(op_lt); break; + case ast::op_less_equal: program.op(op_lte); break; + case ast::op_greater: program.op(op_gt); break; + case ast::op_greater_equal: program.op(op_gte); break; + + case ast::op_and: program.op(op_and); break; + case ast::op_or: program.op(op_or); break; + default: BOOST_ASSERT(0); return false; + } + return true; + } + + bool compiler::operator()(ast::unary const& x) const + { + if (!boost::apply_visitor(*this, x.operand_)) + return false; + switch (x.operator_) + { + case ast::op_negative: program.op(op_neg); break; + case ast::op_not: program.op(op_not); break; + case ast::op_positive: 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 const& x) const + { + return boost::apply_visitor(*this, x); + } + + bool compiler::operator()(ast::statement_list const& x) const + { + BOOST_FOREACH(ast::statement const& s, x) + { + if (!(*this)(s)) + return false; + } + return true; + } + + bool compiler::operator()(ast::if_statement const& x) const + { + if (!(*this)(x.condition)) + return false; + program.op(op_jump_if, 0); // we shall fill this (0) in later + std::size_t skip = program.size()-1; // mark its position + if (!(*this)(x.then)) + return false; + program[skip] = program.size()-skip; // now we know where to jump to (after the if branch) + + if (x.else_) // We got an alse + { + program[skip] += 2; // adjust for the "else" jump + program.op(op_jump, 0); // we shall fill this (0) in later + std::size_t exit = program.size()-1; // mark its position + if (!(*this)(*x.else_)) + return false; + program[exit] = program.size()-exit; // now we know where to jump to (after the else branch) + } + + return true; + } + + bool compiler::operator()(ast::while_statement const& x) const + { + std::size_t loop = program.size(); // mark our position + if (!(*this)(x.condition)) + return false; + program.op(op_jump_if, 0); // we shall fill this (0) in later + std::size_t exit = program.size()-1; // mark its position + if (!(*this)(x.body)) + return false; + program.op(op_jump, + int(loop-1) - int(program.size())); // loop back + program[exit] = program.size()-exit; // now we know where to jump to (to exit the loop) + return true; + } + + bool compiler::start(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); + + if (!(*this)(x)) + { + program.clear(); + return false; + } + program[1] = program.nvars(); // now store the actual number of variables + return true; + } +}} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/compiler.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/compiler.hpp new file mode 100644 index 00000000..915a87d0 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/compiler.hpp @@ -0,0 +1,92 @@ +/*============================================================================= + 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_COMPILER_HPP) +#define BOOST_SPIRIT_CALC8_COMPILER_HPP + +#include "ast.hpp" +#include "error_handler.hpp" +#include <vector> +#include <map> +#include <boost/function.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_function.hpp> +#include <boost/spirit/include/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::size_t size() const { return code.size(); } + 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()(bool x) const; + bool operator()(ast::variable const& x) const; + bool operator()(ast::operation const& x) const; + bool operator()(ast::unary 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; + bool operator()(ast::statement const& x) const; + bool operator()(ast::if_statement const& x) const; + bool operator()(ast::while_statement const& x) const; + + bool start(ast::statement_list const& x) const; + + client::code_gen::program& program; + + boost::function< + void(int tag, std::string const& what)> + error_handler; + }; +}} + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/error_handler.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/error_handler.hpp new file mode 100644 index 00000000..0cc42ca9 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/error_handler.hpp @@ -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_CALC8_ERROR_HANDLER_HPP) +#define BOOST_SPIRIT_CALC8_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 + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/expression.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/expression.cpp new file mode 100644 index 00000000..32f44adc --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/expression.cpp @@ -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>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/expression.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/expression.hpp new file mode 100644 index 00000000..e5770bb1 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/expression.hpp @@ -0,0 +1,68 @@ +/*============================================================================= + 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_EXPRESSION_HPP) +#define BOOST_SPIRIT_CALC8_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, equality_expr, relational_expr, + logical_expr, additive_expr, multiplicative_expr + ; + + qi::rule<Iterator, ast::operand(), ascii::space_type> + unary_expr, primary_expr + ; + + qi::rule<Iterator, std::string(), ascii::space_type> + identifier + ; + + qi::symbols<char, ast::optoken> + equality_op, relational_op, logical_op, + additive_op, multiplicative_op, unary_op + ; + + qi::symbols<char> + keywords + ; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/expression_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/expression_def.hpp new file mode 100644 index 00000000..608992c3 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/expression_def.hpp @@ -0,0 +1,159 @@ +/*============================================================================= + 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/spirit/include/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; + qi::bool_type bool_; + + 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; + + /////////////////////////////////////////////////////////////////////// + // Tokens + logical_op.add + ("&&", ast::op_and) + ("||", ast::op_or) + ; + + equality_op.add + ("==", ast::op_equal) + ("!=", ast::op_not_equal) + ; + + relational_op.add + ("<", ast::op_less) + ("<=", ast::op_less_equal) + (">", ast::op_greater) + (">=", ast::op_greater_equal) + ; + + additive_op.add + ("+", ast::op_plus) + ("-", ast::op_minus) + ; + + multiplicative_op.add + ("*", ast::op_times) + ("/", ast::op_divide) + ; + + unary_op.add + ("+", ast::op_positive) + ("-", ast::op_negative) + ("!", ast::op_not) + ; + + keywords.add + ("var") + ("true") + ("false") + ("if") + ("else") + ("while") + ; + + /////////////////////////////////////////////////////////////////////// + // Main expression grammar + expr = + logical_expr.alias() + ; + + logical_expr = + equality_expr + >> *(logical_op > equality_expr) + ; + + equality_expr = + relational_expr + >> *(equality_op > relational_expr) + ; + + relational_expr = + additive_expr + >> *(relational_op > additive_expr) + ; + + additive_expr = + multiplicative_expr + >> *(additive_op > multiplicative_expr) + ; + + multiplicative_expr = + unary_expr + >> *(multiplicative_op > unary_expr) + ; + + unary_expr = + primary_expr + | (unary_op > primary_expr) + ; + + primary_expr = + uint_ + | identifier + | bool_ + | '(' > expr > ')' + ; + + identifier = + !keywords + >> raw[lexeme[(alpha | '_') >> *(alnum | '_')]] + ; + + /////////////////////////////////////////////////////////////////////// + // Debugging and error handling and reporting support. + BOOST_SPIRIT_DEBUG_NODES( + (expr) + (equality_expr) + (relational_expr) + (logical_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)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/main.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/main.cpp new file mode 100644 index 00000000..9c3b7346 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/main.cpp @@ -0,0 +1,97 @@ +/*============================================================================= + 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 boolean expressions and control structures. +// Is it obvious now what we are up to? ;-) +// +// [ 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.start(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.stack); + } + else + { + std::cout << "Compile failure\n"; + } + } + else + { + std::cout << "Parse failure\n"; + } + + std::cout << "-------------------------\n\n"; + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/statement.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/statement.cpp new file mode 100644 index 00000000..54bc9ac3 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/statement.cpp @@ -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>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/statement.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/statement.hpp new file mode 100644 index 00000000..8b6c6b77 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/statement.hpp @@ -0,0 +1,37 @@ +/*============================================================================= + 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_STATEMENT_HPP) +#define BOOST_SPIRIT_CALC8_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, compound_statement; + + qi::rule<Iterator, ast::statement(), ascii::space_type> statement_; + qi::rule<Iterator, ast::variable_declaration(), ascii::space_type> variable_declaration; + qi::rule<Iterator, ast::assignment(), ascii::space_type> assignment; + qi::rule<Iterator, ast::if_statement(), ascii::space_type> if_statement; + qi::rule<Iterator, ast::while_statement(), ascii::space_type> while_statement; + qi::rule<Iterator, std::string(), ascii::space_type> identifier; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/statement_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/statement_def.hpp new file mode 100644 index 00000000..d007ac06 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/statement_def.hpp @@ -0,0 +1,111 @@ +/*============================================================================= + 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; + qi::lit_type lit; + + 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 = + +statement_ + ; + + statement_ = + variable_declaration + | assignment + | compound_statement + | if_statement + | while_statement + ; + + identifier = + !expr.keywords + >> raw[lexeme[(alpha | '_') >> *(alnum | '_')]] + ; + + variable_declaration = + lexeme["var" >> !(alnum | '_')] // make sure we have whole words + > &identifier // expect an identifier + > assignment + ; + + assignment = + identifier + > '=' + > expr + > ';' + ; + + if_statement = + lit("if") + > '(' + > expr + > ')' + > statement_ + > + -( + lexeme["else" >> !(alnum | '_')] // make sure we have whole words + > statement_ + ) + ; + + while_statement = + lit("while") + > '(' + > expr + > ')' + > statement_ + ; + + compound_statement = + '{' >> -statement_list >> '}' + ; + + // 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)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/vm.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/vm.cpp new file mode 100644 index 00000000..70589fa1 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/vm.cpp @@ -0,0 +1,163 @@ +/*============================================================================= + 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" +#include <boost/assert.hpp> + +#if defined(_MSC_VER) +# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' + // (performance warning) +#endif + +namespace client +{ + int vmachine::execute( + std::vector<int> const& code + , std::vector<int>::const_iterator pc + , std::vector<int>::iterator frame_ptr + ) + { + std::vector<int>::iterator stack_ptr = frame_ptr; + + while (pc != code.end()) + { + BOOST_ASSERT(pc != code.end()); + + switch (*pc++) + { + case op_neg: + stack_ptr[-1] = -stack_ptr[-1]; + break; + + case op_not: + stack_ptr[-1] = !bool(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_eq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] == stack_ptr[0]); + break; + + case op_neq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] != stack_ptr[0]); + break; + + case op_lt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] < stack_ptr[0]); + break; + + case op_lte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] <= stack_ptr[0]); + break; + + case op_gt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] > stack_ptr[0]); + break; + + case op_gte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] >= stack_ptr[0]); + break; + + case op_and: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) && bool(stack_ptr[0]); + break; + + case op_or: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) || bool(stack_ptr[0]); + break; + + case op_load: + *stack_ptr++ = frame_ptr[*pc++]; + break; + + case op_store: + --stack_ptr; + frame_ptr[*pc++] = stack_ptr[0]; + break; + + case op_int: + *stack_ptr++ = *pc++; + break; + + case op_true: + *stack_ptr++ = true; + break; + + case op_false: + *stack_ptr++ = false; + break; + + case op_jump: + pc += *pc; + break; + + case op_jump_if: + if (!bool(stack_ptr[-1])) + pc += *pc; + else + ++pc; + --stack_ptr; + break; + + case op_stk_adj: + stack_ptr = stack.begin() + *pc++; + break; + + case op_call: + { + int nargs = *pc++; + int jump = *pc++; + + // a function call is a recursive call to execute + int r = execute( + code + , code.begin() + jump + , stack_ptr - nargs + ); + + // cleanup after return from function + stack_ptr[-nargs] = r; // get return value + stack_ptr -= (nargs - 1); // the stack will now contain + // the return value + } + break; + + case op_return: + return stack_ptr[-1]; + } + } + return -1; + } +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/vm.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/vm.hpp new file mode 100644 index 00000000..bab387f7 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/calc8/vm.hpp @@ -0,0 +1,77 @@ +/*============================================================================= + 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_VM_HPP) +#define BOOST_SPIRIT_CALC8_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_not, // boolean negate the top stack entry + op_eq, // compare the top two stack entries for == + op_neq, // compare the top two stack entries for != + op_lt, // compare the top two stack entries for < + op_lte, // compare the top two stack entries for <= + op_gt, // compare the top two stack entries for > + op_gte, // compare the top two stack entries for >= + + op_and, // logical and top two stack entries + op_or, // logical or top two stack entries + + op_load, // load a variable + op_store, // store a variable + + op_int, // push constant integer into the stack + op_true, // push constant 0 into the stack + op_false, // push constant 1 into the stack + + op_jump_if, // jump to a relative position in the code if top stack + // evaluates to false + op_jump, // jump to a relative position in the code + + op_stk_adj, // adjust the stack (for args and locals) + op_call, // function call + op_return // return from function + }; + + class vmachine + { + public: + + vmachine(unsigned stackSize = 4096) + : stack(stackSize) + { + } + + int execute( + std::vector<int> const& code // the program code + , std::vector<int>::const_iterator pc // program counter + , std::vector<int>::iterator frame_ptr // start of arguments and locals + ); + + int execute(std::vector<int> const& code) + { + return execute(code, code.begin(), stack.begin()); + }; + + std::vector<int> stack; + }; +} + +#endif + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/annotation.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/annotation.hpp new file mode 100644 index 00000000..2361a7f4 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/annotation.hpp @@ -0,0 +1,95 @@ +/*============================================================================= + 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_CONJURE_ANNOTATION_HPP) +#define BOOST_SPIRIT_CONJURE_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) {} + + void operator()(ast::function_call& x) const + { + x.function_name.id = id; + } + + void operator()(ast::identifier& x) const + { + x.id = id; + } + + template <typename T> + void operator()(T& x) const + { + // no-op + } + }; + + 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::variable_declaration& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.lhs.id = id; + } + + void operator()(ast::assignment& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.lhs.id = id; + } + + void operator()(ast::return_statement& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.id = id; + } + + void operator()(ast::identifier& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.id = id; + } + }; +} + +#endif + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/ast.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/ast.hpp new file mode 100644 index 00000000..e5022472 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/ast.hpp @@ -0,0 +1,275 @@ +/*============================================================================= + 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_CONJURE_AST_HPP) +#define BOOST_SPIRIT_CONJURE_AST_HPP + +#include <boost/config/warning_disable.hpp> +#include <boost/variant/recursive_variant.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/fusion/include/io.hpp> +#include <boost/optional.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 unary; + struct function_call; + struct expression; + + struct identifier : tagged + { + identifier(std::string const& name = "") : name(name) {} + std::string name; + }; + + typedef boost::variant< + nil + , bool + , unsigned int + , identifier + , boost::recursive_wrapper<unary> + , boost::recursive_wrapper<function_call> + , boost::recursive_wrapper<expression> + > + operand; + + enum optoken + { + // precedence 1 + op_comma, + + // precedence 2 + op_assign, + op_plus_assign, + op_minus_assign, + op_times_assign, + op_divide_assign, + op_mod_assign, + op_bit_and_assign, + op_bit_xor_assign, + op_bitor_assign, + op_shift_left_assign, + op_shift_right_assign, + + // precedence 3 + op_logical_or, + + // precedence 4 + op_logical_and, + + // precedence 5 + op_bit_or, + + // precedence 6 + op_bit_xor, + + // precedence 7 + op_bit_and, + + // precedence 8 + op_equal, + op_not_equal, + + // precedence 9 + op_less, + op_less_equal, + op_greater, + op_greater_equal, + + // precedence 10 + op_shift_left, + op_shift_right, + + // precedence 11 + op_plus, + op_minus, + + // precedence 12 + op_times, + op_divide, + op_mod, + + // precedence 13 + op_positive, + op_negative, + op_pre_incr, + op_pre_decr, + op_compl, + op_not, + + // precedence 14 + op_post_incr, + op_post_decr, + }; + + struct unary + { + optoken operator_; + operand operand_; + }; + + struct operation + { + optoken operator_; + operand operand_; + }; + + struct function_call + { + identifier function_name; + std::list<expression> args; + }; + + struct expression + { + operand first; + std::list<operation> rest; + }; + + struct assignment + { + identifier lhs; + expression rhs; + }; + + struct variable_declaration + { + identifier lhs; + boost::optional<expression> rhs; + }; + + struct if_statement; + struct while_statement; + struct statement_list; + struct return_statement; + + typedef boost::variant< + variable_declaration + , assignment + , boost::recursive_wrapper<if_statement> + , boost::recursive_wrapper<while_statement> + , boost::recursive_wrapper<return_statement> + , boost::recursive_wrapper<statement_list> + > + statement; + + struct statement_list : std::list<statement> {}; + + struct if_statement + { + expression condition; + statement then; + boost::optional<statement> else_; + }; + + struct while_statement + { + expression condition; + statement body; + }; + + struct return_statement : tagged + { + boost::optional<expression> expr; + }; + + struct function + { + std::string return_type; + identifier function_name; + std::list<identifier> args; + statement_list body; + }; + + typedef std::list<function> function_list; + + // print functions for debugging + inline std::ostream& operator<<(std::ostream& out, nil) + { + out << "nil"; return out; + } + + inline std::ostream& operator<<(std::ostream& out, identifier const& id) + { + out << id.name; return out; + } +}} + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::unary, + (client::ast::optoken, operator_) + (client::ast::operand, operand_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::operation, + (client::ast::optoken, operator_) + (client::ast::operand, operand_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::function_call, + (client::ast::identifier, function_name) + (std::list<client::ast::expression>, args) +) + +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::identifier, lhs) + (boost::optional<client::ast::expression>, rhs) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::assignment, + (client::ast::identifier, lhs) + (client::ast::expression, rhs) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::if_statement, + (client::ast::expression, condition) + (client::ast::statement, then) + (boost::optional<client::ast::statement>, else_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::while_statement, + (client::ast::expression, condition) + (client::ast::statement, body) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::return_statement, + (boost::optional<client::ast::expression>, expr) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::function, + (std::string, return_type) + (client::ast::identifier, function_name) + (std::list<client::ast::identifier>, args) + (client::ast::statement_list, body) +) + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/compiler.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/compiler.cpp new file mode 100644 index 00000000..22a036d4 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/compiler.cpp @@ -0,0 +1,628 @@ +/*============================================================================= + 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> +#include <boost/lexical_cast.hpp> +#include <set> + +namespace client { namespace code_gen +{ + void function::op(int a) + { + code.push_back(a); + size_ += 1; + } + + void function::op(int a, int b) + { + code.push_back(a); + code.push_back(b); + size_ += 2; + } + + void function::op(int a, int b, int c) + { + code.push_back(a); + code.push_back(b); + code.push_back(c); + size_ += 3; + } + + int const* function::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 function::add_var(std::string const& name) + { + std::size_t n = variables.size(); + variables[name] = n; + } + + void function::link_to(std::string const& name, std::size_t address) + { + function_calls[address] = name; + } + + void function::print_assembler() const + { + std::vector<int>::const_iterator pc = code.begin() + address; + + 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; + } + + std::map<std::size_t, std::string> lines; + std::set<std::size_t> jumps; + + while (pc != (code.begin() + address + size_)) + { + std::string line; + std::size_t address = pc - code.begin(); + + switch (*pc++) + { + case op_neg: + line += " op_neg"; + break; + + case op_not: + line += " op_not"; + break; + + case op_add: + line += " op_add"; + break; + + case op_sub: + line += " op_sub"; + break; + + case op_mul: + line += " op_mul"; + break; + + case op_div: + line += " op_div"; + break; + + case op_eq: + line += " op_eq"; + break; + + case op_neq: + line += " op_neq"; + break; + + case op_lt: + line += " op_lt"; + break; + + case op_lte: + line += " op_lte"; + break; + + case op_gt: + line += " op_gt"; + break; + + case op_gte: + line += " op_gte"; + break; + + case op_and: + line += " op_and"; + break; + + case op_or: + line += " op_or"; + break; + + case op_load: + line += " op_load "; + line += locals[*pc++]; + break; + + case op_store: + line += " op_store "; + line += locals[*pc++]; + break; + + case op_int: + line += " op_int "; + line += boost::lexical_cast<std::string>(*pc++); + break; + + case op_true: + line += " op_true"; + break; + + case op_false: + line += " op_false"; + break; + + case op_jump: + { + line += " op_jump "; + std::size_t pos = (pc - code.begin()) + *pc++; + if (pos == code.size()) + line += "end"; + else + line += boost::lexical_cast<std::string>(pos); + jumps.insert(pos); + } + break; + + case op_jump_if: + { + line += " op_jump_if "; + std::size_t pos = (pc - code.begin()) + *pc++; + if (pos == code.size()) + line += "end"; + else + line += boost::lexical_cast<std::string>(pos); + jumps.insert(pos); + } + break; + + case op_call: + { + line += " op_call "; + int nargs = *pc++; + std::size_t jump = *pc++; + line += boost::lexical_cast<std::string>(nargs) + ", "; + BOOST_ASSERT(function_calls.find(jump) != function_calls.end()); + line += function_calls.find(jump)->second; + } + break; + + case op_stk_adj: + line += " op_stk_adj "; + line += boost::lexical_cast<std::string>(*pc++); + break; + + + case op_return: + line += " op_return"; + break; + } + lines[address] = line; + } + + std::cout << "start:" << std::endl; + typedef std::pair<std::size_t, std::string> line_info; + BOOST_FOREACH(line_info const& l, lines) + { + std::size_t pos = l.first; + if (jumps.find(pos) != jumps.end()) + std::cout << pos << ':' << std::endl; + std::cout << l.second << std::endl; + } + + std::cout << "end:" << std::endl << std::endl; + } + + bool compiler::operator()(unsigned int x) + { + BOOST_ASSERT(current != 0); + current->op(op_int, x); + return true; + } + + bool compiler::operator()(bool x) + { + BOOST_ASSERT(current != 0); + current->op(x ? op_true : op_false); + return true; + } + + bool compiler::operator()(ast::identifier const& x) + { + BOOST_ASSERT(current != 0); + int const* p = current->find_var(x.name); + if (p == 0) + { + error_handler(x.id, "Undeclared variable: " + x.name); + return false; + } + current->op(op_load, *p); + return true; + } + + bool compiler::operator()(ast::optoken const& x) + { + BOOST_ASSERT(current != 0); + switch (x) + { + case ast::op_plus: current->op(op_add); break; + case ast::op_minus: current->op(op_sub); break; + case ast::op_times: current->op(op_mul); break; + case ast::op_divide: current->op(op_div); break; + + case ast::op_equal: current->op(op_eq); break; + case ast::op_not_equal: current->op(op_neq); break; + case ast::op_less: current->op(op_lt); break; + case ast::op_less_equal: current->op(op_lte); break; + case ast::op_greater: current->op(op_gt); break; + case ast::op_greater_equal: current->op(op_gte); break; + + case ast::op_logical_or: current->op(op_or); break; + case ast::op_logical_and: current->op(op_and); break; + default: BOOST_ASSERT(0); return false; + } + return true; + } + + bool compiler::operator()(ast::unary const& x) + { + BOOST_ASSERT(current != 0); + if (!boost::apply_visitor(*this, x.operand_)) + return false; + switch (x.operator_) + { + case ast::op_negative: current->op(op_neg); break; + case ast::op_not: current->op(op_not); break; + case ast::op_positive: break; + default: BOOST_ASSERT(0); return false; + } + return true; + } + + bool compiler::operator()(ast::function_call const& x) + { + BOOST_ASSERT(current != 0); + + if (functions.find(x.function_name.name) == functions.end()) + { + error_handler(x.function_name.id, "Function not found: " + x.function_name.name); + return false; + } + + boost::shared_ptr<code_gen::function> p = functions[x.function_name.name]; + + if (p->nargs() != x.args.size()) + { + error_handler(x.function_name.id, "Wrong number of arguments: " + x.function_name.name); + return false; + } + + BOOST_FOREACH(ast::expression const& expr, x.args) + { + if (!(*this)(expr)) + return false; + } + + current->op( + op_call, + p->nargs(), + p->get_address()); + current->link_to(x.function_name.name, p->get_address()); + + return true; + } + + namespace + { + int precedence[] = { + // precedence 1 + 1, // op_comma + + // precedence 2 + 2, // op_assign + 2, // op_plus_assign + 2, // op_minus_assign + 2, // op_times_assign + 2, // op_divide_assign + 2, // op_mod_assign + 2, // op_bit_and_assign + 2, // op_bit_xor_assign + 2, // op_bitor_assign + 2, // op_shift_left_assign + 2, // op_shift_right_assign + + // precedence 3 + 3, // op_logical_or + + // precedence 4 + 4, // op_logical_and + + // precedence 5 + 5, // op_bit_or + + // precedence 6 + 6, // op_bit_xor + + // precedence 7 + 7, // op_bit_and + + // precedence 8 + 8, // op_equal + 8, // op_not_equal + + // precedence 9 + 9, // op_less + 9, // op_less_equal + 9, // op_greater + 9, // op_greater_equal + + // precedence 10 + 10, // op_shift_left + 10, // op_shift_right + + // precedence 11 + 11, // op_plus + 11, // op_minus + + // precedence 12 + 12, // op_times + 12, // op_divide + 12, // op_mod + + // precedence 13 + 13, // op_positive + 13, // op_negative + 13, // op_pre_incr + 13, // op_pre_decr + 13, // op_compl + 13, // op_not + + // precedence 14 + 14, // op_post_incr + 14 // op_post_decr + }; + } + + // The Shunting-yard algorithm + bool compiler::compile_expression( + int min_precedence, + std::list<ast::operation>::const_iterator& rbegin, + std::list<ast::operation>::const_iterator rend) + { + while ((rbegin != rend) && (precedence[rbegin->operator_] >= min_precedence)) + { + ast::optoken op = rbegin->operator_; + if (!boost::apply_visitor(*this, rbegin->operand_)) + return false; + ++rbegin; + + while ((rbegin != rend) && (precedence[rbegin->operator_] > precedence[op])) + { + ast::optoken next_op = rbegin->operator_; + compile_expression(precedence[next_op], rbegin, rend); + } + (*this)(op); + } + return true; + } + + bool compiler::operator()(ast::expression const& x) + { + BOOST_ASSERT(current != 0); + if (!boost::apply_visitor(*this, x.first)) + return false; + std::list<ast::operation>::const_iterator rbegin = x.rest.begin(); + if (!compile_expression(0, rbegin, x.rest.end())) + return false; + return true; + } + + bool compiler::operator()(ast::assignment const& x) + { + BOOST_ASSERT(current != 0); + if (!(*this)(x.rhs)) + return false; + int const* p = current->find_var(x.lhs.name); + if (p == 0) + { + error_handler(x.lhs.id, "Undeclared variable: " + x.lhs.name); + return false; + } + current->op(op_store, *p); + return true; + } + + bool compiler::operator()(ast::variable_declaration const& x) + { + BOOST_ASSERT(current != 0); + int const* p = current->find_var(x.lhs.name); + if (p != 0) + { + error_handler(x.lhs.id, "Duplicate variable: " + x.lhs.name); + return false; + } + if (x.rhs) // if there's an RHS initializer + { + bool r = (*this)(*x.rhs); + if (r) // don't add the variable if the RHS fails + { + current->add_var(x.lhs.name); + current->op(op_store, *current->find_var(x.lhs.name)); + } + return r; + } + else + { + current->add_var(x.lhs.name); + } + return true; + } + + bool compiler::operator()(ast::statement const& x) + { + BOOST_ASSERT(current != 0); + return boost::apply_visitor(*this, x); + } + + bool compiler::operator()(ast::statement_list const& x) + { + BOOST_ASSERT(current != 0); + BOOST_FOREACH(ast::statement const& s, x) + { + if (!(*this)(s)) + return false; + } + return true; + } + + bool compiler::operator()(ast::if_statement const& x) + { + BOOST_ASSERT(current != 0); + if (!(*this)(x.condition)) + return false; + current->op(op_jump_if, 0); // we shall fill this (0) in later + std::size_t skip = current->size()-1; // mark its position + if (!(*this)(x.then)) + return false; + (*current)[skip] = current->size()-skip; // now we know where to jump to (after the if branch) + + if (x.else_) // We got an alse + { + (*current)[skip] += 2; // adjust for the "else" jump + current->op(op_jump, 0); // we shall fill this (0) in later + std::size_t exit = current->size()-1; // mark its position + if (!(*this)(*x.else_)) + return false; + (*current)[exit] = current->size()-exit;// now we know where to jump to (after the else branch) + } + + return true; + } + + bool compiler::operator()(ast::while_statement const& x) + { + BOOST_ASSERT(current != 0); + std::size_t loop = current->size(); // mark our position + if (!(*this)(x.condition)) + return false; + current->op(op_jump_if, 0); // we shall fill this (0) in later + std::size_t exit = current->size()-1; // mark its position + if (!(*this)(x.body)) + return false; + current->op(op_jump, + int(loop-1) - int(current->size())); // loop back + (*current)[exit] = current->size()-exit; // now we know where to jump to (to exit the loop) + return true; + } + + bool compiler::operator()(ast::return_statement const& x) + { + if (void_return) + { + if (x.expr) + { + error_handler(x.id, "'void' function returning a value: "); + return false; + } + } + else + { + if (!x.expr) + { + error_handler(x.id, current_function_name + " function must return a value: "); + return false; + } + } + + if (x.expr) + { + if (!(*this)(*x.expr)) + return false; + } + current->op(op_return); + return true; + } + + bool compiler::operator()(ast::function const& x) + { + void_return = x.return_type == "void"; + if (functions.find(x.function_name.name) != functions.end()) + { + error_handler(x.function_name.id, "Duplicate function: " + x.function_name.name); + return false; + } + boost::shared_ptr<code_gen::function>& p = functions[x.function_name.name]; + p.reset(new code_gen::function(code, x.args.size())); + current = p.get(); + current_function_name = x.function_name.name; + + // op_stk_adj 0 for now. we'll know how many variables + // we'll have later and add them + current->op(op_stk_adj, 0); + BOOST_FOREACH(ast::identifier const& arg, x.args) + { + current->add_var(arg.name); + } + + if (!(*this)(x.body)) + return false; + (*current)[1] = current->nvars(); // now store the actual number of variables + // this includes the arguments + return true; + } + + bool compiler::operator()(ast::function_list const& x) + { + // Jump to the main function + code.push_back(op_jump); + code.push_back(0); // we will fill this in later when we finish compiling + // and we know where the main function is + + BOOST_FOREACH(ast::function const& f, x) + { + if (!(*this)(f)) + { + code.clear(); + return false; + } + } + // find the main function + boost::shared_ptr<code_gen::function> p = + find_function("main"); + + if (!p) // main function not found + { + std::cerr << "Error: main function not defined" << std::endl; + return false; + } + code[1] = p->get_address()-1; // jump to this (main function) address + + return true; + } + + void compiler::print_assembler() const + { + typedef std::pair<std::string, boost::shared_ptr<code_gen::function> > pair; + BOOST_FOREACH(pair const& p, functions) + { + std::cout << ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" << std::endl; + std::cout << p.second->get_address() << ": function " << p.first << std::endl; + p.second->print_assembler(); + } + } + + boost::shared_ptr<code_gen::function> + compiler::find_function(std::string const& name) const + { + function_table::const_iterator i = functions.find(name); + if (i == functions.end()) + return boost::shared_ptr<code_gen::function>(); + else + return i->second; + } +}} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/compiler.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/compiler.hpp new file mode 100644 index 00000000..461f7a7c --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/compiler.hpp @@ -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) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_COMPILER_HPP) +#define BOOST_SPIRIT_CONJURE_COMPILER_HPP + +#include "ast.hpp" +#include "error_handler.hpp" +#include <vector> +#include <map> +#include <boost/function.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_function.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> + +namespace client { namespace code_gen +{ + /////////////////////////////////////////////////////////////////////////// + // The Function + /////////////////////////////////////////////////////////////////////////// + struct function + { + function(std::vector<int>& code, int nargs) + : code(code), address(code.size()), size_(0), nargs_(nargs) {} + + 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[address+i]; } + int const& operator[](std::size_t i) const { return code[address+i]; } + std::size_t size() const { return size_; } + std::size_t get_address() const { return address; } + + int nargs() const { return nargs_; } + int nvars() const { return variables.size(); } + int const* find_var(std::string const& name) const; + void add_var(std::string const& name); + void link_to(std::string const& name, std::size_t address); + + void print_assembler() const; + + private: + + std::map<std::string, int> variables; + std::map<std::size_t, std::string> function_calls; + std::vector<int>& code; + std::size_t address; + std::size_t size_; + std::size_t nargs_; + }; + + /////////////////////////////////////////////////////////////////////////// + // The Compiler + /////////////////////////////////////////////////////////////////////////// + struct compiler + { + typedef bool result_type; + + template <typename ErrorHandler> + compiler(ErrorHandler& error_handler_) + : current(0) + { + 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) { BOOST_ASSERT(0); return false; } + bool operator()(unsigned int x); + bool operator()(bool x); + bool operator()(ast::identifier const& x); + bool operator()(ast::optoken const& x); + bool operator()(ast::unary const& x); + bool operator()(ast::function_call const& x); + bool operator()(ast::expression const& x); + bool operator()(ast::assignment const& x); + bool operator()(ast::variable_declaration const& x); + bool operator()(ast::statement_list const& x); + bool operator()(ast::statement const& x); + bool operator()(ast::if_statement const& x); + bool operator()(ast::while_statement const& x); + bool operator()(ast::return_statement const& x); + bool operator()(ast::function const& x); + bool operator()(ast::function_list const& x); + + void print_assembler() const; + + boost::shared_ptr<code_gen::function> + find_function(std::string const& name) const; + + std::vector<int>& get_code() { return code; } + std::vector<int> const& get_code() const { return code; } + + private: + + bool compile_expression( + int min_precedence, + std::list<ast::operation>::const_iterator& rbegin, + std::list<ast::operation>::const_iterator rend); + + typedef std::map<std::string, boost::shared_ptr<code_gen::function> > function_table; + + std::vector<int> code; + code_gen::function* current; + std::string current_function_name; + function_table functions; + bool void_return; + + boost::function< + void(int tag, std::string const& what)> + error_handler; + }; +}} + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/error_handler.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/error_handler.hpp new file mode 100644 index 00000000..2c1aa44e --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/error_handler.hpp @@ -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_CONJURE_ERROR_HANDLER_HPP) +#define BOOST_SPIRIT_CONJURE_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 + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/expression.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/expression.cpp new file mode 100644 index 00000000..32f44adc --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/expression.cpp @@ -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>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/expression.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/expression.hpp new file mode 100644 index 00000000..985b972c --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/expression.hpp @@ -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) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_EXPRESSION_HPP) +#define BOOST_SPIRIT_CONJURE_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 "skipper.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(), skipper<Iterator> > + { + expression(error_handler<Iterator>& error_handler); + + qi::rule<Iterator, ast::expression(), skipper<Iterator> > + expr + ; + + qi::rule<Iterator, ast::operand(), skipper<Iterator> > + unary_expr, primary_expr + ; + + qi::rule<Iterator, ast::function_call(), skipper<Iterator> > + function_call + ; + + qi::rule<Iterator, std::list<ast::expression>(), skipper<Iterator> > + argument_list + ; + + qi::rule<Iterator, std::string(), skipper<Iterator> > + identifier + ; + + qi::symbols<char, ast::optoken> + unary_op, binary_op + ; + + qi::symbols<char> + keywords + ; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/expression_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/expression_def.hpp new file mode 100644 index 00000000..21ccc9bd --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/expression_def.hpp @@ -0,0 +1,131 @@ +/*============================================================================= + 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/spirit/include/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; + qi::bool_type bool_; + + 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; + + /////////////////////////////////////////////////////////////////////// + // Tokens + binary_op.add + ("||", ast::op_logical_or) + ("&&", ast::op_logical_and) + ("==", ast::op_equal) + ("!=", ast::op_not_equal) + ("<", ast::op_less) + ("<=", ast::op_less_equal) + (">", ast::op_greater) + (">=", ast::op_greater_equal) + ("+", ast::op_plus) + ("-", ast::op_minus) + ("*", ast::op_times) + ("/", ast::op_divide) + ; + + unary_op.add + ("+", ast::op_positive) + ("-", ast::op_negative) + ("!", ast::op_not) + ; + + keywords.add + ("true") + ("false") + ("if") + ("else") + ("while") + ("int") + ("void") + ("return") + ; + + /////////////////////////////////////////////////////////////////////// + // Main expression grammar + expr = + unary_expr + >> *(binary_op > unary_expr) + ; + + unary_expr = + primary_expr + | (unary_op > unary_expr) + ; + + primary_expr = + uint_ + | function_call + | identifier + | bool_ + | '(' > expr > ')' + ; + + function_call = + (identifier >> '(') + > argument_list + > ')' + ; + + argument_list = -(expr % ','); + + identifier = + !lexeme[keywords >> !(alnum | '_')] + >> raw[lexeme[(alpha | '_') >> *(alnum | '_')]] + ; + + /////////////////////////////////////////////////////////////////////// + // Debugging and error handling and reporting support. + BOOST_SPIRIT_DEBUG_NODES( + (expr) + (unary_expr) + (primary_expr) + (function_call) + (argument_list) + (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)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/function.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/function.cpp new file mode 100644 index 00000000..ababd4ad --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/function.cpp @@ -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 "function_def.hpp" + +typedef std::string::const_iterator iterator_type; +template struct client::parser::function<iterator_type>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/function.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/function.hpp new file mode 100644 index 00000000..9eddd66c --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/function.hpp @@ -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_CONJURE_FUNCTION_HPP) +#define BOOST_SPIRIT_CONJURE_FUNCTION_HPP + +#include "statement.hpp" + +namespace client { namespace parser +{ + /////////////////////////////////////////////////////////////////////////////// + // The function grammar + /////////////////////////////////////////////////////////////////////////////// + template <typename Iterator> + struct function : qi::grammar<Iterator, ast::function(), skipper<Iterator> > + { + function(error_handler<Iterator>& error_handler); + + statement<Iterator> body; + qi::rule<Iterator, std::string(), skipper<Iterator> > name; + qi::rule<Iterator, ast::identifier(), skipper<Iterator> > identifier; + qi::rule<Iterator, std::list<ast::identifier>(), skipper<Iterator> > argument_list; + qi::rule<Iterator, ast::function(), skipper<Iterator> > start; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/function_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/function_def.hpp new file mode 100644 index 00000000..bd2c7d02 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/function_def.hpp @@ -0,0 +1,71 @@ +/*============================================================================= + 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 "function.hpp" +#include "error_handler.hpp" +#include "annotation.hpp" + +namespace client { namespace parser +{ + template <typename Iterator> + function<Iterator>::function(error_handler<Iterator>& error_handler) + : function::base_type(start), body(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; + qi::string_type string; + + 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; + + name = + !body.expr.keywords + >> raw[lexeme[(alpha | '_') >> *(alnum | '_')]] + ; + + identifier = name; + argument_list = -(identifier % ','); + + start = + lexeme[(string("void") | string("int")) + >> !(alnum | '_')] // make sure we have whole words + > identifier + > '(' > argument_list > ')' + > '{' > body > '}' + ; + + // Debugging and error handling and reporting support. + BOOST_SPIRIT_DEBUG_NODES( + (identifier) + (argument_list) + (start) + ); + + // Error handling: on error in start, call error_handler. + on_error<fail>(start, + error_handler_function(error_handler)( + "Error! Expecting ", _4, _3)); + + // Annotation: on success in start, call annotation. + on_success(identifier, + annotation_function(error_handler.iters)(_val, _1)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/main.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/main.cpp new file mode 100644 index 00000000..8b2a8007 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/main.cpp @@ -0,0 +1,121 @@ +/*============================================================================= + 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) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Not a calculator anymore, right? :-) +// +// [ JDG April 10, 2007 ] spirit2 +// [ JDG February 18, 2011 ] Pure attributes. No semantic actions. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "function.hpp" +#include "skipper.hpp" +#include "vm.hpp" +#include "compiler.hpp" +#include <boost/lexical_cast.hpp> +#include <fstream> + +/////////////////////////////////////////////////////////////////////////////// +// 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 source_code; // 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(source_code)); + + typedef std::string::const_iterator iterator_type; + iterator_type iter = source_code.begin(); + iterator_type end = source_code.end(); + + client::vmachine vm; // Our virtual machine + client::ast::function_list ast; // Our AST + + client::error_handler<iterator_type> + error_handler(iter, end); // Our error handler + client::parser::function<iterator_type> + function(error_handler); // Our parser + client::parser::skipper<iterator_type> + skipper; // Our skipper + client::code_gen::compiler + compiler(error_handler); // Our compiler + + + bool success = phrase_parse(iter, end, +function, skipper, ast); + + std::cout << "-------------------------\n"; + + if (success && iter == end) + { + if (compiler(ast)) + { + boost::shared_ptr<client::code_gen::function> + p = compiler.find_function("main"); + if (!p) + return 1; + + int nargs = argc-2; + if (p->nargs() != nargs) + { + std::cerr << "Error: main function requires " << p->nargs() << " arguments." << std::endl; + std::cerr << nargs << " supplied." << std::endl; + return 1; + } + + std::cout << "Success\n"; + std::cout << "-------------------------\n"; + std::cout << "Assembler----------------\n\n"; + compiler.print_assembler(); + + // Push the arguments into our stack + for (int i = 0; i < nargs; ++i) + vm.get_stack()[i] = boost::lexical_cast<int>(argv[i+2]); + + // Call the interpreter + int r = vm.execute(compiler.get_code()); + + std::cout << "-------------------------\n"; + std::cout << "Result: " << r << std::endl; + std::cout << "-------------------------\n\n"; + } + else + { + std::cout << "Compile failure\n"; + } + } + else + { + std::cout << "Parse failure\n"; + } + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/skipper.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/skipper.hpp new file mode 100644 index 00000000..bb1acc21 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/skipper.hpp @@ -0,0 +1,40 @@ +/*============================================================================= + 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_CONJURE_SKIPPER_HPP) +#define BOOST_SPIRIT_CONJURE_SKIPPER_HPP + +#include <boost/spirit/include/qi.hpp> + +namespace client { namespace parser +{ + namespace qi = boost::spirit::qi; + namespace ascii = boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////////////// + // The skipper grammar + /////////////////////////////////////////////////////////////////////////////// + template <typename Iterator> + struct skipper : qi::grammar<Iterator> + { + skipper() : skipper::base_type(start) + { + qi::char_type char_; + ascii::space_type space; + + start = + space // tab/space/cr/lf + | "/*" >> *(char_ - "*/") >> "*/" // C-style comments + ; + } + + qi::rule<Iterator> start; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/statement.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/statement.cpp new file mode 100644 index 00000000..54bc9ac3 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/statement.cpp @@ -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>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/statement.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/statement.hpp new file mode 100644 index 00000000..72485cbf --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/statement.hpp @@ -0,0 +1,38 @@ +/*============================================================================= + 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_CONJURE_STATEMENT_HPP) +#define BOOST_SPIRIT_CONJURE_STATEMENT_HPP + +#include "expression.hpp" + +namespace client { namespace parser +{ + /////////////////////////////////////////////////////////////////////////////// + // The statement grammar + /////////////////////////////////////////////////////////////////////////////// + template <typename Iterator> + struct statement : qi::grammar<Iterator, ast::statement_list(), skipper<Iterator> > + { + statement(error_handler<Iterator>& error_handler); + + expression<Iterator> expr; + qi::rule<Iterator, ast::statement_list(), skipper<Iterator> > + statement_list, compound_statement; + + qi::rule<Iterator, ast::statement(), skipper<Iterator> > statement_; + qi::rule<Iterator, ast::variable_declaration(), skipper<Iterator> > variable_declaration; + qi::rule<Iterator, ast::assignment(), skipper<Iterator> > assignment; + qi::rule<Iterator, ast::if_statement(), skipper<Iterator> > if_statement; + qi::rule<Iterator, ast::while_statement(), skipper<Iterator> > while_statement; + qi::rule<Iterator, ast::return_statement(), skipper<Iterator> > return_statement; + qi::rule<Iterator, std::string(), skipper<Iterator> > identifier; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/statement_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/statement_def.hpp new file mode 100644 index 00000000..29e40942 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/statement_def.hpp @@ -0,0 +1,128 @@ +/*============================================================================= + 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; + qi::lit_type lit; + + 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 = + +statement_ + ; + + statement_ = + variable_declaration + | assignment + | compound_statement + | if_statement + | while_statement + | return_statement + ; + + identifier = + !expr.keywords + >> raw[lexeme[(alpha | '_') >> *(alnum | '_')]] + ; + + variable_declaration = + lexeme["int" >> !(alnum | '_')] // make sure we have whole words + > identifier + > -('=' > expr) + > ';' + ; + + assignment = + identifier + > '=' + > expr + > ';' + ; + + if_statement = + lit("if") + > '(' + > expr + > ')' + > statement_ + > + -( + lexeme["else" >> !(alnum | '_')] // make sure we have whole words + > statement_ + ) + ; + + while_statement = + lit("while") + > '(' + > expr + > ')' + > statement_ + ; + + compound_statement = + '{' >> -statement_list >> '}' + ; + + return_statement = + lexeme["return" >> !(alnum | '_')] // make sure we have whole words + > -expr + > ';' + ; + + // Debugging and error handling and reporting support. + BOOST_SPIRIT_DEBUG_NODES( + (statement_list) + (statement_) + (variable_declaration) + (assignment) + (if_statement) + (while_statement) + (compound_statement) + (return_statement) + ); + + // 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 variable_declaration, + // assignment and return_statement, call annotation. + on_success(variable_declaration, + annotation_function(error_handler.iters)(_val, _1)); + on_success(assignment, + annotation_function(error_handler.iters)(_val, _1)); + on_success(return_statement, + annotation_function(error_handler.iters)(_val, _1)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/vm.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/vm.cpp new file mode 100644 index 00000000..8740bf9c --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/vm.cpp @@ -0,0 +1,159 @@ +/*============================================================================= + 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" + +#if defined(_MSC_VER) +# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' + // (performance warning) +#endif + +namespace client +{ + int vmachine::execute( + std::vector<int> const& code + , std::vector<int>::const_iterator pc + , std::vector<int>::iterator frame_ptr + ) + { + std::vector<int>::iterator stack_ptr = frame_ptr; + + while (true) + { + switch (*pc++) + { + case op_neg: + stack_ptr[-1] = -stack_ptr[-1]; + break; + + case op_not: + stack_ptr[-1] = !bool(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_eq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] == stack_ptr[0]); + break; + + case op_neq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] != stack_ptr[0]); + break; + + case op_lt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] < stack_ptr[0]); + break; + + case op_lte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] <= stack_ptr[0]); + break; + + case op_gt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] > stack_ptr[0]); + break; + + case op_gte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] >= stack_ptr[0]); + break; + + case op_and: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) && bool(stack_ptr[0]); + break; + + case op_or: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) || bool(stack_ptr[0]); + break; + + case op_load: + *stack_ptr++ = frame_ptr[*pc++]; + break; + + case op_store: + --stack_ptr; + frame_ptr[*pc++] = stack_ptr[0]; + break; + + case op_int: + *stack_ptr++ = *pc++; + break; + + case op_true: + *stack_ptr++ = true; + break; + + case op_false: + *stack_ptr++ = false; + break; + + case op_jump: + pc += *pc; + break; + + case op_jump_if: + if (!bool(stack_ptr[-1])) + pc += *pc; + else + ++pc; + --stack_ptr; + break; + + case op_stk_adj: + stack_ptr += *pc++; + break; + + case op_call: + { + int nargs = *pc++; + int jump = *pc++; + + // a function call is a recursive call to execute + int r = execute( + code + , code.begin() + jump + , stack_ptr - nargs + ); + + // cleanup after return from function + stack_ptr[-nargs] = r; // get return value + stack_ptr -= (nargs - 1); // the stack will now contain + // the return value + } + break; + + case op_return: + return stack_ptr[-1]; + } + } + } +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/vm.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/vm.hpp new file mode 100644 index 00000000..0362eaf8 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure1/vm.hpp @@ -0,0 +1,82 @@ +/*============================================================================= + 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_CONJURE_VM_HPP) +#define BOOST_SPIRIT_CONJURE_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_not, // boolean negate the top stack entry + op_eq, // compare the top two stack entries for == + op_neq, // compare the top two stack entries for != + op_lt, // compare the top two stack entries for < + op_lte, // compare the top two stack entries for <= + op_gt, // compare the top two stack entries for > + op_gte, // compare the top two stack entries for >= + + op_and, // logical and top two stack entries + op_or, // logical or top two stack entries + + op_load, // load a variable + op_store, // store a variable + + op_int, // push constant integer into the stack + op_true, // push constant 0 into the stack + op_false, // push constant 1 into the stack + + op_jump_if, // jump to a relative position in the code if top stack + // evaluates to false + op_jump, // jump to a relative position in the code + + op_stk_adj, // adjust the stack (for args and locals) + op_call, // function call + op_return // return from function + }; + + class vmachine + { + public: + + vmachine(unsigned stackSize = 4096) + : stack(stackSize) + { + } + + int execute(std::vector<int> const& code) + { + return execute(code, code.begin(), stack.begin()); + } + + std::vector<int> const& get_stack() const { return stack; }; + std::vector<int>& get_stack() { return stack; }; + + private: + + int execute( + std::vector<int> const& code // the program code + , std::vector<int>::const_iterator pc // program counter + , std::vector<int>::iterator frame_ptr // start of arguments and locals + ); + + std::vector<int> stack; + }; +} + +#endif + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/annotation.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/annotation.hpp new file mode 100644 index 00000000..2361a7f4 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/annotation.hpp @@ -0,0 +1,95 @@ +/*============================================================================= + 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_CONJURE_ANNOTATION_HPP) +#define BOOST_SPIRIT_CONJURE_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) {} + + void operator()(ast::function_call& x) const + { + x.function_name.id = id; + } + + void operator()(ast::identifier& x) const + { + x.id = id; + } + + template <typename T> + void operator()(T& x) const + { + // no-op + } + }; + + 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::variable_declaration& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.lhs.id = id; + } + + void operator()(ast::assignment& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.lhs.id = id; + } + + void operator()(ast::return_statement& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.id = id; + } + + void operator()(ast::identifier& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.id = id; + } + }; +} + +#endif + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/ast.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/ast.hpp new file mode 100644 index 00000000..d2f893b0 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/ast.hpp @@ -0,0 +1,209 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_AST_HPP) +#define BOOST_SPIRIT_CONJURE_AST_HPP + +#include <boost/config/warning_disable.hpp> +#include <boost/variant/recursive_variant.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/fusion/include/io.hpp> +#include <boost/optional.hpp> +#include <list> + +#include "ids.hpp" + +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 unary; + struct function_call; + struct expression; + + struct identifier : tagged + { + identifier(std::string const& name = "") : name(name) {} + std::string name; + }; + + typedef boost::variant< + nil + , bool + , unsigned int + , identifier + , boost::recursive_wrapper<unary> + , boost::recursive_wrapper<function_call> + , boost::recursive_wrapper<expression> + > + operand; + + struct unary + { + token_ids::type operator_; + operand operand_; + }; + + struct operation + { + token_ids::type operator_; + operand operand_; + }; + + struct function_call + { + identifier function_name; + std::list<expression> args; + }; + + struct expression + { + operand first; + std::list<operation> rest; + }; + + struct assignment + { + identifier lhs; + expression rhs; + }; + + struct variable_declaration + { + identifier lhs; + boost::optional<expression> rhs; + }; + + struct if_statement; + struct while_statement; + struct statement_list; + struct return_statement; + + typedef boost::variant< + variable_declaration + , assignment + , boost::recursive_wrapper<if_statement> + , boost::recursive_wrapper<while_statement> + , boost::recursive_wrapper<return_statement> + , boost::recursive_wrapper<statement_list> + > + statement; + + struct statement_list : std::list<statement> {}; + + struct if_statement + { + expression condition; + statement then; + boost::optional<statement> else_; + }; + + struct while_statement + { + expression condition; + statement body; + }; + + struct return_statement : tagged + { + boost::optional<expression> expr; + }; + + struct function + { + std::string return_type; + identifier function_name; + std::list<identifier> args; + statement_list body; + }; + + typedef std::list<function> function_list; + + // print functions for debugging + inline std::ostream& operator<<(std::ostream& out, nil) + { + out << "nil"; return out; + } + + inline std::ostream& operator<<(std::ostream& out, identifier const& id) + { + out << id.name; return out; + } +}} + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::unary, + (client::token_ids::type, operator_) + (client::ast::operand, operand_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::operation, + (client::token_ids::type, operator_) + (client::ast::operand, operand_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::function_call, + (client::ast::identifier, function_name) + (std::list<client::ast::expression>, args) +) + +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::identifier, lhs) + (boost::optional<client::ast::expression>, rhs) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::assignment, + (client::ast::identifier, lhs) + (client::ast::expression, rhs) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::if_statement, + (client::ast::expression, condition) + (client::ast::statement, then) + (boost::optional<client::ast::statement>, else_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::while_statement, + (client::ast::expression, condition) + (client::ast::statement, body) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::return_statement, + (boost::optional<client::ast::expression>, expr) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::function, + (std::string, return_type) + (client::ast::identifier, function_name) + (std::list<client::ast::identifier>, args) + (client::ast::statement_list, body) +) + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/compiler.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/compiler.cpp new file mode 100644 index 00000000..87459b35 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/compiler.cpp @@ -0,0 +1,622 @@ +/*============================================================================= + 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 "config.hpp" +#include "compiler.hpp" +#include "vm.hpp" +#include <boost/foreach.hpp> +#include <boost/variant/apply_visitor.hpp> +#include <boost/assert.hpp> +#include <boost/lexical_cast.hpp> +#include <set> + +namespace client { namespace code_gen +{ + void function::op(int a) + { + code.push_back(a); + size_ += 1; + } + + void function::op(int a, int b) + { + code.push_back(a); + code.push_back(b); + size_ += 2; + } + + void function::op(int a, int b, int c) + { + code.push_back(a); + code.push_back(b); + code.push_back(c); + size_ += 3; + } + + int const* function::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 function::add_var(std::string const& name) + { + std::size_t n = variables.size(); + variables[name] = n; + } + + void function::link_to(std::string const& name, std::size_t address) + { + function_calls[address] = name; + } + + void function::print_assembler() const + { + std::vector<int>::const_iterator pc = code.begin() + address; + + 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; + } + + std::map<std::size_t, std::string> lines; + std::set<std::size_t> jumps; + + while (pc != (code.begin() + address + size_)) + { + std::string line; + std::size_t address = pc - code.begin(); + + switch (*pc++) + { + case op_neg: + line += " op_neg"; + break; + + case op_not: + line += " op_not"; + break; + + case op_add: + line += " op_add"; + break; + + case op_sub: + line += " op_sub"; + break; + + case op_mul: + line += " op_mul"; + break; + + case op_div: + line += " op_div"; + break; + + case op_eq: + line += " op_eq"; + break; + + case op_neq: + line += " op_neq"; + break; + + case op_lt: + line += " op_lt"; + break; + + case op_lte: + line += " op_lte"; + break; + + case op_gt: + line += " op_gt"; + break; + + case op_gte: + line += " op_gte"; + break; + + case op_and: + line += " op_and"; + break; + + case op_or: + line += " op_or"; + break; + + case op_load: + line += " op_load "; + line += locals[*pc++]; + break; + + case op_store: + line += " op_store "; + line += locals[*pc++]; + break; + + case op_int: + line += " op_int "; + line += boost::lexical_cast<std::string>(*pc++); + break; + + case op_true: + line += " op_true"; + break; + + case op_false: + line += " op_false"; + break; + + case op_jump: + { + line += " op_jump "; + std::size_t pos = (pc - code.begin()) + *pc++; + if (pos == code.size()) + line += "end"; + else + line += boost::lexical_cast<std::string>(pos); + jumps.insert(pos); + } + break; + + case op_jump_if: + { + line += " op_jump_if "; + std::size_t pos = (pc - code.begin()) + *pc++; + if (pos == code.size()) + line += "end"; + else + line += boost::lexical_cast<std::string>(pos); + jumps.insert(pos); + } + break; + + case op_call: + { + line += " op_call "; + int nargs = *pc++; + std::size_t jump = *pc++; + line += boost::lexical_cast<std::string>(nargs) + ", "; + BOOST_ASSERT(function_calls.find(jump) != function_calls.end()); + line += function_calls.find(jump)->second; + } + break; + + case op_stk_adj: + line += " op_stk_adj "; + line += boost::lexical_cast<std::string>(*pc++); + break; + + + case op_return: + line += " op_return"; + break; + } + lines[address] = line; + } + + std::cout << "start:" << std::endl; + typedef std::pair<std::size_t, std::string> line_info; + BOOST_FOREACH(line_info const& l, lines) + { + std::size_t pos = l.first; + if (jumps.find(pos) != jumps.end()) + std::cout << pos << ':' << std::endl; + std::cout << l.second << std::endl; + } + + std::cout << "end:" << std::endl << std::endl; + } + + bool compiler::operator()(unsigned int x) + { + BOOST_ASSERT(current != 0); + current->op(op_int, x); + return true; + } + + bool compiler::operator()(bool x) + { + BOOST_ASSERT(current != 0); + current->op(x ? op_true : op_false); + return true; + } + + bool compiler::operator()(ast::identifier const& x) + { + BOOST_ASSERT(current != 0); + int const* p = current->find_var(x.name); + if (p == 0) + { + error_handler(x.id, "Undeclared variable: " + x.name); + return false; + } + current->op(op_load, *p); + return true; + } + + bool compiler::operator()(token_ids::type const& x) + { + BOOST_ASSERT(current != 0); + switch (x) + { + case token_ids::plus: current->op(op_add); break; + case token_ids::minus: current->op(op_sub); break; + case token_ids::times: current->op(op_mul); break; + case token_ids::divide: current->op(op_div); break; + + case token_ids::equal: current->op(op_eq); break; + case token_ids::not_equal: current->op(op_neq); break; + case token_ids::less: current->op(op_lt); break; + case token_ids::less_equal: current->op(op_lte); break; + case token_ids::greater: current->op(op_gt); break; + case token_ids::greater_equal: current->op(op_gte); break; + + case token_ids::logical_or: current->op(op_or); break; + case token_ids::logical_and: current->op(op_and); break; + default: BOOST_ASSERT(0); return false; + } + return true; + } + + bool compiler::operator()(ast::unary const& x) + { + BOOST_ASSERT(current != 0); + if (!boost::apply_visitor(*this, x.operand_)) + return false; + switch (x.operator_) + { + case token_ids::minus: current->op(op_neg); break; + case token_ids::not_: current->op(op_not); break; + case token_ids::plus: break; + default: BOOST_ASSERT(0); return false; + } + return true; + } + + bool compiler::operator()(ast::function_call const& x) + { + BOOST_ASSERT(current != 0); + + if (functions.find(x.function_name.name) == functions.end()) + { + error_handler(x.function_name.id, "Function not found: " + x.function_name.name); + return false; + } + + boost::shared_ptr<code_gen::function> p = functions[x.function_name.name]; + + if (p->nargs() != x.args.size()) + { + error_handler(x.function_name.id, "Wrong number of arguments: " + x.function_name.name); + return false; + } + + BOOST_FOREACH(ast::expression const& expr, x.args) + { + if (!(*this)(expr)) + return false; + } + + current->op( + op_call, + p->nargs(), + p->get_address()); + current->link_to(x.function_name.name, p->get_address()); + + return true; + } + + namespace + { + int precedence[] = { + // precedence 1 + 1, // op_comma + + // precedence 2 + 2, // op_assign + 2, // op_plus_assign + 2, // op_minus_assign + 2, // op_times_assign + 2, // op_divide_assign + 2, // op_mod_assign + 2, // op_bit_and_assign + 2, // op_bit_xor_assign + 2, // op_bitor_assign + 2, // op_shift_left_assign + 2, // op_shift_right_assign + + // precedence 3 + 3, // op_logical_or + + // precedence 4 + 4, // op_logical_and + + // precedence 5 + 5, // op_bit_or + + // precedence 6 + 6, // op_bit_xor + + // precedence 7 + 7, // op_bit_and + + // precedence 8 + 8, // op_equal + 8, // op_not_equal + + // precedence 9 + 9, // op_less + 9, // op_less_equal + 9, // op_greater + 9, // op_greater_equal + + // precedence 10 + 10, // op_shift_left + 10, // op_shift_right + + // precedence 11 + 11, // op_plus + 11, // op_minus + + // precedence 12 + 12, // op_times + 12, // op_divide + 12 // op_mod + }; + } + + inline int precedence_of(token_ids::type op) + { + return precedence[op & 0xFF]; + } + + // The Shunting-yard algorithm + bool compiler::compile_expression( + int min_precedence, + std::list<ast::operation>::const_iterator& rbegin, + std::list<ast::operation>::const_iterator rend) + { + while ((rbegin != rend) && (precedence_of(rbegin->operator_) >= min_precedence)) + { + token_ids::type op = rbegin->operator_; + if (!boost::apply_visitor(*this, rbegin->operand_)) + return false; + ++rbegin; + + while ((rbegin != rend) && (precedence_of(rbegin->operator_) > precedence_of(op))) + { + token_ids::type next_op = rbegin->operator_; + compile_expression(precedence_of(next_op), rbegin, rend); + } + (*this)(op); + } + return true; + } + + bool compiler::operator()(ast::expression const& x) + { + BOOST_ASSERT(current != 0); + if (!boost::apply_visitor(*this, x.first)) + return false; + std::list<ast::operation>::const_iterator rbegin = x.rest.begin(); + if (!compile_expression(0, rbegin, x.rest.end())) + return false; + return true; + } + + bool compiler::operator()(ast::assignment const& x) + { + BOOST_ASSERT(current != 0); + if (!(*this)(x.rhs)) + return false; + int const* p = current->find_var(x.lhs.name); + if (p == 0) + { + error_handler(x.lhs.id, "Undeclared variable: " + x.lhs.name); + return false; + } + current->op(op_store, *p); + return true; + } + + bool compiler::operator()(ast::variable_declaration const& x) + { + BOOST_ASSERT(current != 0); + int const* p = current->find_var(x.lhs.name); + if (p != 0) + { + error_handler(x.lhs.id, "Duplicate variable: " + x.lhs.name); + return false; + } + if (x.rhs) // if there's an RHS initializer + { + bool r = (*this)(*x.rhs); + if (r) // don't add the variable if the RHS fails + { + current->add_var(x.lhs.name); + current->op(op_store, *current->find_var(x.lhs.name)); + } + return r; + } + else + { + current->add_var(x.lhs.name); + } + return true; + } + + bool compiler::operator()(ast::statement const& x) + { + BOOST_ASSERT(current != 0); + return boost::apply_visitor(*this, x); + } + + bool compiler::operator()(ast::statement_list const& x) + { + BOOST_ASSERT(current != 0); + BOOST_FOREACH(ast::statement const& s, x) + { + if (!(*this)(s)) + return false; + } + return true; + } + + bool compiler::operator()(ast::if_statement const& x) + { + BOOST_ASSERT(current != 0); + if (!(*this)(x.condition)) + return false; + current->op(op_jump_if, 0); // we shall fill this (0) in later + std::size_t skip = current->size()-1; // mark its position + if (!(*this)(x.then)) + return false; + (*current)[skip] = current->size()-skip; // now we know where to jump to (after the if branch) + + if (x.else_) // We got an alse + { + (*current)[skip] += 2; // adjust for the "else" jump + current->op(op_jump, 0); // we shall fill this (0) in later + std::size_t exit = current->size()-1; // mark its position + if (!(*this)(*x.else_)) + return false; + (*current)[exit] = current->size()-exit;// now we know where to jump to (after the else branch) + } + + return true; + } + + bool compiler::operator()(ast::while_statement const& x) + { + BOOST_ASSERT(current != 0); + std::size_t loop = current->size(); // mark our position + if (!(*this)(x.condition)) + return false; + current->op(op_jump_if, 0); // we shall fill this (0) in later + std::size_t exit = current->size()-1; // mark its position + if (!(*this)(x.body)) + return false; + current->op(op_jump, + int(loop-1) - int(current->size())); // loop back + (*current)[exit] = current->size()-exit; // now we know where to jump to (to exit the loop) + return true; + } + + bool compiler::operator()(ast::return_statement const& x) + { + if (void_return) + { + if (x.expr) + { + error_handler(x.id, "'void' function returning a value: "); + return false; + } + } + else + { + if (!x.expr) + { + error_handler(x.id, current_function_name + " function must return a value: "); + return false; + } + } + + if (x.expr) + { + if (!(*this)(*x.expr)) + return false; + } + current->op(op_return); + return true; + } + + bool compiler::operator()(ast::function const& x) + { + void_return = x.return_type == "void"; + if (functions.find(x.function_name.name) != functions.end()) + { + error_handler(x.function_name.id, "Duplicate function: " + x.function_name.name); + return false; + } + boost::shared_ptr<code_gen::function>& p = functions[x.function_name.name]; + p.reset(new code_gen::function(code, x.args.size())); + current = p.get(); + current_function_name = x.function_name.name; + + // op_stk_adj 0 for now. we'll know how many variables + // we'll have later and add them + current->op(op_stk_adj, 0); + BOOST_FOREACH(ast::identifier const& arg, x.args) + { + current->add_var(arg.name); + } + + if (!(*this)(x.body)) + return false; + (*current)[1] = current->nvars(); // now store the actual number of variables + // this includes the arguments + return true; + } + + bool compiler::operator()(ast::function_list const& x) + { + // Jump to the main function + code.push_back(op_jump); + code.push_back(0); // we will fill this in later when we finish compiling + // and we know where the main function is + + BOOST_FOREACH(ast::function const& f, x) + { + if (!(*this)(f)) + { + code.clear(); + return false; + } + } + // find the main function + boost::shared_ptr<code_gen::function> p = + find_function("main"); + + if (!p) // main function not found + { + std::cerr << "Error: main function not defined" << std::endl; + return false; + } + code[1] = p->get_address()-1; // jump to this (main function) address + + return true; + } + + void compiler::print_assembler() const + { + typedef std::pair<std::string, boost::shared_ptr<code_gen::function> > pair; + BOOST_FOREACH(pair const& p, functions) + { + std::cout << ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" << std::endl; + std::cout << p.second->get_address() << ": function " << p.first << std::endl; + p.second->print_assembler(); + } + } + + boost::shared_ptr<code_gen::function> + compiler::find_function(std::string const& name) const + { + function_table::const_iterator i = functions.find(name); + if (i == functions.end()) + return boost::shared_ptr<code_gen::function>(); + else + return i->second; + } +}} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/compiler.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/compiler.hpp new file mode 100644 index 00000000..b49c0ac4 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/compiler.hpp @@ -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) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_COMPILER_HPP) +#define BOOST_SPIRIT_CONJURE_COMPILER_HPP + +#include "ast.hpp" +#include "error_handler.hpp" +#include <vector> +#include <map> +#include <boost/function.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_function.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> + +namespace client { namespace code_gen +{ + /////////////////////////////////////////////////////////////////////////// + // The Function + /////////////////////////////////////////////////////////////////////////// + struct function + { + function(std::vector<int>& code, int nargs) + : code(code), address(code.size()), size_(0), nargs_(nargs) {} + + 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[address+i]; } + int const& operator[](std::size_t i) const { return code[address+i]; } + std::size_t size() const { return size_; } + std::size_t get_address() const { return address; } + + int nargs() const { return nargs_; } + int nvars() const { return variables.size(); } + int const* find_var(std::string const& name) const; + void add_var(std::string const& name); + void link_to(std::string const& name, std::size_t address); + + void print_assembler() const; + + private: + + std::map<std::string, int> variables; + std::map<std::size_t, std::string> function_calls; + std::vector<int>& code; + std::size_t address; + std::size_t size_; + std::size_t nargs_; + }; + + /////////////////////////////////////////////////////////////////////////// + // The Compiler + /////////////////////////////////////////////////////////////////////////// + struct compiler + { + typedef bool result_type; + + template <typename ErrorHandler> + compiler(ErrorHandler& error_handler_) + : current(0) + { + 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) { BOOST_ASSERT(0); return false; } + bool operator()(unsigned int x); + bool operator()(bool x); + bool operator()(ast::identifier const& x); + bool operator()(token_ids::type const& x); + bool operator()(ast::unary const& x); + bool operator()(ast::function_call const& x); + bool operator()(ast::expression const& x); + bool operator()(ast::assignment const& x); + bool operator()(ast::variable_declaration const& x); + bool operator()(ast::statement_list const& x); + bool operator()(ast::statement const& x); + bool operator()(ast::if_statement const& x); + bool operator()(ast::while_statement const& x); + bool operator()(ast::return_statement const& x); + bool operator()(ast::function const& x); + bool operator()(ast::function_list const& x); + + void print_assembler() const; + + boost::shared_ptr<code_gen::function> + find_function(std::string const& name) const; + + std::vector<int>& get_code() { return code; } + std::vector<int> const& get_code() const { return code; } + + private: + + bool compile_expression( + int min_precedence, + std::list<ast::operation>::const_iterator& rbegin, + std::list<ast::operation>::const_iterator rend); + + typedef std::map<std::string, boost::shared_ptr<code_gen::function> > function_table; + + std::vector<int> code; + code_gen::function* current; + std::string current_function_name; + function_table functions; + bool void_return; + + boost::function< + void(int tag, std::string const& what)> + error_handler; + }; +}} + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/config.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/config.hpp new file mode 100644 index 00000000..70af0db0 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/config.hpp @@ -0,0 +1,51 @@ +/*============================================================================= + 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) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_LEXER_CONFIG_HPP) +#define BOOST_SPIRIT_CONJURE_LEXER_CONFIG_HPP + +/////////////////////////////////////////////////////////////////////////////// +// The conjure lexer example can be built in 3 different variations: +// +// - With a lexer using runtime generated DFA tables +// - With a lexer using pre-generated (static) DFA tables +// - With a lexer using a pre-generated custom switch based state machine +// +// Use one of the following preprocessor constants to define, which of those +// will be built: + +/////////////////////////////////////////////////////////////////////////////// +// Use the lexer based on runtime generated DFA tables +// #define CONJURE_LEXER_DYNAMIC_TABLES 1 + +/////////////////////////////////////////////////////////////////////////////// +// Use the lexer based on pre-generated static DFA tables +// #define CONJURE_LEXER_STATIC_TABLES 1 + +/////////////////////////////////////////////////////////////////////////////// +// Use the lexer based on runtime generated DFA tables +// #define CONJURE_LEXER_STATIC_SWITCH 1 + +/////////////////////////////////////////////////////////////////////////////// +// The default is to use the dynamic table driven lexer +#if CONJURE_LEXER_DYNAMIC_TABLES == 0 && \ + CONJURE_LEXER_STATIC_TABLES == 0 && \ + CONJURE_LEXER_STATIC_SWITCH == 0 + +#define CONJURE_LEXER_DYNAMIC_TABLES 1 +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Make sure we have only one lexer type selected +#if (CONJURE_LEXER_DYNAMIC_TABLES != 0 && CONJURE_LEXER_STATIC_TABLES != 0) || \ + (CONJURE_LEXER_DYNAMIC_TABLES != 0 && CONJURE_LEXER_STATIC_SWITCH != 0) || \ + (CONJURE_LEXER_STATIC_TABLES != 0 && CONJURE_LEXER_STATIC_SWITCH != 0) + +#error "Configuration problem: please select exactly one type of lexer to build" +#endif + +#endif + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/conjure_static_lexer.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/conjure_static_lexer.hpp new file mode 100644 index 00000000..d626840b --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/conjure_static_lexer.hpp @@ -0,0 +1,483 @@ +// Copyright (c) 2008-2009 Ben Hanson +// Copyright (c) 2008-2011 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_CONJURE_STATIC_JUL_25_2011_07_03_08) +#define BOOST_SPIRIT_LEXER_NEXT_TOKEN_CONJURE_STATIC_JUL_25_2011_07_03_08 + +#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_conjure_static[1] = +{ + "INITIAL" +}; + +//////////////////////////////////////////////////////////////////////////////// +// this variable defines the number of lexer states +std::size_t const lexer_state_count_conjure_static = 1; + +//////////////////////////////////////////////////////////////////////////////// +// this function returns the next matched token +template<typename Iterator> +std::size_t next_token_conjure_static (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 std::size_t const npos = static_cast<std::size_t>(~0); + static std::size_t const lookup_[256] = { + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 7, 7, 41, 41, 7, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 7, 8, 41, 41, 41, 41, 9, 41, + 10, 11, 12, 13, 14, 15, 41, 16, + 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 41, 19, 20, 21, 22, 41, + 41, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 41, 41, 41, 41, 18, + 41, 23, 18, 18, 24, 25, 26, 18, + 27, 28, 18, 18, 29, 18, 30, 31, + 18, 18, 32, 33, 34, 35, 36, 37, + 18, 18, 18, 38, 39, 40, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41 }; + static std::size_t const dfa_alphabet_ = 42; + static std::size_t const dfa_[2604] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 27, 13, 11, 20, 21, 18, 16, + 24, 17, 19, 2, 26, 25, 14, 12, + 15, 26, 26, 7, 4, 26, 6, 26, + 26, 26, 9, 26, 3, 26, 5, 8, + 22, 10, 23, 0, 1, 35, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 28, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 1, 32, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 29, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 0, 0, + 0, 0, 1, 32, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 26, 26, 26, 26, 26, + 26, 30, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 1, 32, 28, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 26, 32, 26, + 26, 26, 31, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 33, 26, 26, 26, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 1, 32, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 26, + 26, 26, 26, 34, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 0, 0, + 0, 0, 1, 32, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 35, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 37, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 61, 26, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 38, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 262177, 20, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 39, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 131091, 12, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 40, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 131093, + 14, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 393241, 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 393242, 17, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 131099, 18, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 131100, + 19, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 42, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 40, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 41, 22, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 123, 23, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 125, + 24, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 44, 25, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 59, 27, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 32, 28, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 34, + 30, 0, 0, 0, 0, 27, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 32, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 43, 26, 26, 0, 0, + 0, 0, 1, 32, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 26, 26, 26, 26, 44, + 26, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 1, 32, 28, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 26, 26, 26, + 45, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 46, 26, 26, 26, 0, 0, 0, 0, + 1, 65538, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 0, 0, + 0, 0, 1, 32, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 47, 26, 26, 26, 26, + 0, 0, 0, 0, 1, 32, 28, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 26, 26, 26, + 48, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 49, 26, 26, 26, 0, 0, 0, 0, + 1, 131084, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 131085, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 131089, 10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 131090, + 11, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 131092, 13, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 131094, 15, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 42, 42, 42, 42, 42, + 50, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 51, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 1, 32, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 43, 26, 26, 26, 26, 0, 0, + 0, 0, 1, 32, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 52, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 1, 65537, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 53, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 1, 32, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 26, + 26, 26, 26, 26, 26, 54, 26, 26, + 26, 26, 26, 26, 26, 26, 0, 0, + 0, 0, 1, 32, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 55, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 56, 56, 56, 56, 56, + 50, 56, 56, 56, 57, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 1, 36, + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 1, 65536, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 0, 0, + 0, 0, 1, 65539, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 1, 32, 28, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 58, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 59, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 56, + 56, 56, 56, 56, 60, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 1, 33, 29, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 65540, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 26, 61, 26, 26, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 56, + 56, 56, 56, 56, 60, 56, 56, 56, + 57, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 1, 65541, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 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_conjure_static +{ + // 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_conjure_static; + } + + // return the name of the lexer state as given by 'idx' + static char const* state_name(std::size_t idx) + { + return lexer_state_names_conjure_static[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_conjure_static(start_state_, bol_, start_token_, end_, unique_id_); + } +}; + +}}}}} // namespace boost::spirit::lex::lexertl::static_ + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/conjure_static_lexer_generate.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/conjure_static_lexer_generate.cpp new file mode 100644 index 00000000..1ba8fac0 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/conjure_static_lexer_generate.cpp @@ -0,0 +1,43 @@ +// 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) + +// This small utility program generates the 2 static lexers, the static table +// driven and the static switch based lexer. + +#include <fstream> +#include <iostream> + +#include "lexer_def.hpp" +#include <boost/spirit/include/lex_generate_static_lexertl.hpp> + +int main() +{ + typedef std::string::const_iterator base_iterator_type; + typedef client::lexer::conjure_tokens<base_iterator_type> lexer_type; + + lexer_type lexer; + + // first generate the static switch based lexer + std::ofstream out_static("conjure_static_switch_lexer.hpp"); + + bool result = boost::spirit::lex::lexertl::generate_static_switch( + lexer, out_static, "conjure_static_switch"); + if (!result) { + std::cerr << "Failed to generate static switch based lexer\n"; + return -1; + } + + // now generate the static table based lexer + std::ofstream out("conjure_static_lexer.hpp"); + result = boost::spirit::lex::lexertl::generate_static( + lexer, out, "conjure_static"); + if (!result) { + std::cerr << "Failed to generate static table based lexer\n"; + return -1; + } + + return 0; +} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/conjure_static_switch_lexer.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/conjure_static_switch_lexer.hpp new file mode 100644 index 00000000..3b23016b --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/conjure_static_switch_lexer.hpp @@ -0,0 +1,873 @@ +// Copyright (c) 2008-2009 Ben Hanson +// Copyright (c) 2008-2011 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_CONJURE_STATIC_SWITCH_JUL_25_2011_07_03_08) +#define BOOST_SPIRIT_LEXER_NEXT_TOKEN_CONJURE_STATIC_SWITCH_JUL_25_2011_07_03_08 + +#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_conjure_static_switch[1] = +{ + "INITIAL" +}; + +//////////////////////////////////////////////////////////////////////////////// +// this variable defines the number of lexer states +std::size_t const lexer_state_count_conjure_static_switch = 1; + +//////////////////////////////////////////////////////////////////////////////// +// this function returns the next matched token +template<typename Iterator> +std::size_t next_token_conjure_static_switch (std::size_t& /*start_state_*/, bool& /*bol_*/, + Iterator &start_token_, Iterator const& end_, std::size_t& unique_id_) +{ + static std::size_t const npos = static_cast<std::size_t>(~0); + + if (start_token_ == end_) + { + unique_id_ = npos; + return 0; + } + + Iterator curr_ = start_token_; + bool end_state_ = false; + std::size_t id_ = npos; + std::size_t uid_ = npos; + Iterator end_token_ = start_token_; + + char ch_ = 0; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9')) goto state0_1; + + if (ch_ == 't') goto state0_2; + + if (ch_ == 'f') goto state0_3; + + if (ch_ == 'v') goto state0_4; + + if (ch_ == 'i') goto state0_5; + + if (ch_ == 'e') goto state0_6; + + if (ch_ == 'w') goto state0_7; + + if (ch_ == 'r') goto state0_8; + + if (ch_ == '|') goto state0_9; + + if (ch_ == '&') goto state0_10; + + if (ch_ == '=') goto state0_11; + + if (ch_ == '!') goto state0_12; + + if (ch_ == '<') goto state0_13; + + if (ch_ == '>') goto state0_14; + + if (ch_ == '+') goto state0_15; + + if (ch_ == '-') goto state0_16; + + if (ch_ == '*') goto state0_17; + + if (ch_ == '/') goto state0_18; + + if (ch_ == '(') goto state0_19; + + if (ch_ == ')') goto state0_20; + + if (ch_ == '{') goto state0_21; + + if (ch_ == '}') goto state0_22; + + if (ch_ == ',') goto state0_23; + + if (ch_ == ';') goto state0_24; + + if ((ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'd') || (ch_ >= 'g' && ch_ <= 'h') || (ch_ >= 'j' && ch_ <= 'q') || ch_ == 's' || ch_ == 'u' || (ch_ >= 'x' && ch_ <= 'z')) goto state0_25; + + if ((ch_ >= '\t' && ch_ <= '\n') || ch_ == '\r' || ch_ == ' ') goto state0_26; + goto end; + +state0_1: + end_state_ = true; + id_ = 35; + uid_ = 0; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9')) goto state0_1; + goto end; + +state0_2: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'q') || (ch_ >= 's' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'r') goto state0_27; + goto end; + +state0_3: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'b' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'a') goto state0_28; + goto end; + +state0_4: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'n') || (ch_ >= 'p' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'o') goto state0_29; + goto end; + +state0_5: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'e') || (ch_ >= 'g' && ch_ <= 'm') || (ch_ >= 'o' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'n') goto state0_30; + + if (ch_ == 'f') goto state0_31; + goto end; + +state0_6: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'k') || (ch_ >= 'm' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'l') goto state0_32; + goto end; + +state0_7: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'g') || (ch_ >= 'i' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'h') goto state0_33; + goto end; + +state0_8: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'd') || (ch_ >= 'f' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'e') goto state0_34; + goto end; + +state0_9: + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '|') goto state0_35; + goto end; + +state0_10: + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '&') goto state0_36; + goto end; + +state0_11: + end_state_ = true; + id_ = 61; + uid_ = 26; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '=') goto state0_37; + goto end; + +state0_12: + end_state_ = true; + id_ = 262177; + uid_ = 20; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '=') goto state0_38; + goto end; + +state0_13: + end_state_ = true; + id_ = 131091; + uid_ = 12; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '=') goto state0_39; + goto end; + +state0_14: + end_state_ = true; + id_ = 131093; + uid_ = 14; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '=') goto state0_40; + goto end; + +state0_15: + end_state_ = true; + id_ = 393241; + uid_ = 16; + end_token_ = curr_; + goto end; + +state0_16: + end_state_ = true; + id_ = 393242; + uid_ = 17; + end_token_ = curr_; + goto end; + +state0_17: + end_state_ = true; + id_ = 131099; + uid_ = 18; + end_token_ = curr_; + goto end; + +state0_18: + end_state_ = true; + id_ = 131100; + uid_ = 19; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '*') goto state0_41; + goto end; + +state0_19: + end_state_ = true; + id_ = 40; + uid_ = 21; + end_token_ = curr_; + goto end; + +state0_20: + end_state_ = true; + id_ = 41; + uid_ = 22; + end_token_ = curr_; + goto end; + +state0_21: + end_state_ = true; + id_ = 123; + uid_ = 23; + end_token_ = curr_; + goto end; + +state0_22: + end_state_ = true; + id_ = 125; + uid_ = 24; + end_token_ = curr_; + goto end; + +state0_23: + end_state_ = true; + id_ = 44; + uid_ = 25; + end_token_ = curr_; + goto end; + +state0_24: + end_state_ = true; + id_ = 59; + uid_ = 27; + end_token_ = curr_; + goto end; + +state0_25: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_26: + end_state_ = true; + id_ = 34; + uid_ = 30; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '\t' && ch_ <= '\n') || ch_ == '\r' || ch_ == ' ') goto state0_26; + goto end; + +state0_27: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 't') || (ch_ >= 'v' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'u') goto state0_42; + goto end; + +state0_28: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'k') || (ch_ >= 'm' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'l') goto state0_43; + goto end; + +state0_29: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'h') || (ch_ >= 'j' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'i') goto state0_44; + goto end; + +state0_30: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 's') || (ch_ >= 'u' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 't') goto state0_45; + goto end; + +state0_31: + end_state_ = true; + id_ = 65538; + uid_ = 4; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_32: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'r') || (ch_ >= 't' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 's') goto state0_46; + goto end; + +state0_33: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'h') || (ch_ >= 'j' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'i') goto state0_47; + goto end; + +state0_34: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 's') || (ch_ >= 'u' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 't') goto state0_48; + goto end; + +state0_35: + end_state_ = true; + id_ = 131084; + uid_ = 8; + end_token_ = curr_; + goto end; + +state0_36: + end_state_ = true; + id_ = 131085; + uid_ = 9; + end_token_ = curr_; + goto end; + +state0_37: + end_state_ = true; + id_ = 131089; + uid_ = 10; + end_token_ = curr_; + goto end; + +state0_38: + end_state_ = true; + id_ = 131090; + uid_ = 11; + end_token_ = curr_; + goto end; + +state0_39: + end_state_ = true; + id_ = 131092; + uid_ = 13; + end_token_ = curr_; + goto end; + +state0_40: + end_state_ = true; + id_ = 131094; + uid_ = 15; + end_token_ = curr_; + goto end; + +state0_41: + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ != '*') goto state0_41; + + if (ch_ == '*') goto state0_49; + goto end; + +state0_42: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'd') || (ch_ >= 'f' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'e') goto state0_50; + goto end; + +state0_43: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'r') || (ch_ >= 't' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 's') goto state0_42; + goto end; + +state0_44: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'c') || (ch_ >= 'e' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'd') goto state0_51; + goto end; + +state0_45: + end_state_ = true; + id_ = 65537; + uid_ = 3; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_46: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'd') || (ch_ >= 'f' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'e') goto state0_52; + goto end; + +state0_47: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'k') || (ch_ >= 'm' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'l') goto state0_53; + goto end; + +state0_48: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 't') || (ch_ >= 'v' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'u') goto state0_54; + goto end; + +state0_49: + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '*') goto state0_49; + + if (ch_ != '*' && ch_ != '/') goto state0_55; + + if (ch_ == '/') goto state0_56; + goto end; + +state0_50: + end_state_ = true; + id_ = 36; + uid_ = 1; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_51: + end_state_ = true; + id_ = 65536; + uid_ = 2; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_52: + end_state_ = true; + id_ = 65539; + uid_ = 5; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_53: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'd') || (ch_ >= 'f' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'e') goto state0_57; + goto end; + +state0_54: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'q') || (ch_ >= 's' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'r') goto state0_58; + goto end; + +state0_55: + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ != '*') goto state0_55; + + if (ch_ == '*') goto state0_59; + goto end; + +state0_56: + end_state_ = true; + id_ = 33; + uid_ = 29; + end_token_ = curr_; + goto end; + +state0_57: + end_state_ = true; + id_ = 65540; + uid_ = 6; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_58: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'm') || (ch_ >= 'o' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'n') goto state0_60; + goto end; + +state0_59: + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ != '*' && ch_ != '/') goto state0_55; + + if (ch_ == '/') goto state0_56; + + if (ch_ == '*') goto state0_59; + goto end; + +state0_60: + end_state_ = true; + id_ = 65541; + uid_ = 7; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + +end: + 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_conjure_static_switch +{ + // 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_conjure_static_switch; + } + + // return the name of the lexer state as given by 'idx' + static char const* state_name(std::size_t idx) + { + return lexer_state_names_conjure_static_switch[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_conjure_static_switch(start_state_, bol_, start_token_, end_, unique_id_); + } +}; + +}}}}} // namespace boost::spirit::lex::lexertl::static_ + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/error_handler.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/error_handler.hpp new file mode 100644 index 00000000..2b1ee5b2 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/error_handler.hpp @@ -0,0 +1,105 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_ERROR_HANDLER_HPP) +#define BOOST_SPIRIT_CONJURE_ERROR_HANDLER_HPP + +#include <iostream> +#include <string> +#include <vector> + +namespace client +{ + /////////////////////////////////////////////////////////////////////////////// + // The error handler + /////////////////////////////////////////////////////////////////////////////// + template <typename BaseIterator, typename Iterator> + struct error_handler + { + template <typename, typename, typename> + struct result { typedef void type; }; + + error_handler(BaseIterator first, BaseIterator last) + : first(first), last(last) {} + + template <typename Message, typename What> + void operator()( + Message const& message, + What const& what, + Iterator err_pos) const + { + // retrieve underlying iterator from current token, err_pos points + // to the last validly matched token, so we use its end iterator + // as the error position + BaseIterator err_pos_base = err_pos->matched().end(); + std::cout << message << what << std::endl; + if (err_pos_base != BaseIterator()) + dump_error_line(err_pos_base); + } + + void dump_error_line(BaseIterator err_pos_base) const + { + int line; + BaseIterator line_start = get_pos(err_pos_base, line); + if (err_pos_base != last) + { + std::cout << " line " << line << ':' << std::endl; + std::cout << get_line(line_start) << std::endl; + for (; line_start != err_pos_base; ++line_start) + std::cout << ' '; + std::cout << '^' << std::endl; + } + else + { + std::cout << "Unexpected end of file.\n"; + } + + } + + BaseIterator get_pos(BaseIterator err_pos, int& line) const + { + line = 1; + BaseIterator i = first; + BaseIterator 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(BaseIterator err_pos) const + { + BaseIterator i = err_pos; + // position i to the next EOL + while (i != last && (*i != '\r' && *i != '\n')) + ++i; + return std::string(err_pos, i); + } + + BaseIterator first; + BaseIterator last; + std::vector<Iterator> iters; + }; +} + +#endif + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/expression.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/expression.cpp new file mode 100644 index 00000000..5b51ce36 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/expression.cpp @@ -0,0 +1,20 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if defined(_MSC_VER) +# pragma warning(disable: 4345) +#endif + +#include "config.hpp" +#include "lexer.hpp" +#include "expression_def.hpp" + +typedef std::string::const_iterator base_iterator_type; +typedef client::lexer::conjure_tokens<base_iterator_type> lexer_type; +typedef lexer_type::iterator_type iterator_type; + +template struct client::parser::expression<iterator_type, lexer_type>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/expression.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/expression.hpp new file mode 100644 index 00000000..a5d431ef --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/expression.hpp @@ -0,0 +1,58 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_EXPRESSION_HPP) +#define BOOST_SPIRIT_CONJURE_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, typename Lexer> + struct expression : qi::grammar<Iterator, ast::expression()> + { + typedef error_handler<typename Lexer::base_iterator_type, Iterator> + error_handler_type; + + expression(error_handler_type& error_handler, Lexer const& l); + + Lexer const& lexer; + + qi::rule<Iterator, ast::expression()> expr; + qi::rule<Iterator, ast::operand()> unary_expr, primary_expr; + qi::rule<Iterator, ast::function_call()> function_call; + qi::rule<Iterator, std::list<ast::expression>()> argument_list; + qi::rule<Iterator, std::string()> identifier; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/expression_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/expression_def.hpp new file mode 100644 index 00000000..795051eb --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/expression_def.hpp @@ -0,0 +1,94 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "expression.hpp" +#include "error_handler.hpp" +#include "annotation.hpp" +#include <boost/spirit/include/phoenix_function.hpp> +#include <boost/spirit/include/lex_plain_token.hpp> + +namespace client { namespace parser +{ + template <typename Iterator, typename Lexer> + expression<Iterator, Lexer>::expression( + error_handler<typename Lexer::base_iterator_type, Iterator>& error_handler + , Lexer const& l) + : expression::base_type(expr), lexer(l) + { + qi::_1_type _1; + qi::_2_type _2; + qi::_3_type _3; + qi::_4_type _4; + + qi::_val_type _val; + qi::tokenid_mask_type tokenid_mask; + + using qi::on_error; + using qi::on_success; + using qi::fail; + using boost::phoenix::function; + + typedef client::error_handler<typename Lexer::base_iterator_type, Iterator> + error_handler_type; + typedef function<error_handler_type> error_handler_function; + typedef function<client::annotation<Iterator> > annotation_function; + + /////////////////////////////////////////////////////////////////////// + // Main expression grammar + expr = + unary_expr + >> *(tokenid_mask(token_ids::op_binary) > unary_expr) + ; + + unary_expr = + primary_expr + | (tokenid_mask(token_ids::op_unary) > unary_expr) + ; + + primary_expr = + lexer.lit_uint + | function_call + | identifier + | lexer.true_or_false + | '(' > expr > ')' + ; + + function_call = + (identifier >> '(') + > argument_list + > ')' + ; + + argument_list = -(expr % ','); + + identifier = lexer.identifier; + + /////////////////////////////////////////////////////////////////////// + // Debugging and error handling and reporting support. + BOOST_SPIRIT_DEBUG_NODES( + (expr) + (unary_expr) + (primary_expr) + (function_call) + (argument_list) + (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)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/function.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/function.cpp new file mode 100644 index 00000000..98c8169b --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/function.cpp @@ -0,0 +1,20 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if defined(_MSC_VER) +# pragma warning(disable: 4345) +#endif + +#include "config.hpp" +#include "lexer.hpp" +#include "function_def.hpp" + +typedef std::string::const_iterator base_iterator_type; +typedef client::lexer::conjure_tokens<base_iterator_type> lexer_type; +typedef lexer_type::iterator_type iterator_type; + +template struct client::parser::function<iterator_type, lexer_type>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/function.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/function.hpp new file mode 100644 index 00000000..fc5263f1 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/function.hpp @@ -0,0 +1,36 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_FUNCTION_HPP) +#define BOOST_SPIRIT_CONJURE_FUNCTION_HPP + +#include "statement.hpp" + +namespace client { namespace parser +{ + /////////////////////////////////////////////////////////////////////////////// + // The function grammar + /////////////////////////////////////////////////////////////////////////////// + template <typename Iterator, typename Lexer> + struct function : qi::grammar<Iterator, ast::function()> + { + typedef error_handler<typename Lexer::base_iterator_type, Iterator> + error_handler_type; + + function(error_handler_type& error_handler, Lexer const& l); + + statement<Iterator, Lexer> body; + + qi::rule<Iterator, ast::identifier()> identifier; + qi::rule<Iterator, std::list<ast::identifier>()> argument_list; + qi::rule<Iterator, ast::function()> start; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/function_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/function_def.hpp new file mode 100644 index 00000000..72bab856 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/function_def.hpp @@ -0,0 +1,64 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "function.hpp" +#include "error_handler.hpp" +#include "annotation.hpp" + +namespace client { namespace parser +{ + template <typename Iterator, typename Lexer> + function<Iterator, Lexer>::function( + error_handler<typename Lexer::base_iterator_type, Iterator>& error_handler + , Lexer const& l) + : function::base_type(start), body(error_handler, l) + { + qi::_1_type _1; + qi::_2_type _2; + qi::_3_type _3; + qi::_4_type _4; + + qi::_val_type _val; + + using qi::on_error; + using qi::on_success; + using qi::fail; + using boost::phoenix::function; + + typedef client::error_handler<typename Lexer::base_iterator_type, Iterator> + error_handler_type; + typedef function<error_handler_type> error_handler_function; + typedef function<client::annotation<Iterator> > annotation_function; + + identifier = body.expr.identifier; + argument_list = -(identifier % ','); + + start = (l.token("void") | l.token("int")) + > identifier + > '(' > argument_list > ')' + > '{' > body > '}' + ; + + // Debugging and error handling and reporting support. + BOOST_SPIRIT_DEBUG_NODES( + (identifier) + (argument_list) + (start) + ); + + // Error handling: on error in start, call error_handler. + on_error<fail>(start, + error_handler_function(error_handler)( + "Error! Expecting ", _4, _3)); + + // Annotation: on success in start, call annotation. + on_success(identifier, + annotation_function(error_handler.iters)(_val, _1)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/ids.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/ids.hpp new file mode 100644 index 00000000..9446074c --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/ids.hpp @@ -0,0 +1,154 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_IDS_HPP) +#define BOOST_SPIRIT_CONJURE_IDS_HPP + +namespace client +{ + struct op_type + { + enum type + { + binary = 0x20000, + unary = 0x40000, + assign = 0x80000 + }; + }; + + struct op + { + enum type + { + // binary + comma, + assign, + plus_assign, + minus_assign, + times_assign, + divide_assign, + mod_assign, + bit_and_assign, + bit_xor_assign, + bit_or_assign, + shift_left_assign, + shift_right_assign, + logical_or, + logical_and, + bit_or, + bit_xor, + bit_and, + equal, + not_equal, + less, + less_equal, + greater, + greater_equal, + shift_left, + shift_right, + plus, + minus, + times, + divide, + mod, + + // unary + plus_plus, + minus_minus, + compl_, + not_, + }; + }; + + template <int type, int op> + struct make_op + { + static int const value = type + op; + }; + + template <op::type op> + struct unary_op : make_op<op_type::unary, op> {}; + + template <op::type op> + struct binary_op + : make_op<op_type::binary, op> {}; + + template <op::type op> + struct assign_op + : make_op<op_type::assign, op> {}; + + template <op::type op> + struct binary_or_unary_op + : make_op<op_type::unary | op_type::binary, op> {}; + + struct token_ids + { + enum type + { + // pseudo tags + invalid = -1, + op_binary = op_type::binary, + op_unary = op_type::unary, + op_assign = op_type::assign, + + // binary / unary operators with common tokens + // '+' and '-' can be binary or unary + // (the lexer cannot distinguish which) + plus = binary_or_unary_op<op::plus>::value, + minus = binary_or_unary_op<op::minus>::value, + + // binary operators + comma = binary_op<op::comma>::value, + assign = assign_op<op::assign>::value, + plus_assign = assign_op<op::plus_assign>::value, + minus_assign = assign_op<op::minus_assign>::value, + times_assign = assign_op<op::times_assign>::value, + divide_assign = assign_op<op::divide_assign>::value, + mod_assign = assign_op<op::mod_assign>::value, + bit_and_assign = assign_op<op::bit_and_assign>::value, + bit_xor_assign = assign_op<op::bit_xor_assign>::value, + bit_or_assign = assign_op<op::bit_or_assign>::value, + shift_left_assign = assign_op<op::shift_left_assign>::value, + shift_right_assign = assign_op<op::shift_right_assign>::value, + logical_or = binary_op<op::logical_or>::value, + logical_and = binary_op<op::logical_and>::value, + bit_or = binary_op<op::bit_or>::value, + bit_xor = binary_op<op::bit_xor>::value, + bit_and = binary_op<op::bit_and>::value, + equal = binary_op<op::equal>::value, + not_equal = binary_op<op::not_equal>::value, + less = binary_op<op::less>::value, + less_equal = binary_op<op::less_equal>::value, + greater = binary_op<op::greater>::value, + greater_equal = binary_op<op::greater_equal>::value, + shift_left = binary_op<op::shift_left>::value, + shift_right = binary_op<op::shift_right>::value, + times = binary_op<op::times>::value, + divide = binary_op<op::divide>::value, + mod = binary_op<op::mod>::value, + + // unary operators with overlaps + // '++' and '--' can be prefix or postfix + // (the lexer cannot distinguish which) + plus_plus = unary_op<op::plus_plus>::value, + minus_minus = unary_op<op::minus_minus>::value, + + // unary operators + compl_ = unary_op<op::compl_>::value, + not_ = unary_op<op::not_>::value, + + // misc tags + identifier = op::not_ + 1, + comment, + whitespace, + lit_uint, + true_or_false + }; + }; +} + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/lexer.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/lexer.cpp new file mode 100644 index 00000000..7ce7ec0c --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/lexer.cpp @@ -0,0 +1,17 @@ +/*============================================================================= + 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) +=============================================================================*/ +#if defined(_MSC_VER) +# pragma warning(disable: 4345) +#endif + +#include "config.hpp" +#include "lexer_def.hpp" + +typedef std::string::const_iterator base_iterator_type; +template client::lexer::conjure_tokens<base_iterator_type>::conjure_tokens(); +template bool client::lexer::conjure_tokens<base_iterator_type>::add_keyword( + std::string const&); diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/lexer.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/lexer.hpp new file mode 100644 index 00000000..c4945779 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/lexer.hpp @@ -0,0 +1,128 @@ +/*============================================================================= + 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) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_LEXER_HPP) +#define BOOST_SPIRIT_CONJURE_LEXER_HPP + +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/spirit/include/lex_lexertl_position_token.hpp> + +#include "config.hpp" +#include "ids.hpp" + +#if CONJURE_LEXER_STATIC_TABLES != 0 +#include <boost/spirit/include/lex_static_lexertl.hpp> +#include "conjure_static_lexer.hpp" +#elif CONJURE_LEXER_STATIC_SWITCH != 0 +#include <boost/spirit/include/lex_static_lexertl.hpp> +#include "conjure_static_switch_lexer.hpp" +#endif +#include <boost/assert.hpp> + +namespace client { namespace lexer +{ + namespace lex = boost::spirit::lex; + + /////////////////////////////////////////////////////////////////////////// + namespace detail + { + namespace lex = boost::spirit::lex; + + template <typename BaseIterator> + struct get_lexer_type + { + // Our token needs to be able to carry several token values: + // std::string, unsigned int, and bool + typedef boost::mpl::vector<std::string, unsigned int, bool> + token_value_types; + + // Using the position_token class as the token type to be returned + // from the lexer iterators allows to retain positional information + // as every token instance stores an iterator pair pointing to the + // matched input sequence. + typedef lex::lexertl::position_token< + BaseIterator, token_value_types, boost::mpl::false_ + > token_type; + +#if CONJURE_LEXER_DYNAMIC_TABLES != 0 + // use the lexer based on runtime generated DFA tables + typedef lex::lexertl::actor_lexer<token_type> type; +#elif CONJURE_LEXER_STATIC_TABLES != 0 + // use the lexer based on pre-generated static DFA tables + typedef lex::lexertl::static_actor_lexer< + token_type + , boost::spirit::lex::lexertl::static_::lexer_conjure_static + > type; +#elif CONJURE_LEXER_STATIC_SWITCH != 0 + // use the lexer based on pre-generated static code + typedef lex::lexertl::static_actor_lexer< + token_type + , boost::spirit::lex::lexertl::static_::lexer_conjure_static_switch + > type; +#else +#error "Configuration problem: please select exactly one type of lexer to build" +#endif + }; + } + + /////////////////////////////////////////////////////////////////////////// + template <typename BaseIterator> + struct conjure_tokens + : lex::lexer<typename detail::get_lexer_type<BaseIterator>::type> + { + private: + // get the type of any qi::raw_token(...) and qi::token(...) constructs + typedef typename boost::spirit::result_of::terminal< + boost::spirit::tag::raw_token(token_ids::type) + >::type raw_token_spec; + + typedef typename boost::spirit::result_of::terminal< + boost::spirit::tag::token(token_ids::type) + >::type token_spec; + + typedef std::map<std::string, token_ids::type> keyword_map_type; + + protected: + // add a keyword to the mapping table + bool add_keyword(std::string const& keyword); + + public: + typedef BaseIterator base_iterator_type; + + conjure_tokens(); + + // extract a raw_token(id) for the given registered keyword + raw_token_spec operator()(std::string const& kwd) const + { + namespace qi = boost::spirit::qi; + qi::raw_token_type raw_token; + + typename keyword_map_type::const_iterator it = keywords_.find(kwd); + BOOST_ASSERT(it != keywords_.end()); + return raw_token((it != keywords_.end()) ? (*it).second : token_ids::invalid); + } + + // extract a token(id) for the given registered keyword + token_spec token(std::string const& kwd) const + { + namespace qi = boost::spirit::qi; + qi::token_type token; + + typename keyword_map_type::const_iterator it = keywords_.find(kwd); + BOOST_ASSERT(it != keywords_.end()); + return token((it != keywords_.end()) ? (*it).second : token_ids::invalid); + } + + lex::token_def<std::string> identifier; + lex::token_def<unsigned int> lit_uint; + lex::token_def<bool> true_or_false; + keyword_map_type keywords_; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/lexer_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/lexer_def.hpp new file mode 100644 index 00000000..cab990f7 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/lexer_def.hpp @@ -0,0 +1,74 @@ +/*============================================================================= + 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) +=============================================================================*/ + +#include "lexer.hpp" + +namespace client { namespace lexer +{ + template <typename BaseIterator> + conjure_tokens<BaseIterator>::conjure_tokens() + : identifier("[a-zA-Z_][a-zA-Z_0-9]*", token_ids::identifier) + , lit_uint("[0-9]+", token_ids::lit_uint) + , true_or_false("true|false", token_ids::true_or_false) + { + lex::_pass_type _pass; + + this->self = lit_uint | true_or_false; + + add_keyword("void"); + add_keyword("int"); + add_keyword("if"); + add_keyword("else"); + add_keyword("while"); + add_keyword("return"); + + this->self.add + ("\\|\\|", token_ids::logical_or) + ("&&", token_ids::logical_and) + ("==", token_ids::equal) + ("!=", token_ids::not_equal) + ("<", token_ids::less) + ("<=", token_ids::less_equal) + (">", token_ids::greater) + (">=", token_ids::greater_equal) + ("\\+", token_ids::plus) + ("\\-", token_ids::minus) + ("\\*", token_ids::times) + ("\\/", token_ids::divide) + ("!", token_ids::not_) + ; + + this->self += lex::char_('(') | ')' | '{' | '}' | ',' | '=' | ';'; + + this->self += + identifier + | lex::string("\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/", token_ids::comment) + [ + lex::_pass = lex::pass_flags::pass_ignore + ] + | lex::string("[ \t\n\r]+", token_ids::whitespace) + [ + lex::_pass = lex::pass_flags::pass_ignore + ] + ; + } + + template <typename BaseIterator> + bool conjure_tokens<BaseIterator>::add_keyword(std::string const& keyword) + { + // add the token to the lexer + token_ids::type id = token_ids::type(this->get_next_id()); + this->self.add(keyword, id); + + // store the mapping for later retrieval + std::pair<typename keyword_map_type::iterator, bool> p = + keywords_.insert(typename keyword_map_type::value_type(keyword, id)); + + return p.second; + } +}} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/main.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/main.cpp new file mode 100644 index 00000000..3e4e83c9 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/main.cpp @@ -0,0 +1,131 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Not a calculator anymore, right? :-) +// +// [ JDG April 10, 2007 ] spirit2 +// [ JDG February 18, 2011 ] Pure attributes. No semantic actions. +// [ HK June 3, 2011 ] Adding lexer +// +/////////////////////////////////////////////////////////////////////////////// + +#include "config.hpp" +#include "function.hpp" +#include "vm.hpp" +#include "compiler.hpp" +#include "lexer.hpp" +#include <boost/lexical_cast.hpp> +#include <fstream> + +/////////////////////////////////////////////////////////////////////////////// +// 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 source_code; // 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(source_code)); + + typedef std::string::const_iterator base_iterator_type; + typedef client::lexer::conjure_tokens<base_iterator_type> lexer_type; + typedef lexer_type::iterator_type iterator_type; + + lexer_type lexer; // Our lexer + + base_iterator_type first = source_code.begin(); + base_iterator_type last = source_code.end(); + + iterator_type iter = lexer.begin(first, last); + iterator_type end = lexer.end(); + + client::vmachine vm; // Our virtual machine + client::ast::function_list ast; // Our AST + + client::error_handler<base_iterator_type, iterator_type> + error_handler(first, last); // Our error handler + client::parser::function<iterator_type, lexer_type> + function(error_handler, lexer); // Our parser + client::code_gen::compiler + compiler(error_handler); // Our compiler + + // note: we don't need a skipper + bool success = parse(iter, end, +function, ast); + + std::cout << "-------------------------\n"; + + if (success && iter == end) + { + if (compiler(ast)) + { + boost::shared_ptr<client::code_gen::function> + p = compiler.find_function("main"); + if (!p) + return 1; + + int nargs = argc-2; + if (p->nargs() != nargs) + { + std::cerr << "Error: main function requires " << p->nargs() << " arguments." << std::endl; + std::cerr << nargs << " supplied." << std::endl; + return 1; + } + + std::cout << "Success\n"; + std::cout << "-------------------------\n"; + std::cout << "Assembler----------------\n\n"; + compiler.print_assembler(); + + // Push the arguments into our stack + for (int i = 0; i < nargs; ++i) + vm.get_stack()[i] = boost::lexical_cast<int>(argv[i+2]); + + // Call the interpreter + int r = vm.execute(compiler.get_code()); + + std::cout << "-------------------------\n"; + std::cout << "Result: " << r << std::endl; + std::cout << "-------------------------\n\n"; + } + else + { + std::cout << "Compile failure\n"; + } + } + else + { + error_handler.dump_error_line(first); + std::cout << "Parse failure\n"; + } + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/statement.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/statement.cpp new file mode 100644 index 00000000..ca85a61c --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/statement.cpp @@ -0,0 +1,20 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if defined(_MSC_VER) +# pragma warning(disable: 4345) +#endif + +#include "config.hpp" +#include "lexer.hpp" +#include "statement_def.hpp" + +typedef std::string::const_iterator base_iterator_type; +typedef client::lexer::conjure_tokens<base_iterator_type> lexer_type; +typedef lexer_type::iterator_type iterator_type; + +template struct client::parser::statement<iterator_type, lexer_type>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/statement.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/statement.hpp new file mode 100644 index 00000000..208ca247 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/statement.hpp @@ -0,0 +1,42 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_STATEMENT_HPP) +#define BOOST_SPIRIT_CONJURE_STATEMENT_HPP + +#include "expression.hpp" + +namespace client { namespace parser +{ + /////////////////////////////////////////////////////////////////////////////// + // The statement grammar + /////////////////////////////////////////////////////////////////////////////// + template <typename Iterator, typename Lexer> + struct statement : qi::grammar<Iterator, ast::statement_list()> + { + typedef error_handler<typename Lexer::base_iterator_type, Iterator> + error_handler_type; + + statement(error_handler_type& error_handler, Lexer const& l); + + expression<Iterator, Lexer> expr; + + qi::rule<Iterator, ast::statement_list()> + statement_list, compound_statement; + + qi::rule<Iterator, ast::statement()> statement_; + qi::rule<Iterator, ast::variable_declaration()> variable_declaration; + qi::rule<Iterator, ast::assignment()> assignment; + qi::rule<Iterator, ast::if_statement()> if_statement; + qi::rule<Iterator, ast::while_statement()> while_statement; + qi::rule<Iterator, ast::return_statement()> return_statement; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/statement_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/statement_def.hpp new file mode 100644 index 00000000..ab937085 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/statement_def.hpp @@ -0,0 +1,123 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "statement.hpp" +#include "error_handler.hpp" +#include "annotation.hpp" + +namespace client { namespace parser +{ + template <typename Iterator, typename Lexer> + statement<Iterator, Lexer>::statement( + error_handler<typename Lexer::base_iterator_type, Iterator>& error_handler + , Lexer const& l) + : statement::base_type(statement_list), expr(error_handler, l) + { + qi::_1_type _1; + qi::_2_type _2; + qi::_3_type _3; + qi::_4_type _4; + + qi::_val_type _val; + + using qi::on_error; + using qi::on_success; + using qi::fail; + using boost::phoenix::function; + + typedef client::error_handler<typename Lexer::base_iterator_type, Iterator> + error_handler_type; + typedef function<error_handler_type> error_handler_function; + typedef function<client::annotation<Iterator> > annotation_function; + + statement_list = + +statement_ + ; + + statement_ = + variable_declaration + | assignment + | compound_statement + | if_statement + | while_statement + | return_statement + ; + + variable_declaration = + l("int") + > expr.identifier + > -('=' > expr) + > ';' + ; + + assignment = + expr.identifier + > '=' + > expr + > ';' + ; + + if_statement = + l("if") + > '(' + > expr + > ')' + > statement_ + > + -( + l("else") + > statement_ + ) + ; + + while_statement = + l("while") + > '(' + > expr + > ')' + > statement_ + ; + + compound_statement = + '{' >> -statement_list >> '}' + ; + + return_statement = + l("return") + > -expr + > ';' + ; + + // Debugging and error handling and reporting support. + BOOST_SPIRIT_DEBUG_NODES( + (statement_list) + (statement_) + (variable_declaration) + (assignment) + (if_statement) + (while_statement) + (compound_statement) + (return_statement) + ); + + // 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 variable_declaration, + // assignment and return_statement, call annotation. + on_success(variable_declaration, + annotation_function(error_handler.iters)(_val, _1)); + on_success(assignment, + annotation_function(error_handler.iters)(_val, _1)); + on_success(return_statement, + annotation_function(error_handler.iters)(_val, _1)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/vm.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/vm.cpp new file mode 100644 index 00000000..af68f523 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/vm.cpp @@ -0,0 +1,160 @@ +/*============================================================================= + 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 "config.hpp" +#include "vm.hpp" + +#if defined(_MSC_VER) +# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' + // (performance warning) +#endif + +namespace client +{ + int vmachine::execute( + std::vector<int> const& code + , std::vector<int>::const_iterator pc + , std::vector<int>::iterator frame_ptr + ) + { + std::vector<int>::iterator stack_ptr = frame_ptr; + + while (true) + { + switch (*pc++) + { + case op_neg: + stack_ptr[-1] = -stack_ptr[-1]; + break; + + case op_not: + stack_ptr[-1] = !bool(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_eq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] == stack_ptr[0]); + break; + + case op_neq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] != stack_ptr[0]); + break; + + case op_lt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] < stack_ptr[0]); + break; + + case op_lte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] <= stack_ptr[0]); + break; + + case op_gt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] > stack_ptr[0]); + break; + + case op_gte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] >= stack_ptr[0]); + break; + + case op_and: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) && bool(stack_ptr[0]); + break; + + case op_or: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) || bool(stack_ptr[0]); + break; + + case op_load: + *stack_ptr++ = frame_ptr[*pc++]; + break; + + case op_store: + --stack_ptr; + frame_ptr[*pc++] = stack_ptr[0]; + break; + + case op_int: + *stack_ptr++ = *pc++; + break; + + case op_true: + *stack_ptr++ = true; + break; + + case op_false: + *stack_ptr++ = false; + break; + + case op_jump: + pc += *pc; + break; + + case op_jump_if: + if (!bool(stack_ptr[-1])) + pc += *pc; + else + ++pc; + --stack_ptr; + break; + + case op_stk_adj: + stack_ptr += *pc++; + break; + + case op_call: + { + int nargs = *pc++; + int jump = *pc++; + + // a function call is a recursive call to execute + int r = execute( + code + , code.begin() + jump + , stack_ptr - nargs + ); + + // cleanup after return from function + stack_ptr[-nargs] = r; // get return value + stack_ptr -= (nargs - 1); // the stack will now contain + // the return value + } + break; + + case op_return: + return stack_ptr[-1]; + } + } + } +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/vm.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/vm.hpp new file mode 100644 index 00000000..0362eaf8 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure2/vm.hpp @@ -0,0 +1,82 @@ +/*============================================================================= + 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_CONJURE_VM_HPP) +#define BOOST_SPIRIT_CONJURE_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_not, // boolean negate the top stack entry + op_eq, // compare the top two stack entries for == + op_neq, // compare the top two stack entries for != + op_lt, // compare the top two stack entries for < + op_lte, // compare the top two stack entries for <= + op_gt, // compare the top two stack entries for > + op_gte, // compare the top two stack entries for >= + + op_and, // logical and top two stack entries + op_or, // logical or top two stack entries + + op_load, // load a variable + op_store, // store a variable + + op_int, // push constant integer into the stack + op_true, // push constant 0 into the stack + op_false, // push constant 1 into the stack + + op_jump_if, // jump to a relative position in the code if top stack + // evaluates to false + op_jump, // jump to a relative position in the code + + op_stk_adj, // adjust the stack (for args and locals) + op_call, // function call + op_return // return from function + }; + + class vmachine + { + public: + + vmachine(unsigned stackSize = 4096) + : stack(stackSize) + { + } + + int execute(std::vector<int> const& code) + { + return execute(code, code.begin(), stack.begin()); + } + + std::vector<int> const& get_stack() const { return stack; }; + std::vector<int>& get_stack() { return stack; }; + + private: + + int execute( + std::vector<int> const& code // the program code + , std::vector<int>::const_iterator pc // program counter + , std::vector<int>::iterator frame_ptr // start of arguments and locals + ); + + std::vector<int> stack; + }; +} + +#endif + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/annotation.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/annotation.hpp new file mode 100644 index 00000000..3b876431 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/annotation.hpp @@ -0,0 +1,141 @@ +/*============================================================================= + 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_CONJURE_ANNOTATION_HPP) +#define BOOST_SPIRIT_CONJURE_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. + /////////////////////////////////////////////////////////////////////////////// + struct set_annotation_id + { + typedef void result_type; + + int id; + set_annotation_id(int id) : id(id) {} + + void operator()(ast::function_call& x) const + { + x.function_name.id = id; + } + + template <typename T> + void dispatch(T& x, boost::mpl::true_) const + { + x.id = id; + } + + template <typename T> + void dispatch(T& x, boost::mpl::false_) const + { + // no-op + } + + template <typename T> + void operator()(T& x) const + { + typename boost::is_base_of<ast::tagged, T> is_tagged; + dispatch(x, is_tagged); + } + }; + + struct get_annotation_id + { + typedef int result_type; + + int operator()(ast::function_call& x) const + { + return x.function_name.id; + } + + template <typename T> + int dispatch(T& x, boost::mpl::true_) const + { + return x.id; + } + + template <typename T> + int dispatch(T& x, boost::mpl::false_) const + { + return -1; + } + + template <typename T> + int operator()(T& x) const + { + typename boost::is_base_of<ast::tagged, T> is_tagged; + return dispatch(x, is_tagged); + } + }; + + template <typename Iterator> + struct annotation + { + template <typename, typename> + struct result { typedef void type; }; + + std::vector<Iterator>& iters; + annotation(std::vector<Iterator>& iters) + : iters(iters) {} + + void operator()(ast::operand& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + boost::apply_visitor(set_annotation_id(id), ast); + ast.id = id; + } + + void operator()(ast::primary_expr& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + boost::apply_visitor(set_annotation_id(id), ast); + ast.id = id; + } + + void operator()(ast::variable_declaration& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.lhs.id = id; + } + + void operator()(ast::assignment& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.lhs.id = id; + } + + void operator()(ast::return_statement& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.id = id; + } + + void operator()(ast::identifier& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.id = id; + } + }; +} + +#endif + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/ast.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/ast.hpp new file mode 100644 index 00000000..a77cb0ed --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/ast.hpp @@ -0,0 +1,240 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_AST_HPP) +#define BOOST_SPIRIT_CONJURE_AST_HPP + +#include <boost/config/warning_disable.hpp> +#include <boost/variant/recursive_variant.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/fusion/include/io.hpp> +#include <boost/spirit/include/support_extended_variant.hpp> +#include <boost/spirit/include/support_attributes.hpp> +#include <boost/optional.hpp> +#include <list> + +#include "ids.hpp" + +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 unary_expr; + struct function_call; + struct expression; + + struct identifier : tagged + { + identifier(std::string const& name = "") : name(name) {} + std::string name; + }; + + struct primary_expr : + tagged, + boost::spirit::extended_variant< + nil + , bool + , unsigned int + , identifier + , boost::recursive_wrapper<expression> + > + { + primary_expr() : base_type() {} + primary_expr(bool val) : base_type(val) {} + primary_expr(unsigned int val) : base_type(val) {} + primary_expr(identifier const& val) : base_type(val) {} + primary_expr(expression const& val) : base_type(val) {} + primary_expr(primary_expr const& rhs) + : base_type(rhs.get()) {} + }; + + struct operand : + tagged, + boost::spirit::extended_variant< + nil + , primary_expr + , boost::recursive_wrapper<unary_expr> + , boost::recursive_wrapper<function_call> + > + { + operand() : base_type() {} + operand(primary_expr const& val) : base_type(val) {} + operand(unary_expr const& val) : base_type(val) {} + operand(function_call const& val) : base_type(val) {} + operand(operand const& rhs) + : base_type(rhs.get()) {} + }; + + struct unary_expr : tagged + { + token_ids::type operator_; + operand operand_; + }; + + struct operation + { + token_ids::type operator_; + operand operand_; + }; + + struct function_call + { + identifier function_name; + std::list<expression> args; + }; + + struct expression + { + operand first; + std::list<operation> rest; + }; + + struct assignment + { + identifier lhs; + token_ids::type operator_; + expression rhs; + }; + + struct variable_declaration + { + identifier lhs; + boost::optional<expression> rhs; + }; + + struct if_statement; + struct while_statement; + struct statement_list; + struct return_statement; + + typedef boost::variant< + nil + , variable_declaration + , assignment + , boost::recursive_wrapper<if_statement> + , boost::recursive_wrapper<while_statement> + , boost::recursive_wrapper<return_statement> + , boost::recursive_wrapper<statement_list> + , boost::recursive_wrapper<expression> + > + statement; + + struct statement_list : std::list<statement> {}; + + struct if_statement + { + expression condition; + statement then; + boost::optional<statement> else_; + }; + + struct while_statement + { + expression condition; + statement body; + }; + + struct return_statement : tagged + { + boost::optional<expression> expr; + }; + + struct function + { + std::string return_type; + identifier function_name; + std::list<identifier> args; + boost::optional<statement_list> body; + }; + + typedef std::list<function> function_list; + + // print functions for debugging + inline std::ostream& operator<<(std::ostream& out, nil) + { + out << "nil"; return out; + } + + inline std::ostream& operator<<(std::ostream& out, identifier const& id) + { + out << id.name; return out; + } +}} + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::unary_expr, + (client::token_ids::type, operator_) + (client::ast::operand, operand_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::operation, + (client::token_ids::type, operator_) + (client::ast::operand, operand_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::function_call, + (client::ast::identifier, function_name) + (std::list<client::ast::expression>, args) +) + +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::identifier, lhs) + (boost::optional<client::ast::expression>, rhs) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::assignment, + (client::ast::identifier, lhs) + (client::token_ids::type, operator_) + (client::ast::expression, rhs) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::if_statement, + (client::ast::expression, condition) + (client::ast::statement, then) + (boost::optional<client::ast::statement>, else_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::while_statement, + (client::ast::expression, condition) + (client::ast::statement, body) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::return_statement, + (boost::optional<client::ast::expression>, expr) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::function, + (std::string, return_type) + (client::ast::identifier, function_name) + (std::list<client::ast::identifier>, args) + (boost::optional<client::ast::statement_list>, body) +) + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.cpp new file mode 100644 index 00000000..e36ad770 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.cpp @@ -0,0 +1,1141 @@ +/*============================================================================= + 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 "config.hpp" +#include "compiler.hpp" +#include "annotation.hpp" +#include "vm.hpp" + +#include <boost/foreach.hpp> +#include <boost/variant/apply_visitor.hpp> +#include <boost/range/adaptor/transformed.hpp> +#include <boost/assert.hpp> +#include <set> + +namespace client { namespace code_gen +{ + value::value() + : v(0), + is_lvalue_(false), + builder(0) + {} + + value::value( + llvm::Value* v, + bool is_lvalue_, + llvm::IRBuilder<>* builder) + : v(v), + is_lvalue_(is_lvalue_), + builder(builder) + {} + + value::value(value const& rhs) + : v(rhs.v), + is_lvalue_(rhs.is_lvalue_), + builder(rhs.builder) + {} + + bool value::is_lvalue() const + { + return is_lvalue_; + } + + bool value::is_valid() const + { + return v != 0; + } + + value::operator bool() const + { + return v != 0; + } + + void value::name(char const* id) + { + v->setName(id); + } + + void value::name(std::string const& id) + { + v->setName(id); + } + + value::operator llvm::Value*() const + { + if (is_lvalue_) + { + BOOST_ASSERT(builder != 0); + return builder->CreateLoad(v, v->getName()); + } + return v; + } + + value& value::operator=(value const& rhs) + { + v = rhs.v; + is_lvalue_ = rhs.is_lvalue_; + builder = rhs.builder; + return *this; + } + + value& value::assign(value const& rhs) + { + BOOST_ASSERT(is_lvalue()); + BOOST_ASSERT(builder != 0); + builder->CreateStore(rhs, v); + return *this; + } + + value operator-(value a) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateNeg(a, "neg_tmp"), + false, a.builder + ); + } + + value operator!(value a) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateNot(a, "not_tmp"), + false, a.builder + ); + } + + value operator+(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateAdd(a, b, "add_tmp"), + false, a.builder + ); + } + + value operator-(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateSub(a, b, "sub_tmp"), + false, a.builder + ); + } + + value operator*(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateMul(a, b, "mul_tmp"), + false, a.builder + ); + } + + value operator/(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateSDiv(a, b, "div_tmp"), + false, a.builder + ); + } + + value operator%(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateSRem(a, b, "mod_tmp"), + false, a.builder + ); + } + + value operator&(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateAnd(a, b, "and_tmp"), + false, a.builder + ); + } + + value operator|(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateOr(a, b, "or_tmp"), + false, a.builder + ); + } + + value operator^(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateXor(a, b, "xor_tmp"), + false, a.builder + ); + } + + value operator<<(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateShl(a, b, "shl_tmp"), + false, a.builder + ); + } + + value operator>>(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateLShr(a, b, "shr_tmp"), + false, a.builder + ); + } + + value operator==(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateICmpEQ(a, b, "eq_tmp"), + false, a.builder + ); + } + + value operator!=(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateICmpNE(a, b, "ne_tmp"), + false, a.builder + ); + } + + value operator<(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateICmpSLT(a, b, "slt_tmp"), + false, a.builder + ); + } + + value operator<=(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateICmpSLE(a, b, "sle_tmp"), + false, a.builder + ); + } + + value operator>(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateICmpSGT(a, b, "sgt_tmp"), + false, a.builder + ); + } + + value operator>=(value a, value b) + { + BOOST_ASSERT(a.builder != 0); + return value( + a.builder->CreateICmpSGE(a, b, "sge_tmp"), + false, a.builder + ); + } + + struct function::to_value + { + typedef value result_type; + llvm_compiler* c; + + to_value(llvm_compiler* c = 0) : c(c) {} + + value operator()(llvm::Value& v) const + { + return c->val(&v); + } + }; + + bool basic_block::has_terminator() const + { + return b->getTerminator() != 0; + } + + bool basic_block::is_valid() const + { + return b != 0; + } + + function::operator llvm::Function*() const + { + return f; + } + + std::size_t function::arg_size() const + { + return f->arg_size(); + } + + void function::add(basic_block const& b) + { + f->getBasicBlockList().push_back(b); + } + + void function::erase_from_parent() + { + f->eraseFromParent(); + } + + basic_block function::last_block() + { + return &f->getBasicBlockList().back(); + } + + bool function::empty() const + { + return f->empty(); + } + + std::string function::name() const + { + return f->getName(); + } + + function::arg_range function::args() const + { + BOOST_ASSERT(c != 0); + arg_val_iterator first(f->arg_begin(), to_value()); + arg_val_iterator last(f->arg_end(), to_value()); + return arg_range(first, last); + } + + bool function::is_valid() const + { + return f != 0; + } + + void function::verify() const + { + llvm::verifyFunction(*f); + } + + value llvm_compiler::val(unsigned int x) + { + return value( + llvm::ConstantInt::get(context(), llvm::APInt(int_size, x)), + false, &llvm_builder); + } + + value llvm_compiler::val(int x) + { + return value( + llvm::ConstantInt::get(context(), llvm::APInt(int_size, x)), + false, &llvm_builder); + } + + value llvm_compiler::val(bool x) + { + return value( + llvm::ConstantInt::get(context(), llvm::APInt(1, x)), + false, &llvm_builder); + } + + value llvm_compiler::val(llvm::Value* v) + { + return value(v, false, &llvm_builder); + } + + namespace + { + // Create an alloca instruction in the entry block of + // the function. This is used for mutable variables etc. + llvm::AllocaInst* + make_entry_block_alloca( + llvm::Function* f, + char const* name, + llvm::LLVMContext& context) + { + llvm::IRBuilder<> builder( + &f->getEntryBlock(), + f->getEntryBlock().begin()); + + return builder.CreateAlloca( + llvm::Type::getIntNTy(context, int_size), 0, name); + } + } + + value llvm_compiler::var(char const* name) + { + llvm::Function* f = llvm_builder.GetInsertBlock()->getParent(); + llvm::IRBuilder<> builder( + &f->getEntryBlock(), + f->getEntryBlock().begin()); + + llvm::AllocaInst* alloca = builder.CreateAlloca( + llvm::Type::getIntNTy(context(), int_size), 0, name); + + return value(alloca, true, &llvm_builder); + } + + struct value::to_llvm_value + { + typedef llvm::Value* result_type; + llvm::Value* operator()(value const& x) const + { + return x; + } + }; + + template <typename C> + llvm::Value* llvm_compiler::call_impl( + function callee, + C const& args_) + { + // Sigh. LLVM requires CreateCall arguments to be random access. + // It would have been better if it can accept forward iterators. + // I guess it needs the arguments to be in contiguous memory. + // So, we have to put the args into a temporary std::vector. + std::vector<llvm::Value*> args( + args_.begin(), args_.end()); + + // Check the args for null values. We can't have null values. + // Return 0 if we find one to flag error. + BOOST_FOREACH(llvm::Value* arg, args) + { + if (arg == 0) + return 0; + } + + return llvm_builder.CreateCall( + callee, args.begin(), args.end(), "call_tmp"); + } + + template <typename Container> + value llvm_compiler::call( + function callee, + Container const& args) + { + llvm::Value* call = call_impl( + callee, + args | boost::adaptors::transformed(value::to_llvm_value())); + + if (call == 0) + return val(); + return value(call, false, &llvm_builder); + } + + function llvm_compiler::get_function(char const* name) + { + return function(vm.module()->getFunction(name), this); + } + + function llvm_compiler::get_current_function() + { + // get the current function + return function(llvm_builder.GetInsertBlock()->getParent(), this); + } + + function llvm_compiler::declare_function( + bool void_return + , std::string const& name + , std::size_t nargs) + { + llvm::Type const* int_type = + llvm::Type::getIntNTy(context(), int_size); + llvm::Type const* void_type = llvm::Type::getVoidTy(context()); + + std::vector<llvm::Type const*> ints(nargs, int_type); + llvm::Type const* return_type = void_return ? void_type : int_type; + + llvm::FunctionType* function_type = + llvm::FunctionType::get(void_return ? void_type : int_type, ints, false); + + return function(llvm::Function::Create( + function_type, llvm::Function::ExternalLinkage, + name, vm.module()), this); + } + + basic_block llvm_compiler::make_basic_block( + char const* name + , function parent + , basic_block before) + { + return llvm::BasicBlock::Create(context(), name, parent, before); + } + + value llvm_compiler::var(std::string const& name) + { + return var(name.c_str()); + } + + function llvm_compiler::get_function(std::string const& name) + { + return get_function(name.c_str()); + } + + basic_block llvm_compiler::get_insert_block() + { + return llvm_builder.GetInsertBlock(); + } + + void llvm_compiler::set_insert_point(basic_block b) + { + llvm_builder.SetInsertPoint(b); + } + + void llvm_compiler::conditional_branch( + value cond, basic_block true_br, basic_block false_br) + { + llvm_builder.CreateCondBr(cond, true_br, false_br); + } + + void llvm_compiler::branch(basic_block b) + { + llvm_builder.CreateBr(b); + } + + void llvm_compiler::return_() + { + llvm_builder.CreateRetVoid(); + } + + void llvm_compiler::return_(value v) + { + llvm_builder.CreateRet(v); + } + + void llvm_compiler::optimize_function(function f) + { + // Optimize the function. + fpm.run(*f); + } + + void llvm_compiler::init_fpm() + { + // Set up the optimizer pipeline. Start with registering info about how the + // target lays out data structures. + fpm.add(new llvm::TargetData(*vm.execution_engine()->getTargetData())); + // Provide basic AliasAnalysis support for GVN. + fpm.add(llvm::createBasicAliasAnalysisPass()); + // Promote allocas to registers. + fpm.add(llvm::createPromoteMemoryToRegisterPass()); + // Do simple "peephole" optimizations and bit-twiddling optzns. + fpm.add(llvm::createInstructionCombiningPass()); + // Reassociate expressions. + fpm.add(llvm::createReassociatePass()); + // Eliminate Common SubExpressions. + fpm.add(llvm::createGVNPass()); + // Simplify the control flow graph (deleting unreachable blocks, etc). + fpm.add(llvm::createCFGSimplificationPass()); + + fpm.doInitialization(); + } + + value compiler::operator()(unsigned int x) + { + return val(x); + } + + value compiler::operator()(bool x) + { + return val(x); + } + + value compiler::operator()(ast::primary_expr const& x) + { + return boost::apply_visitor(*this, x.get()); + } + + value compiler::operator()(ast::identifier const& x) + { + // Look this variable up in the function. + if (locals.find(x.name) == locals.end()) + { + error_handler(x.id, "Undeclared variable: " + x.name); + return val(); + } + return locals[x.name]; + } + + value compiler::operator()(ast::unary_expr const& x) + { + value operand = boost::apply_visitor(*this, x.operand_); + if (!operand.is_valid()) + return val(); + + switch (x.operator_) + { + case token_ids::compl_: return operand ^ val(-1); + case token_ids::minus: return -operand; + case token_ids::not_: return !operand; + case token_ids::plus: return operand; + case token_ids::plus_plus: + { + if (!operand.is_lvalue()) + { + error_handler(x.id, "++ needs an var"); + return val(); + } + + value r = operand + val(1); + operand.assign(r); + return operand; + } + case token_ids::minus_minus: + { + if (!operand.is_lvalue()) + { + error_handler(x.id, "-- needs an var"); + return val(); + } + + value r = operand - val(1); + operand.assign(r); + return operand; + } + default: + BOOST_ASSERT(0); + return val(); + } + } + + namespace + { + struct compile_args + { + compiler& c; + compile_args(compiler& c) : c(c) {} + + typedef value result_type; + value operator()(ast::expression const& expr) const + { + return c(expr); + } + }; + } + + value compiler::operator()(ast::function_call const& x) + { + function callee = get_function(x.function_name.name); + if (!callee.is_valid()) + { + error_handler(x.function_name.id, + "Function not found: " + x.function_name.name); + return val(); + } + + if (callee.arg_size() != x.args.size()) + { + error_handler(x.function_name.id, + "Wrong number of arguments: " + x.function_name.name); + return val(); + } + + return call(callee, + x.args | boost::adaptors::transformed(compile_args(*this))); + } + + namespace + { + int precedence[] = { + // precedence 1 + 1, // op_comma + + // precedence 2 + 2, // op_assign + 2, // op_plus_assign + 2, // op_minus_assign + 2, // op_times_assign + 2, // op_divide_assign + 2, // op_mod_assign + 2, // op_bit_and_assign + 2, // op_bit_xor_assign + 2, // op_bitor_assign + 2, // op_shift_left_assign + 2, // op_shift_right_assign + + // precedence 3 + 3, // op_logical_or + + // precedence 4 + 4, // op_logical_and + + // precedence 5 + 5, // op_bit_or + + // precedence 6 + 6, // op_bit_xor + + // precedence 7 + 7, // op_bit_and + + // precedence 8 + 8, // op_equal + 8, // op_not_equal + + // precedence 9 + 9, // op_less + 9, // op_less_equal + 9, // op_greater + 9, // op_greater_equal + + // precedence 10 + 10, // op_shift_left + 10, // op_shift_right + + // precedence 11 + 11, // op_plus + 11, // op_minus + + // precedence 12 + 12, // op_times + 12, // op_divide + 12 // op_mod + }; + } + + inline int precedence_of(token_ids::type op) + { + return precedence[op & 0xFF]; + } + + value compiler::compile_binary_expression( + value lhs, value rhs, token_ids::type op) + { + switch (op) + { + case token_ids::plus: return lhs + rhs; + case token_ids::minus: return lhs - rhs; + case token_ids::times: return lhs * rhs; + case token_ids::divide: return lhs / rhs; + case token_ids::mod: return lhs % rhs; + + case token_ids::logical_or: + case token_ids::bit_or: return lhs | rhs; + + case token_ids::logical_and: + case token_ids::bit_and: return lhs & rhs; + + case token_ids::bit_xor: return lhs ^ rhs; + case token_ids::shift_left: return lhs << rhs; + case token_ids::shift_right: return lhs >> rhs; + + case token_ids::equal: return lhs == rhs; + case token_ids::not_equal: return lhs != rhs; + case token_ids::less: return lhs < rhs; + case token_ids::less_equal: return lhs <= rhs; + case token_ids::greater: return lhs > rhs; + case token_ids::greater_equal: return lhs >= rhs; + + default: BOOST_ASSERT(0); return val(); + } + } + + // The Shunting-yard algorithm + value compiler::compile_expression( + int min_precedence, + value lhs, + std::list<ast::operation>::const_iterator& rest_begin, + std::list<ast::operation>::const_iterator rest_end) + { + while ((rest_begin != rest_end) && + (precedence_of(rest_begin->operator_) >= min_precedence)) + { + token_ids::type op = rest_begin->operator_; + value rhs = boost::apply_visitor(*this, rest_begin->operand_); + if (!rhs.is_valid()) + return val(); + ++rest_begin; + + while ((rest_begin != rest_end) && + (precedence_of(rest_begin->operator_) > precedence_of(op))) + { + token_ids::type next_op = rest_begin->operator_; + rhs = compile_expression( + precedence_of(next_op), rhs, rest_begin, rest_end); + } + + lhs = compile_binary_expression(lhs, rhs, op); + } + return lhs; + } + + value compiler::operator()(ast::expression const& x) + { + value lhs = boost::apply_visitor(*this, x.first); + if (!lhs.is_valid()) + return val(); + std::list<ast::operation>::const_iterator rest_begin = x.rest.begin(); + return compile_expression(0, lhs, rest_begin, x.rest.end()); + } + + value compiler::operator()(ast::assignment const& x) + { + if (locals.find(x.lhs.name) == locals.end()) + { + error_handler(x.lhs.id, "Undeclared variable: " + x.lhs.name); + return val(); + } + + value lhs = locals[x.lhs.name]; + value rhs = (*this)(x.rhs); + if (!rhs.is_valid()) + return val(); + + if (x.operator_ == token_ids::assign) + { + lhs.assign(rhs); + return lhs; + } + + value result; + switch (x.operator_) + { + case token_ids::plus_assign: result = lhs + rhs; break; + case token_ids::minus_assign: result = lhs - rhs; break; + case token_ids::times_assign: result = lhs * rhs; break; + case token_ids::divide_assign: result = lhs / rhs; break; + case token_ids::mod_assign: result = lhs % rhs; break; + case token_ids::bit_and_assign: result = lhs & rhs; break; + case token_ids::bit_xor_assign: result = lhs ^ rhs; break; + case token_ids::bit_or_assign: result = lhs | rhs; break; + case token_ids::shift_left_assign: result = lhs << rhs; break; + case token_ids::shift_right_assign: result = lhs >> rhs; break; + default: BOOST_ASSERT(0); return val(); + } + + lhs.assign(result); + return lhs; + } + + bool compiler::operator()(ast::variable_declaration const& x) + { + if (locals.find(x.lhs.name) != locals.end()) + { + error_handler(x.lhs.id, "Duplicate variable: " + x.lhs.name); + return false; + } + + value init; + std::string const& name = x.lhs.name; + + if (x.rhs) // if there's an RHS initializer + { + init = (*this)(*x.rhs); + if (!init.is_valid()) // don't add the variable if the RHS fails + return false; + } + + value var_ = var(name.c_str()); + if (init.is_valid()) + var_.assign(init); + + // Remember this binding. + locals[name] = var_; + return true; + } + + struct compiler::statement_compiler : compiler + { + typedef bool result_type; + }; + + compiler::statement_compiler& compiler::as_statement() + { + return *static_cast<statement_compiler*>(this); + } + + bool compiler::operator()(ast::statement const& x) + { + if (boost::get<ast::nil>(&x) != 0) // empty statement + return true; + return boost::apply_visitor(as_statement(), x); + } + + bool compiler::operator()(ast::statement_list const& x) + { + BOOST_FOREACH(ast::statement const& s, x) + { + if (!(*this)(s)) + return false; + } + return true; + } + + bool compiler::operator()(ast::if_statement const& x) + { + value condition = (*this)(x.condition); + if (!condition.is_valid()) + return false; + + function f = get_current_function(); + + // Create blocks for the then and else cases. Insert the 'then' block at the + // end of the function. + basic_block then_block = make_basic_block("if.then", f); + basic_block else_block; + basic_block exit_block; + + if (x.else_) + { + else_block = make_basic_block("if.else"); + conditional_branch(condition, then_block, else_block); + } + else + { + exit_block = make_basic_block("if.end"); + conditional_branch(condition, then_block, exit_block); + } + + // Emit then value. + set_insert_point(then_block); + if (!(*this)(x.then)) + return false; + if (!then_block.has_terminator()) + { + if (!exit_block.is_valid()) + exit_block = make_basic_block("if.end"); + branch(exit_block); + } + // Codegen of 'then' can change the current block, update then_block + then_block = get_insert_block(); + + if (x.else_) + { + // Emit else block. + f.add(else_block); + set_insert_point(else_block); + if (!(*this)(*x.else_)) + return false; + if (!else_block.has_terminator()) + { + if (!exit_block.is_valid()) + exit_block = make_basic_block("if.end"); + branch(exit_block); + } + // Codegen of 'else' can change the current block, update else_block + else_block = get_insert_block(); + } + + if (exit_block.is_valid()) + { + // Emit exit block + f.add(exit_block); + set_insert_point(exit_block); + } + return true; + } + + bool compiler::operator()(ast::while_statement const& x) + { + function f = get_current_function(); + + basic_block cond_block = make_basic_block("while.cond", f); + basic_block body_block = make_basic_block("while.body"); + basic_block exit_block = make_basic_block("while.end"); + + branch(cond_block); + set_insert_point(cond_block); + value condition = (*this)(x.condition); + if (!condition.is_valid()) + return false; + conditional_branch(condition, body_block, exit_block); + f.add(body_block); + set_insert_point(body_block); + + if (!(*this)(x.body)) + return false; + + if (!body_block.has_terminator()) + branch(cond_block); // loop back + + // Emit exit block + f.add(exit_block); + set_insert_point(exit_block); + + return true; + } + + bool compiler::operator()(ast::return_statement const& x) + { + if (void_return) + { + if (x.expr) + { + error_handler( + x.id, "'void' function returning a value: "); + return false; + } + } + else + { + if (!x.expr) + { + error_handler( + x.id, current_function_name + + " function must return a value: "); + return false; + } + } + + if (x.expr) + { + value return_val = (*this)(*x.expr); + if (!return_val.is_valid()) + return false; + return_var.assign(return_val); + } + + branch(return_block); + return true; + } + + function compiler::function_decl(ast::function const& x) + { + void_return = x.return_type == "void"; + current_function_name = x.function_name.name; + + function f = + declare_function( + void_return + , current_function_name + , x.args.size()); + + // If function conflicted, the function already exixts. If it has a + // body, don't allow redefinition or reextern. + if (f.name() != current_function_name) + { + // Delete the one we just made and get the existing one. + f.erase_from_parent(); + f = get_function(current_function_name); + + // If function already has a body, reject this. + if (!f.empty()) + { + error_handler( + x.function_name.id, + "Duplicate function: " + x.function_name.name); + return function(); + } + + // If function took a different number of args, reject. + if (f.arg_size() != x.args.size()) + { + error_handler( + x.function_name.id, + "Redefinition of function with different # args: " + + x.function_name.name); + return function(); + } + + // Set names for all arguments. + function::arg_range rng = f.args(); + function::arg_range::const_iterator iter = rng.begin(); + BOOST_FOREACH(ast::identifier const& arg, x.args) + { + iter->name(arg.name); + ++iter; + } + } + return f; + } + + void compiler::function_allocas(ast::function const& x, function f) + { + // Create variables for each argument and register the + // argument in the symbol table so that references to it will succeed. + function::arg_range rng = f.args(); + function::arg_range::const_iterator iter = rng.begin(); + BOOST_FOREACH(ast::identifier const& arg, x.args) + { + // Create an arg_ for this variable. + value arg_ = var(arg.name); + + // Store the initial value into the arg_. + arg_.assign(*iter); + + // Add arguments to variable symbol table. + locals[arg.name] = arg_; + ++iter; + } + + if (!void_return) + { + // Create an alloca for the return value + return_var = var("return.val"); + } + } + + bool compiler::operator()(ast::function const& x) + { + /////////////////////////////////////////////////////////////////////// + // the signature: + function f = function_decl(x); + if (!f.is_valid()) + return false; + + /////////////////////////////////////////////////////////////////////// + // the body: + if (x.body) // compile the body if this is not a prototype + { + // Create a new basic block to start insertion into. + basic_block block = make_basic_block("entry", f); + set_insert_point(block); + + function_allocas(x, f); + return_block = make_basic_block("return"); + + if (!(*this)(*x.body)) + { + // Error reading body, remove function. + f.erase_from_parent(); + return false; + } + + basic_block last_block = f.last_block(); + + // If the last block is unterminated, connect it to return_block + if (!last_block.has_terminator()) + { + set_insert_point(last_block); + branch(return_block); + } + + f.add(return_block); + set_insert_point(return_block); + + if (void_return) + return_(); + else + return_(return_var); + + // Validate the generated code, checking for consistency. + f.verify(); + + // Optimize the function. + optimize_function(f); + } + + return true; + } + + bool compiler::operator()(ast::function_list const& x) + { + BOOST_FOREACH(ast::function const& f, x) + { + locals.clear(); // clear the variables + if (!(*this)(f)) + return false; + } + return true; + } +}} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.hpp new file mode 100644 index 00000000..ccca3644 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/compiler.hpp @@ -0,0 +1,311 @@ +/*============================================================================= + 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_CONJURE_COMPILER_HPP) +#define BOOST_SPIRIT_CONJURE_COMPILER_HPP + +#include "ast.hpp" +#include "error_handler.hpp" +#include "vm.hpp" +#include <map> + +#include <boost/function.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_function.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/iterator/transform_iterator.hpp> + +#include <llvm/DerivedTypes.h> +#include <llvm/Constants.h> +#include <llvm/LLVMContext.h> +#include <llvm/Module.h> +#include <llvm/PassManager.h> +#include <llvm/Analysis/Verifier.h> +#include <llvm/Analysis/Passes.h> +#include <llvm/Transforms/Scalar.h> +#include <llvm/Support/IRBuilder.h> + +namespace client { namespace code_gen +{ + unsigned const int_size = 32; + struct compiler; + struct llvm_compiler; + + /////////////////////////////////////////////////////////////////////////// + // The Value (light abstraction of an LLVM::Value) + /////////////////////////////////////////////////////////////////////////// + struct value + { + value(); + value(value const& rhs); + + value& operator=(value const& rhs); + bool is_lvalue() const; + bool is_valid() const; + operator bool() const; + + value& assign(value const& rhs); + + void name(char const* id); + void name(std::string const& id); + + friend value operator-(value a); + friend value operator!(value a); + friend value operator+(value a, value b); + friend value operator-(value a, value b); + friend value operator*(value a, value b); + friend value operator/(value a, value b); + friend value operator%(value a, value b); + + friend value operator&(value a, value b); + friend value operator|(value a, value b); + friend value operator^(value a, value b); + friend value operator<<(value a, value b); + friend value operator>>(value a, value b); + + friend value operator==(value a, value b); + friend value operator!=(value a, value b); + friend value operator<(value a, value b); + friend value operator<=(value a, value b); + friend value operator>(value a, value b); + friend value operator>=(value a, value b); + + private: + + struct to_llvm_value; + friend struct to_llvm_value; + friend struct llvm_compiler; + + value( + llvm::Value* v, + bool is_lvalue_, + llvm::IRBuilder<>* builder); + + llvm::LLVMContext& context() const + { return llvm::getGlobalContext(); } + + operator llvm::Value*() const; + + llvm::Value* v; + bool is_lvalue_; + llvm::IRBuilder<>* builder; + }; + + /////////////////////////////////////////////////////////////////////////// + // The Basic Block (light abstraction of an LLVM::BasicBlock) + /////////////////////////////////////////////////////////////////////////// + struct function; + + struct basic_block + { + basic_block() + : b(0) {} + + bool has_terminator() const; + bool is_valid() const; + + private: + + basic_block(llvm::BasicBlock* b) + : b(b) {} + + operator llvm::BasicBlock*() const + { return b; } + + friend struct llvm_compiler; + friend struct function; + llvm::BasicBlock* b; + }; + + /////////////////////////////////////////////////////////////////////////// + // The Function (light abstraction of an LLVM::Function) + /////////////////////////////////////////////////////////////////////////// + struct llvm_compiler; + + struct function + { + private: + + struct to_value; + typedef llvm::Function::arg_iterator arg_iterator; + typedef boost::transform_iterator< + to_value, arg_iterator> + arg_val_iterator; + + public: + + typedef boost::iterator_range<arg_val_iterator> arg_range; + + function() + : f(0), c(c) {} + + std::string name() const; + + std::size_t arg_size() const; + arg_range args() const; + + void add(basic_block const& b); + void erase_from_parent(); + basic_block last_block(); + bool empty() const; + + bool is_valid() const; + void verify() const; + + private: + + function(llvm::Function* f, llvm_compiler* c) + : f(f), c(c) {} + + operator llvm::Function*() const; + + friend struct llvm_compiler; + llvm::Function* f; + llvm_compiler* c; + }; + + /////////////////////////////////////////////////////////////////////////// + // The LLVM Compiler. Lower level compiler (does not deal with ASTs) + /////////////////////////////////////////////////////////////////////////// + struct llvm_compiler + { + llvm_compiler(vmachine& vm) + : llvm_builder(context()) + , vm(vm) + , fpm(vm.module()) + { init_fpm(); } + + value val() { return value(); } + value val(unsigned int x); + value val(int x); + value val(bool x); + + value var(char const* name); + value var(std::string const& name); + + template <typename Container> + value call(function callee, Container const& args); + + function get_function(char const* name); + function get_function(std::string const& name); + function get_current_function(); + + function declare_function( + bool void_return + , std::string const& name + , std::size_t nargs); + + basic_block make_basic_block( + char const* name + , function parent = function() + , basic_block before = basic_block()); + + basic_block get_insert_block(); + void set_insert_point(basic_block b); + + void conditional_branch( + value cond, basic_block true_br, basic_block false_br); + void branch(basic_block b); + + void return_(); + void return_(value v); + + void optimize_function(function f); + + protected: + + llvm::LLVMContext& context() const + { return llvm::getGlobalContext(); } + + llvm::IRBuilder<>& builder() + { return llvm_builder; } + + private: + + friend struct function::to_value; + + value val(llvm::Value* v); + + template <typename C> + llvm::Value* call_impl( + function callee, + C const& args); + + void init_fpm(); + llvm::IRBuilder<> llvm_builder; + vmachine& vm; + llvm::FunctionPassManager fpm; + }; + + /////////////////////////////////////////////////////////////////////////// + // The main compiler. Generates code from our AST. + /////////////////////////////////////////////////////////////////////////// + struct compiler : llvm_compiler + { + typedef value result_type; + + template <typename ErrorHandler> + compiler(vmachine& vm, ErrorHandler& error_handler_) + : llvm_compiler(vm) + { + 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]); + } + + value operator()(ast::nil) { BOOST_ASSERT(0); return val(); } + value operator()(unsigned int x); + value operator()(bool x); + value operator()(ast::primary_expr const& x); + value operator()(ast::identifier const& x); + value operator()(ast::unary_expr const& x); + value operator()(ast::function_call const& x); + value operator()(ast::expression const& x); + value operator()(ast::assignment const& x); + + bool operator()(ast::variable_declaration const& x); + bool operator()(ast::statement_list const& x); + bool operator()(ast::statement const& x); + bool operator()(ast::if_statement const& x); + bool operator()(ast::while_statement const& x); + bool operator()(ast::return_statement const& x); + bool operator()(ast::function const& x); + bool operator()(ast::function_list const& x); + + private: + + value compile_binary_expression( + value lhs, value rhs, token_ids::type op); + + value compile_expression( + int min_precedence, + value lhs, + std::list<ast::operation>::const_iterator& rest_begin, + std::list<ast::operation>::const_iterator rest_end); + + struct statement_compiler; + statement_compiler& as_statement(); + + function function_decl(ast::function const& x); + void function_allocas(ast::function const& x, function function); + + boost::function< + void(int tag, std::string const& what)> + error_handler; + + bool void_return; + std::string current_function_name; + std::map<std::string, value> locals; + basic_block return_block; + value return_var; + }; +}} + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/config.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/config.hpp new file mode 100644 index 00000000..70af0db0 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/config.hpp @@ -0,0 +1,51 @@ +/*============================================================================= + 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) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_LEXER_CONFIG_HPP) +#define BOOST_SPIRIT_CONJURE_LEXER_CONFIG_HPP + +/////////////////////////////////////////////////////////////////////////////// +// The conjure lexer example can be built in 3 different variations: +// +// - With a lexer using runtime generated DFA tables +// - With a lexer using pre-generated (static) DFA tables +// - With a lexer using a pre-generated custom switch based state machine +// +// Use one of the following preprocessor constants to define, which of those +// will be built: + +/////////////////////////////////////////////////////////////////////////////// +// Use the lexer based on runtime generated DFA tables +// #define CONJURE_LEXER_DYNAMIC_TABLES 1 + +/////////////////////////////////////////////////////////////////////////////// +// Use the lexer based on pre-generated static DFA tables +// #define CONJURE_LEXER_STATIC_TABLES 1 + +/////////////////////////////////////////////////////////////////////////////// +// Use the lexer based on runtime generated DFA tables +// #define CONJURE_LEXER_STATIC_SWITCH 1 + +/////////////////////////////////////////////////////////////////////////////// +// The default is to use the dynamic table driven lexer +#if CONJURE_LEXER_DYNAMIC_TABLES == 0 && \ + CONJURE_LEXER_STATIC_TABLES == 0 && \ + CONJURE_LEXER_STATIC_SWITCH == 0 + +#define CONJURE_LEXER_DYNAMIC_TABLES 1 +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Make sure we have only one lexer type selected +#if (CONJURE_LEXER_DYNAMIC_TABLES != 0 && CONJURE_LEXER_STATIC_TABLES != 0) || \ + (CONJURE_LEXER_DYNAMIC_TABLES != 0 && CONJURE_LEXER_STATIC_SWITCH != 0) || \ + (CONJURE_LEXER_STATIC_TABLES != 0 && CONJURE_LEXER_STATIC_SWITCH != 0) + +#error "Configuration problem: please select exactly one type of lexer to build" +#endif + +#endif + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/conjure_static_lexer.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/conjure_static_lexer.hpp new file mode 100644 index 00000000..651d34d6 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/conjure_static_lexer.hpp @@ -0,0 +1,483 @@ +// Copyright (c) 2008-2009 Ben Hanson +// Copyright (c) 2008-2011 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_CONJURE_STATIC_JUL_25_2011_07_25_53) +#define BOOST_SPIRIT_LEXER_NEXT_TOKEN_CONJURE_STATIC_JUL_25_2011_07_25_53 + +#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_conjure_static[1] = +{ + "INITIAL" +}; + +//////////////////////////////////////////////////////////////////////////////// +// this variable defines the number of lexer states +std::size_t const lexer_state_count_conjure_static = 1; + +//////////////////////////////////////////////////////////////////////////////// +// this function returns the next matched token +template<typename Iterator> +std::size_t next_token_conjure_static (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 std::size_t const npos = static_cast<std::size_t>(~0); + static std::size_t const lookup_[256] = { + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 7, 7, 41, 41, 7, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 7, 8, 41, 41, 41, 41, 9, 41, + 10, 11, 12, 13, 14, 15, 41, 16, + 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 41, 19, 20, 21, 22, 41, + 41, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 41, 41, 41, 41, 18, + 41, 23, 18, 18, 24, 25, 26, 18, + 27, 28, 18, 18, 29, 18, 30, 31, + 18, 18, 32, 33, 34, 35, 36, 37, + 18, 18, 18, 38, 39, 40, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41 }; + static std::size_t const dfa_alphabet_ = 42; + static std::size_t const dfa_[2604] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 27, 13, 11, 20, 21, 18, 16, + 24, 17, 19, 2, 26, 25, 14, 12, + 15, 26, 26, 7, 4, 26, 6, 26, + 26, 26, 9, 26, 3, 26, 5, 8, + 22, 10, 23, 0, 1, 35, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 28, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 1, 32, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 29, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 0, 0, + 0, 0, 1, 32, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 26, 26, 26, 26, 26, + 26, 30, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 1, 32, 28, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 26, 32, 26, + 26, 26, 31, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 33, 26, 26, 26, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 1, 32, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 26, + 26, 26, 26, 34, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 0, 0, + 0, 0, 1, 32, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 35, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 37, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 61, 26, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 38, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 262177, 20, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 39, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 131091, 12, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 40, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 131093, + 14, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 393241, 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 393242, 17, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 131099, 18, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 131100, + 19, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 42, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 40, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 41, 22, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 123, 23, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 125, + 24, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 44, 25, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 59, 27, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 32, 28, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 34, + 30, 0, 0, 0, 0, 27, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 32, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 43, 26, 26, 0, 0, + 0, 0, 1, 32, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 26, 26, 26, 26, 44, + 26, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 1, 32, 28, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 26, 26, 26, + 45, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 46, 26, 26, 26, 0, 0, 0, 0, + 1, 65538, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 0, 0, + 0, 0, 1, 32, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 47, 26, 26, 26, 26, + 0, 0, 0, 0, 1, 32, 28, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 26, 26, 26, + 48, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 49, 26, 26, 26, 0, 0, 0, 0, + 1, 131084, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 131085, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 131089, 10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 131090, + 11, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 131092, 13, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 131094, 15, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 42, 42, 42, 42, 42, + 50, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 51, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 1, 32, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 43, 26, 26, 26, 26, 0, 0, + 0, 0, 1, 32, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 52, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 1, 65537, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 53, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 1, 32, 28, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 26, + 26, 26, 26, 26, 26, 54, 26, 26, + 26, 26, 26, 26, 26, 26, 0, 0, + 0, 0, 1, 32, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 55, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 56, 56, 56, 56, 56, + 50, 56, 56, 56, 57, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 1, 36, + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 1, 65536, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 0, 0, + 0, 0, 1, 65539, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 1, 32, 28, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 58, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 59, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 56, + 56, 56, 56, 56, 60, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 1, 33, 29, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 65540, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 26, 26, 0, + 0, 0, 0, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 1, 32, + 28, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, + 26, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 26, 61, 26, 26, 26, + 26, 26, 26, 26, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 56, + 56, 56, 56, 56, 60, 56, 56, 56, + 57, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 1, 65541, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 0, 0, 0, + 0, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 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_conjure_static +{ + // 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_conjure_static; + } + + // return the name of the lexer state as given by 'idx' + static char const* state_name(std::size_t idx) + { + return lexer_state_names_conjure_static[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_conjure_static(start_state_, bol_, start_token_, end_, unique_id_); + } +}; + +}}}}} // namespace boost::spirit::lex::lexertl::static_ + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/conjure_static_lexer_generate.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/conjure_static_lexer_generate.cpp new file mode 100644 index 00000000..1ba8fac0 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/conjure_static_lexer_generate.cpp @@ -0,0 +1,43 @@ +// 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) + +// This small utility program generates the 2 static lexers, the static table +// driven and the static switch based lexer. + +#include <fstream> +#include <iostream> + +#include "lexer_def.hpp" +#include <boost/spirit/include/lex_generate_static_lexertl.hpp> + +int main() +{ + typedef std::string::const_iterator base_iterator_type; + typedef client::lexer::conjure_tokens<base_iterator_type> lexer_type; + + lexer_type lexer; + + // first generate the static switch based lexer + std::ofstream out_static("conjure_static_switch_lexer.hpp"); + + bool result = boost::spirit::lex::lexertl::generate_static_switch( + lexer, out_static, "conjure_static_switch"); + if (!result) { + std::cerr << "Failed to generate static switch based lexer\n"; + return -1; + } + + // now generate the static table based lexer + std::ofstream out("conjure_static_lexer.hpp"); + result = boost::spirit::lex::lexertl::generate_static( + lexer, out, "conjure_static"); + if (!result) { + std::cerr << "Failed to generate static table based lexer\n"; + return -1; + } + + return 0; +} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/conjure_static_switch_lexer.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/conjure_static_switch_lexer.hpp new file mode 100644 index 00000000..0dcd1ac7 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/conjure_static_switch_lexer.hpp @@ -0,0 +1,873 @@ +// Copyright (c) 2008-2009 Ben Hanson +// Copyright (c) 2008-2011 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_CONJURE_STATIC_SWITCH_JUL_25_2011_07_25_53) +#define BOOST_SPIRIT_LEXER_NEXT_TOKEN_CONJURE_STATIC_SWITCH_JUL_25_2011_07_25_53 + +#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_conjure_static_switch[1] = +{ + "INITIAL" +}; + +//////////////////////////////////////////////////////////////////////////////// +// this variable defines the number of lexer states +std::size_t const lexer_state_count_conjure_static_switch = 1; + +//////////////////////////////////////////////////////////////////////////////// +// this function returns the next matched token +template<typename Iterator> +std::size_t next_token_conjure_static_switch (std::size_t& /*start_state_*/, bool& /*bol_*/, + Iterator &start_token_, Iterator const& end_, std::size_t& unique_id_) +{ + static std::size_t const npos = static_cast<std::size_t>(~0); + + if (start_token_ == end_) + { + unique_id_ = npos; + return 0; + } + + Iterator curr_ = start_token_; + bool end_state_ = false; + std::size_t id_ = npos; + std::size_t uid_ = npos; + Iterator end_token_ = start_token_; + + char ch_ = 0; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9')) goto state0_1; + + if (ch_ == 't') goto state0_2; + + if (ch_ == 'f') goto state0_3; + + if (ch_ == 'v') goto state0_4; + + if (ch_ == 'i') goto state0_5; + + if (ch_ == 'e') goto state0_6; + + if (ch_ == 'w') goto state0_7; + + if (ch_ == 'r') goto state0_8; + + if (ch_ == '|') goto state0_9; + + if (ch_ == '&') goto state0_10; + + if (ch_ == '=') goto state0_11; + + if (ch_ == '!') goto state0_12; + + if (ch_ == '<') goto state0_13; + + if (ch_ == '>') goto state0_14; + + if (ch_ == '+') goto state0_15; + + if (ch_ == '-') goto state0_16; + + if (ch_ == '*') goto state0_17; + + if (ch_ == '/') goto state0_18; + + if (ch_ == '(') goto state0_19; + + if (ch_ == ')') goto state0_20; + + if (ch_ == '{') goto state0_21; + + if (ch_ == '}') goto state0_22; + + if (ch_ == ',') goto state0_23; + + if (ch_ == ';') goto state0_24; + + if ((ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'd') || (ch_ >= 'g' && ch_ <= 'h') || (ch_ >= 'j' && ch_ <= 'q') || ch_ == 's' || ch_ == 'u' || (ch_ >= 'x' && ch_ <= 'z')) goto state0_25; + + if ((ch_ >= '\t' && ch_ <= '\n') || ch_ == '\r' || ch_ == ' ') goto state0_26; + goto end; + +state0_1: + end_state_ = true; + id_ = 35; + uid_ = 0; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9')) goto state0_1; + goto end; + +state0_2: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'q') || (ch_ >= 's' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'r') goto state0_27; + goto end; + +state0_3: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'b' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'a') goto state0_28; + goto end; + +state0_4: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'n') || (ch_ >= 'p' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'o') goto state0_29; + goto end; + +state0_5: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'e') || (ch_ >= 'g' && ch_ <= 'm') || (ch_ >= 'o' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'n') goto state0_30; + + if (ch_ == 'f') goto state0_31; + goto end; + +state0_6: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'k') || (ch_ >= 'm' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'l') goto state0_32; + goto end; + +state0_7: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'g') || (ch_ >= 'i' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'h') goto state0_33; + goto end; + +state0_8: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'd') || (ch_ >= 'f' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'e') goto state0_34; + goto end; + +state0_9: + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '|') goto state0_35; + goto end; + +state0_10: + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '&') goto state0_36; + goto end; + +state0_11: + end_state_ = true; + id_ = 61; + uid_ = 26; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '=') goto state0_37; + goto end; + +state0_12: + end_state_ = true; + id_ = 262177; + uid_ = 20; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '=') goto state0_38; + goto end; + +state0_13: + end_state_ = true; + id_ = 131091; + uid_ = 12; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '=') goto state0_39; + goto end; + +state0_14: + end_state_ = true; + id_ = 131093; + uid_ = 14; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '=') goto state0_40; + goto end; + +state0_15: + end_state_ = true; + id_ = 393241; + uid_ = 16; + end_token_ = curr_; + goto end; + +state0_16: + end_state_ = true; + id_ = 393242; + uid_ = 17; + end_token_ = curr_; + goto end; + +state0_17: + end_state_ = true; + id_ = 131099; + uid_ = 18; + end_token_ = curr_; + goto end; + +state0_18: + end_state_ = true; + id_ = 131100; + uid_ = 19; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '*') goto state0_41; + goto end; + +state0_19: + end_state_ = true; + id_ = 40; + uid_ = 21; + end_token_ = curr_; + goto end; + +state0_20: + end_state_ = true; + id_ = 41; + uid_ = 22; + end_token_ = curr_; + goto end; + +state0_21: + end_state_ = true; + id_ = 123; + uid_ = 23; + end_token_ = curr_; + goto end; + +state0_22: + end_state_ = true; + id_ = 125; + uid_ = 24; + end_token_ = curr_; + goto end; + +state0_23: + end_state_ = true; + id_ = 44; + uid_ = 25; + end_token_ = curr_; + goto end; + +state0_24: + end_state_ = true; + id_ = 59; + uid_ = 27; + end_token_ = curr_; + goto end; + +state0_25: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_26: + end_state_ = true; + id_ = 34; + uid_ = 30; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '\t' && ch_ <= '\n') || ch_ == '\r' || ch_ == ' ') goto state0_26; + goto end; + +state0_27: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 't') || (ch_ >= 'v' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'u') goto state0_42; + goto end; + +state0_28: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'k') || (ch_ >= 'm' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'l') goto state0_43; + goto end; + +state0_29: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'h') || (ch_ >= 'j' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'i') goto state0_44; + goto end; + +state0_30: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 's') || (ch_ >= 'u' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 't') goto state0_45; + goto end; + +state0_31: + end_state_ = true; + id_ = 65538; + uid_ = 4; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_32: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'r') || (ch_ >= 't' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 's') goto state0_46; + goto end; + +state0_33: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'h') || (ch_ >= 'j' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'i') goto state0_47; + goto end; + +state0_34: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 's') || (ch_ >= 'u' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 't') goto state0_48; + goto end; + +state0_35: + end_state_ = true; + id_ = 131084; + uid_ = 8; + end_token_ = curr_; + goto end; + +state0_36: + end_state_ = true; + id_ = 131085; + uid_ = 9; + end_token_ = curr_; + goto end; + +state0_37: + end_state_ = true; + id_ = 131089; + uid_ = 10; + end_token_ = curr_; + goto end; + +state0_38: + end_state_ = true; + id_ = 131090; + uid_ = 11; + end_token_ = curr_; + goto end; + +state0_39: + end_state_ = true; + id_ = 131092; + uid_ = 13; + end_token_ = curr_; + goto end; + +state0_40: + end_state_ = true; + id_ = 131094; + uid_ = 15; + end_token_ = curr_; + goto end; + +state0_41: + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ != '*') goto state0_41; + + if (ch_ == '*') goto state0_49; + goto end; + +state0_42: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'd') || (ch_ >= 'f' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'e') goto state0_50; + goto end; + +state0_43: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'r') || (ch_ >= 't' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 's') goto state0_42; + goto end; + +state0_44: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'c') || (ch_ >= 'e' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'd') goto state0_51; + goto end; + +state0_45: + end_state_ = true; + id_ = 65537; + uid_ = 3; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_46: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'd') || (ch_ >= 'f' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'e') goto state0_52; + goto end; + +state0_47: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'k') || (ch_ >= 'm' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'l') goto state0_53; + goto end; + +state0_48: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 't') || (ch_ >= 'v' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'u') goto state0_54; + goto end; + +state0_49: + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ == '*') goto state0_49; + + if (ch_ != '*' && ch_ != '/') goto state0_55; + + if (ch_ == '/') goto state0_56; + goto end; + +state0_50: + end_state_ = true; + id_ = 36; + uid_ = 1; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_51: + end_state_ = true; + id_ = 65536; + uid_ = 2; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_52: + end_state_ = true; + id_ = 65539; + uid_ = 5; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_53: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'd') || (ch_ >= 'f' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'e') goto state0_57; + goto end; + +state0_54: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'q') || (ch_ >= 's' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'r') goto state0_58; + goto end; + +state0_55: + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ != '*') goto state0_55; + + if (ch_ == '*') goto state0_59; + goto end; + +state0_56: + end_state_ = true; + id_ = 33; + uid_ = 29; + end_token_ = curr_; + goto end; + +state0_57: + end_state_ = true; + id_ = 65540; + uid_ = 6; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + goto end; + +state0_58: + end_state_ = true; + id_ = 32; + uid_ = 28; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'm') || (ch_ >= 'o' && ch_ <= 'z')) goto state0_25; + + if (ch_ == 'n') goto state0_60; + goto end; + +state0_59: + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if (ch_ != '*' && ch_ != '/') goto state0_55; + + if (ch_ == '/') goto state0_56; + + if (ch_ == '*') goto state0_59; + goto end; + +state0_60: + end_state_ = true; + id_ = 65541; + uid_ = 7; + end_token_ = curr_; + + if (curr_ == end_) goto end; + ch_ = *curr_; + ++curr_; + + if ((ch_ >= '0' && ch_ <= '9') || (ch_ >= 'A' && ch_ <= 'Z') || ch_ == '_' || (ch_ >= 'a' && ch_ <= 'z')) goto state0_25; + +end: + 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_conjure_static_switch +{ + // 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_conjure_static_switch; + } + + // return the name of the lexer state as given by 'idx' + static char const* state_name(std::size_t idx) + { + return lexer_state_names_conjure_static_switch[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_conjure_static_switch(start_state_, bol_, start_token_, end_, unique_id_); + } +}; + +}}}}} // namespace boost::spirit::lex::lexertl::static_ + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/error_handler.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/error_handler.hpp new file mode 100644 index 00000000..efda9a6a --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/error_handler.hpp @@ -0,0 +1,99 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_ERROR_HANDLER_HPP) +#define BOOST_SPIRIT_CONJURE_ERROR_HANDLER_HPP + +#include <iostream> +#include <string> +#include <vector> + +namespace client +{ + /////////////////////////////////////////////////////////////////////////////// + // The error handler + /////////////////////////////////////////////////////////////////////////////// + template <typename BaseIterator, typename Iterator> + struct error_handler + { + template <typename, typename, typename> + struct result { typedef void type; }; + + error_handler(BaseIterator first, BaseIterator last) + : first(first), last(last) {} + + template <typename Message, typename What> + void operator()( + Message const& message, + What const& what, + Iterator err_pos) const + { + // retrieve underlying iterator from current token, err_pos points + // to the last validly matched token, so we use its end iterator + // as the error position + BaseIterator err_pos_base = err_pos->matched().end(); + + int line; + BaseIterator line_start = get_pos(err_pos_base, line); + if (err_pos_base != last) + { + std::cout << message << what << " line " << line << ':' << std::endl; + std::cout << get_line(line_start) << std::endl; + for (; line_start != err_pos_base; ++line_start) + std::cout << ' '; + std::cout << '^' << std::endl; + } + else + { + std::cout << "Unexpected end of file. "; + std::cout << message << what << " line " << line << std::endl; + } + } + + BaseIterator get_pos(BaseIterator err_pos, int& line) const + { + line = 1; + BaseIterator i = first; + BaseIterator 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(BaseIterator err_pos) const + { + BaseIterator i = err_pos; + // position i to the next EOL + while (i != last && (*i != '\r' && *i != '\n')) + ++i; + return std::string(err_pos, i); + } + + BaseIterator first; + BaseIterator last; + std::vector<Iterator> iters; + }; +} + +#endif + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/expression.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/expression.cpp new file mode 100644 index 00000000..5b51ce36 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/expression.cpp @@ -0,0 +1,20 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if defined(_MSC_VER) +# pragma warning(disable: 4345) +#endif + +#include "config.hpp" +#include "lexer.hpp" +#include "expression_def.hpp" + +typedef std::string::const_iterator base_iterator_type; +typedef client::lexer::conjure_tokens<base_iterator_type> lexer_type; +typedef lexer_type::iterator_type iterator_type; + +template struct client::parser::expression<iterator_type, lexer_type>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/expression.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/expression.hpp new file mode 100644 index 00000000..eec9cd6a --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/expression.hpp @@ -0,0 +1,59 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_EXPRESSION_HPP) +#define BOOST_SPIRIT_CONJURE_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, typename Lexer> + struct expression : qi::grammar<Iterator, ast::expression()> + { + typedef error_handler<typename Lexer::base_iterator_type, Iterator> + error_handler_type; + + expression(error_handler_type& error_handler, Lexer const& l); + + Lexer const& lexer; + + qi::rule<Iterator, ast::expression()> expr; + qi::rule<Iterator, ast::operand()> unary_expr, postfix_expr; + qi::rule<Iterator, ast::function_call()> function_call; + qi::rule<Iterator, std::list<ast::expression>()> argument_list; + qi::rule<Iterator, std::string()> identifier; + qi::rule<Iterator, ast::primary_expr()> primary_expr; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/expression_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/expression_def.hpp new file mode 100644 index 00000000..2d21323a --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/expression_def.hpp @@ -0,0 +1,104 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "expression.hpp" +#include "error_handler.hpp" +#include "annotation.hpp" +#include <boost/spirit/include/phoenix_function.hpp> +#include <boost/spirit/include/lex_plain_token.hpp> + +namespace client { namespace parser +{ + template <typename Iterator, typename Lexer> + expression<Iterator, Lexer>::expression( + error_handler<typename Lexer::base_iterator_type, Iterator>& error_handler + , Lexer const& l) + : expression::base_type(expr), lexer(l) + { + qi::_1_type _1; + qi::_2_type _2; + qi::_3_type _3; + qi::_4_type _4; + + qi::_val_type _val; + qi::tokenid_mask_type tokenid_mask; + + using qi::on_error; + using qi::on_success; + using qi::fail; + using boost::phoenix::function; + + typedef client::error_handler<typename Lexer::base_iterator_type, Iterator> + error_handler_type; + typedef function<error_handler_type> error_handler_function; + typedef function<client::annotation<Iterator> > annotation_function; + + /////////////////////////////////////////////////////////////////////// + // Main expression grammar + expr = + unary_expr + >> *(tokenid_mask(token_ids::op_binary) > unary_expr) + ; + + unary_expr = + postfix_expr + | (tokenid_mask(token_ids::op_unary) > unary_expr) + ; + + postfix_expr = + function_call + | primary_expr + ; + + primary_expr = + lexer.lit_uint + | lexer.true_or_false + | identifier + | '(' > expr > ')' + ; + + function_call = + (identifier >> '(') + > argument_list + > ')' + ; + + argument_list = -(expr % ','); + + identifier = lexer.identifier; + + /////////////////////////////////////////////////////////////////////// + // Debugging and error handling and reporting support. + BOOST_SPIRIT_DEBUG_NODES( + (expr) + (unary_expr) + (postfix_expr) + (primary_expr) + (function_call) + (argument_list) + (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 unary_expr, postfix_expr, + // and primary_expr call annotation. + on_success(unary_expr, + annotation_function(error_handler.iters)(_val, _1)); + on_success(postfix_expr, + annotation_function(error_handler.iters)(_val, _1)); + on_success(primary_expr, + annotation_function(error_handler.iters)(_val, _1)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/function.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/function.cpp new file mode 100644 index 00000000..98c8169b --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/function.cpp @@ -0,0 +1,20 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if defined(_MSC_VER) +# pragma warning(disable: 4345) +#endif + +#include "config.hpp" +#include "lexer.hpp" +#include "function_def.hpp" + +typedef std::string::const_iterator base_iterator_type; +typedef client::lexer::conjure_tokens<base_iterator_type> lexer_type; +typedef lexer_type::iterator_type iterator_type; + +template struct client::parser::function<iterator_type, lexer_type>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/function.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/function.hpp new file mode 100644 index 00000000..fc5263f1 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/function.hpp @@ -0,0 +1,36 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_FUNCTION_HPP) +#define BOOST_SPIRIT_CONJURE_FUNCTION_HPP + +#include "statement.hpp" + +namespace client { namespace parser +{ + /////////////////////////////////////////////////////////////////////////////// + // The function grammar + /////////////////////////////////////////////////////////////////////////////// + template <typename Iterator, typename Lexer> + struct function : qi::grammar<Iterator, ast::function()> + { + typedef error_handler<typename Lexer::base_iterator_type, Iterator> + error_handler_type; + + function(error_handler_type& error_handler, Lexer const& l); + + statement<Iterator, Lexer> body; + + qi::rule<Iterator, ast::identifier()> identifier; + qi::rule<Iterator, std::list<ast::identifier>()> argument_list; + qi::rule<Iterator, ast::function()> start; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/function_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/function_def.hpp new file mode 100644 index 00000000..95846264 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/function_def.hpp @@ -0,0 +1,64 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "function.hpp" +#include "error_handler.hpp" +#include "annotation.hpp" + +namespace client { namespace parser +{ + template <typename Iterator, typename Lexer> + function<Iterator, Lexer>::function( + error_handler<typename Lexer::base_iterator_type, Iterator>& error_handler + , Lexer const& l) + : function::base_type(start), body(error_handler, l) + { + qi::_1_type _1; + qi::_2_type _2; + qi::_3_type _3; + qi::_4_type _4; + + qi::_val_type _val; + + using qi::on_error; + using qi::on_success; + using qi::fail; + using boost::phoenix::function; + + typedef client::error_handler<typename Lexer::base_iterator_type, Iterator> + error_handler_type; + typedef function<error_handler_type> error_handler_function; + typedef function<client::annotation<Iterator> > annotation_function; + + identifier = body.expr.identifier; + argument_list = -(identifier % ','); + + start = (l.token("void") | l.token("int")) + > identifier + > '(' > argument_list > ')' + > (';' | '{' > body > '}') + ; + + // Debugging and error handling and reporting support. + BOOST_SPIRIT_DEBUG_NODES( + (identifier) + (argument_list) + (start) + ); + + // Error handling: on error in start, call error_handler. + on_error<fail>(start, + error_handler_function(error_handler)( + "Error! Expecting ", _4, _3)); + + // Annotation: on success in start, call annotation. + on_success(identifier, + annotation_function(error_handler.iters)(_val, _1)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/ids.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/ids.hpp new file mode 100644 index 00000000..c4d98acc --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/ids.hpp @@ -0,0 +1,160 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_IDS_HPP) +#define BOOST_SPIRIT_CONJURE_IDS_HPP + +namespace client +{ + struct op_type + { + enum type + { + binary = 0x20000, + unary = 0x40000, + postfix_unary = 0x80000, + assign = 0x100000 + }; + }; + + struct op + { + enum type + { + // binary + comma, + assign, + plus_assign, + minus_assign, + times_assign, + divide_assign, + mod_assign, + bit_and_assign, + bit_xor_assign, + bit_or_assign, + shift_left_assign, + shift_right_assign, + logical_or, + logical_and, + bit_or, + bit_xor, + bit_and, + equal, + not_equal, + less, + less_equal, + greater, + greater_equal, + shift_left, + shift_right, + plus, + minus, + times, + divide, + mod, + + // unary + plus_plus, + minus_minus, + compl_, + not_, + }; + }; + + template <int type, int op> + struct make_op + { + static int const value = type + op; + }; + + template <op::type op> + struct unary_op : make_op<op_type::unary, op> {}; + + template <op::type op> + struct binary_op + : make_op<op_type::binary, op> {}; + + template <op::type op> + struct assign_op + : make_op<op_type::assign, op> {}; + + template <op::type op> + struct binary_or_unary_op + : make_op<op_type::unary | op_type::binary, op> {}; + + struct token_ids + { + enum type + { + // pseudo tags + invalid = -1, + op_binary = op_type::binary, + op_unary = op_type::unary, + op_assign = op_type::assign, + + // binary / unary operators with common tokens + // '+' and '-' can be binary or unary + // (the lexer cannot distinguish which) + plus = binary_or_unary_op<op::plus>::value, + minus = binary_or_unary_op<op::minus>::value, + + // binary operators + comma = binary_op<op::comma>::value, + assign = assign_op<op::assign>::value, + plus_assign = assign_op<op::plus_assign>::value, + minus_assign = assign_op<op::minus_assign>::value, + times_assign = assign_op<op::times_assign>::value, + divide_assign = assign_op<op::divide_assign>::value, + mod_assign = assign_op<op::mod_assign>::value, + bit_and_assign = assign_op<op::bit_and_assign>::value, + bit_xor_assign = assign_op<op::bit_xor_assign>::value, + bit_or_assign = assign_op<op::bit_or_assign>::value, + shift_left_assign = assign_op<op::shift_left_assign>::value, + shift_right_assign = assign_op<op::shift_right_assign>::value, + logical_or = binary_op<op::logical_or>::value, + logical_and = binary_op<op::logical_and>::value, + bit_or = binary_op<op::bit_or>::value, + bit_xor = binary_op<op::bit_xor>::value, + bit_and = binary_op<op::bit_and>::value, + equal = binary_op<op::equal>::value, + not_equal = binary_op<op::not_equal>::value, + less = binary_op<op::less>::value, + less_equal = binary_op<op::less_equal>::value, + greater = binary_op<op::greater>::value, + greater_equal = binary_op<op::greater_equal>::value, + shift_left = binary_op<op::shift_left>::value, + shift_right = binary_op<op::shift_right>::value, + times = binary_op<op::times>::value, + divide = binary_op<op::divide>::value, + mod = binary_op<op::mod>::value, + + // unary operators with overlaps + // '++' and '--' can be prefix or postfix + // (the lexer cannot distinguish which) + plus_plus = make_op< + op_type::unary + | op_type::postfix_unary, op::plus_plus>::value, + + minus_minus = make_op< + op_type::unary + | op_type::postfix_unary, op::minus_minus>::value, + + // unary operators + compl_ = unary_op<op::compl_>::value, + not_ = unary_op<op::not_>::value, + + // misc tags + identifier = op::not_ + 1, + comment, + whitespace, + lit_uint, + true_or_false + }; + }; +} + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/lexer.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/lexer.cpp new file mode 100644 index 00000000..d7125fed --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/lexer.cpp @@ -0,0 +1,17 @@ +/*============================================================================= + 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) +=============================================================================*/ +#if defined(_MSC_VER) +# pragma warning(disable: 4345) +#endif + +#include "config.hpp" +#include "lexer_def.hpp" + +typedef std::string::const_iterator base_iterator_type; +template client::lexer::conjure_tokens<base_iterator_type>::conjure_tokens(); +template bool client::lexer::conjure_tokens<base_iterator_type>::add_( + std::string const&, int); diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/lexer.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/lexer.hpp new file mode 100644 index 00000000..9624fe2f --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/lexer.hpp @@ -0,0 +1,144 @@ +/*============================================================================= + 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) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_LEXER_HPP) +#define BOOST_SPIRIT_CONJURE_LEXER_HPP + +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/spirit/include/lex_lexertl_position_token.hpp> + +#include "config.hpp" +#include "ids.hpp" + +#if CONJURE_LEXER_STATIC_TABLES != 0 +#include <boost/spirit/include/lex_static_lexertl.hpp> +#include "conjure_static_lexer.hpp" +#elif CONJURE_LEXER_STATIC_SWITCH != 0 +#include <boost/spirit/include/lex_static_lexertl.hpp> +#include "conjure_static_switch_lexer.hpp" +#endif +#include <boost/assert.hpp> + +namespace client { namespace lexer +{ + namespace lex = boost::spirit::lex; + + /////////////////////////////////////////////////////////////////////////// + namespace detail + { + namespace lex = boost::spirit::lex; + + template <typename BaseIterator> + struct get_lexer_type + { + // Our token needs to be able to carry several token values: + // std::string, unsigned int, and bool + typedef boost::mpl::vector<std::string, unsigned int, bool> + token_value_types; + + // Using the position_token class as the token type to be returned + // from the lexer iterators allows to retain positional information + // as every token instance stores an iterator pair pointing to the + // matched input sequence. + typedef lex::lexertl::position_token< + BaseIterator, token_value_types, boost::mpl::false_ + > token_type; + +#if CONJURE_LEXER_DYNAMIC_TABLES != 0 + // use the lexer based on runtime generated DFA tables + typedef lex::lexertl::actor_lexer<token_type> type; +#elif CONJURE_LEXER_STATIC_TABLES != 0 + // use the lexer based on pre-generated static DFA tables + typedef lex::lexertl::static_actor_lexer< + token_type + , boost::spirit::lex::lexertl::static_::lexer_conjure_static + > type; +#elif CONJURE_LEXER_STATIC_SWITCH != 0 + // use the lexer based on pre-generated static code + typedef lex::lexertl::static_actor_lexer< + token_type + , boost::spirit::lex::lexertl::static_::lexer_conjure_static_switch + > type; +#else +#error "Configuration problem: please select exactly one type of lexer to build" +#endif + }; + } + + /////////////////////////////////////////////////////////////////////////// + template <typename BaseIterator> + struct conjure_tokens + : lex::lexer<typename detail::get_lexer_type<BaseIterator>::type> + { + private: + // get the type of any qi::raw_token(...) and qi::token(...) constructs + typedef typename boost::spirit::result_of::terminal< + boost::spirit::tag::raw_token(token_ids::type) + >::type raw_token_spec; + + typedef typename boost::spirit::result_of::terminal< + boost::spirit::tag::token(token_ids::type) + >::type token_spec; + + typedef std::map<std::string, token_ids::type> keyword_map_type; + + protected: + // add a keyword to the mapping table + bool add_(std::string const& keyword, int id = token_ids::invalid); + + struct keyword_adder + { + conjure_tokens& l; + keyword_adder(conjure_tokens& l) : l(l) {} + keyword_adder& operator()( + std::string const& keyword, int id = token_ids::invalid) + { + l.add_(keyword, id); + return *this; + } + }; + + friend struct keyword_adder; + keyword_adder add; + + public: + typedef BaseIterator base_iterator_type; + + conjure_tokens(); + + // extract a raw_token(id) for the given registered keyword + raw_token_spec operator()(std::string const& kwd) const + { + namespace qi = boost::spirit::qi; + qi::raw_token_type raw_token; + + typename keyword_map_type::const_iterator it = keywords_.find(kwd); + BOOST_ASSERT(it != keywords_.end()); + return raw_token((it != keywords_.end()) ? (*it).second : token_ids::invalid); + } + + // extract a token(id) for the given registered keyword + token_spec token(std::string const& kwd) const + { + namespace qi = boost::spirit::qi; + qi::token_type token; + + typename keyword_map_type::const_iterator it = keywords_.find(kwd); + BOOST_ASSERT(it != keywords_.end()); + return token((it != keywords_.end()) ? (*it).second : token_ids::invalid); + } + + lex::token_def<std::string> identifier; + lex::token_def<unsigned int> lit_uint; + lex::token_def<bool> true_or_false; + keyword_map_type keywords_; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/lexer_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/lexer_def.hpp new file mode 100644 index 00000000..29239768 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/lexer_def.hpp @@ -0,0 +1,100 @@ +/*============================================================================= + 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) +=============================================================================*/ + +#include "lexer.hpp" + +namespace client { namespace lexer +{ + template <typename BaseIterator> + conjure_tokens<BaseIterator>::conjure_tokens() + : identifier("[a-zA-Z_][a-zA-Z_0-9]*", token_ids::identifier) + , lit_uint("[0-9]+", token_ids::lit_uint) + , true_or_false("true|false", token_ids::true_or_false) + , add(*this) + { + lex::_pass_type _pass; + + this->self = lit_uint | true_or_false; + + this->add + ("void") + ("int") + ("if") + ("else") + ("while") + ("return") + ("=", token_ids::assign) + ("\\+=", token_ids::plus_assign) + ("\\-=", token_ids::minus_assign) + ("\\*=", token_ids::times_assign) + ("\\/=", token_ids::divide_assign) + ("%=", token_ids::mod_assign) + ("\\&=", token_ids::bit_and_assign) + ("\\^=", token_ids::bit_xor_assign) + ("\\|=", token_ids::bit_or_assign) + ("<<=", token_ids::shift_left_assign) + (">>=", token_ids::shift_right_assign) + ("\\|\\|", token_ids::logical_or) + ("&&", token_ids::logical_and) + ("\\|", token_ids::bit_or) + ("\\^", token_ids::bit_xor) + ("&", token_ids::bit_and) + ("<<", token_ids::shift_left) + (">>", token_ids::shift_right) + ("==", token_ids::equal) + ("!=", token_ids::not_equal) + ("<", token_ids::less) + ("<=", token_ids::less_equal) + (">", token_ids::greater) + (">=", token_ids::greater_equal) + ("\\+", token_ids::plus) + ("\\-", token_ids::minus) + ("\\*", token_ids::times) + ("\\/", token_ids::divide) + ("%", token_ids::mod) + ("\\+\\+", token_ids::plus_plus) + ("\\-\\-", token_ids::minus_minus) + ("~", token_ids::compl_) + ("!", token_ids::not_) + ; + + this->self += lex::char_('(') | ')' | '{' | '}' | ',' | ';'; + + this->self += + identifier + | lex::string("(\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/)|(\\/\\/[^\r\n]*)", token_ids::comment) + [ + lex::_pass = lex::pass_flags::pass_ignore + ] + | lex::string("[ \t\n\r]+", token_ids::whitespace) + [ + lex::_pass = lex::pass_flags::pass_ignore + ] + ; + } + + template <typename BaseIterator> + bool conjure_tokens<BaseIterator>::add_( + std::string const& keyword, int id_) + { + // add the token to the lexer + token_ids::type id; + if (id_ == token_ids::invalid) + id = token_ids::type(this->get_next_id()); + else + id = token_ids::type(id_); + + this->self.add(keyword, id); + // store the mapping for later retrieval + std::pair<typename keyword_map_type::iterator, bool> p = + keywords_.insert(typename keyword_map_type::value_type(keyword, id)); + + return p.second; + } +}} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/main.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/main.cpp new file mode 100644 index 00000000..9788e56f --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/main.cpp @@ -0,0 +1,156 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Not a calculator anymore, right? :-) +// +// [ JDG April 10, 2007 ] spirit2 +// [ JDG February 18, 2011 ] Pure attributes. No semantic actions. +// [ HK June 3, 2011 ] Adding lexer +// [ JDG July 18, 2011 ] Switching to LLVM backend +// +/////////////////////////////////////////////////////////////////////////////// + +#include "config.hpp" +#include "function.hpp" +#include "vm.hpp" +#include "compiler.hpp" +#include "lexer.hpp" +#include <boost/lexical_cast.hpp> +#include <fstream> + +/////////////////////////////////////////////////////////////////////////////// +// 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 source_code; // 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(source_code)); + + typedef std::string::const_iterator base_iterator_type; + typedef client::lexer::conjure_tokens<base_iterator_type> lexer_type; + typedef lexer_type::iterator_type iterator_type; + + lexer_type lexer; // Our lexer + + base_iterator_type first = source_code.begin(); + base_iterator_type last = source_code.end(); + + iterator_type iter = lexer.begin(first, last); + iterator_type end = lexer.end(); + + client::vmachine vm; // Our virtual machine + client::ast::function_list ast; // Our AST + + client::error_handler<base_iterator_type, iterator_type> + error_handler(first, last); // Our error handler + client::parser::function<iterator_type, lexer_type> + function(error_handler, lexer); // Our parser + client::code_gen::compiler + compiler(vm, error_handler); // Our compiler + + // note: we don't need a skipper + bool success = parse(iter, end, +function, ast); + + std::cout << "-------------------------\n"; + + if (success && iter == end) + { + if (compiler(ast)) + { + // JIT the main function + client::function main_function = vm.get_function("main"); + if (!main_function) + { + std::cerr << "function main not found" << std::endl; + return 1; + } + + int nargs = argc-2; + if (main_function.arity() != nargs) + { + std::cerr << "Error: main function requires " + << main_function.arity() << " arguments." << std::endl; + std::cerr << nargs << " supplied." << std::endl; + return 1; + } + + std::cout << "Success\n"; + std::cout << "-------------------------\n"; + std::cout << "Assembler----------------\n\n"; + vm.print_assembler(); + + // Call the main function + int r; + char** args = argv + 2; + switch (nargs) + { + case 0: r = main_function(); break; + + case 1: r = main_function( + boost::lexical_cast<int>(args[0])); + break; + + case 2: r = main_function( + boost::lexical_cast<int>(args[0]), + boost::lexical_cast<int>(args[1])); + break; + + case 3: r = main_function( + boost::lexical_cast<int>(args[0]), + boost::lexical_cast<int>(args[1]), + boost::lexical_cast<int>(args[2])); + break; + + default: + std::cerr << "Function calls with more " << + "than 3 arguments not supported" << std::endl; + return 1; + } + + std::cout << "-------------------------\n"; + std::cout << "Result: " << r << std::endl; + std::cout << "-------------------------\n\n"; + } + else + { + std::cout << "Compile failure\n"; + } + } + else + { + std::cout << "Parse failure\n"; + } + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/statement.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/statement.cpp new file mode 100644 index 00000000..ca85a61c --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/statement.cpp @@ -0,0 +1,20 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if defined(_MSC_VER) +# pragma warning(disable: 4345) +#endif + +#include "config.hpp" +#include "lexer.hpp" +#include "statement_def.hpp" + +typedef std::string::const_iterator base_iterator_type; +typedef client::lexer::conjure_tokens<base_iterator_type> lexer_type; +typedef lexer_type::iterator_type iterator_type; + +template struct client::parser::statement<iterator_type, lexer_type>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/statement.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/statement.hpp new file mode 100644 index 00000000..208ca247 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/statement.hpp @@ -0,0 +1,42 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CONJURE_STATEMENT_HPP) +#define BOOST_SPIRIT_CONJURE_STATEMENT_HPP + +#include "expression.hpp" + +namespace client { namespace parser +{ + /////////////////////////////////////////////////////////////////////////////// + // The statement grammar + /////////////////////////////////////////////////////////////////////////////// + template <typename Iterator, typename Lexer> + struct statement : qi::grammar<Iterator, ast::statement_list()> + { + typedef error_handler<typename Lexer::base_iterator_type, Iterator> + error_handler_type; + + statement(error_handler_type& error_handler, Lexer const& l); + + expression<Iterator, Lexer> expr; + + qi::rule<Iterator, ast::statement_list()> + statement_list, compound_statement; + + qi::rule<Iterator, ast::statement()> statement_; + qi::rule<Iterator, ast::variable_declaration()> variable_declaration; + qi::rule<Iterator, ast::assignment()> assignment; + qi::rule<Iterator, ast::if_statement()> if_statement; + qi::rule<Iterator, ast::while_statement()> while_statement; + qi::rule<Iterator, ast::return_statement()> return_statement; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/statement_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/statement_def.hpp new file mode 100644 index 00000000..4712aa5f --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/statement_def.hpp @@ -0,0 +1,126 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2001-2011 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "statement.hpp" +#include "error_handler.hpp" +#include "annotation.hpp" + +namespace client { namespace parser +{ + template <typename Iterator, typename Lexer> + statement<Iterator, Lexer>::statement( + error_handler<typename Lexer::base_iterator_type, Iterator>& error_handler + , Lexer const& l) + : statement::base_type(statement_list), expr(error_handler, l) + { + qi::_1_type _1; + qi::_2_type _2; + qi::_3_type _3; + qi::_4_type _4; + + qi::_val_type _val; + qi::tokenid_mask_type tokenid_mask; + + using qi::on_error; + using qi::on_success; + using qi::fail; + using boost::phoenix::function; + + typedef client::error_handler<typename Lexer::base_iterator_type, Iterator> + error_handler_type; + typedef function<error_handler_type> error_handler_function; + typedef function<client::annotation<Iterator> > annotation_function; + + statement_list = + +statement_ + ; + + statement_ = + variable_declaration + | assignment + | compound_statement + | if_statement + | while_statement + | return_statement + | expr + | ';' + ; + + variable_declaration = + l("int") + > expr.identifier + > -(l("=") > expr) + > ';' + ; + + assignment = + expr.identifier + > tokenid_mask(token_ids::op_assign) + > expr + > ';' + ; + + if_statement = + l("if") + > '(' + > expr + > ')' + > statement_ + > + -( + l("else") + > statement_ + ) + ; + + while_statement = + l("while") + > '(' + > expr + > ')' + > statement_ + ; + + compound_statement = + '{' >> -statement_list >> '}' + ; + + return_statement = + l("return") + > -expr + > ';' + ; + + // Debugging and error handling and reporting support. + BOOST_SPIRIT_DEBUG_NODES( + (statement_list) + (statement_) + (variable_declaration) + (assignment) + (if_statement) + (while_statement) + (compound_statement) + (return_statement) + ); + + // 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 variable_declaration, + // assignment and return_statement, call annotation. + on_success(variable_declaration, + annotation_function(error_handler.iters)(_val, _1)); + on_success(assignment, + annotation_function(error_handler.iters)(_val, _1)); + on_success(return_statement, + annotation_function(error_handler.iters)(_val, _1)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/vm.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/vm.cpp new file mode 100644 index 00000000..5434046f --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/vm.cpp @@ -0,0 +1,43 @@ +/*============================================================================= + 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 "config.hpp" +#include "vm.hpp" +#include <iostream> + +#if defined(_MSC_VER) +# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' + // (performance warning) +#endif + +namespace client +{ + vmachine::vmachine() + { + llvm::InitializeNativeTarget(); + llvm::LLVMContext& context = llvm::getGlobalContext(); + + // Make the module, which holds all the code. + module_ = new llvm::Module("Conjure JIT", context); + + // Create the JIT. This takes ownership of the module. + std::string error; + execution_engine_ = + llvm::EngineBuilder(module_).setErrorStr(&error).create(); + + BOOST_ASSERT(execution_engine_ != 0); + if (!execution_engine_) + { + std::cerr << + "Could not create ExecutionEngine: " << + error << std::endl; + + exit(1); + } + } +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/vm.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/vm.hpp new file mode 100644 index 00000000..68698920 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure3/vm.hpp @@ -0,0 +1,121 @@ +/*============================================================================= + 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_CONJURE_VM_HPP) +#define BOOST_SPIRIT_CONJURE_VM_HPP + +#include <llvm/ExecutionEngine/ExecutionEngine.h> +#include <llvm/ExecutionEngine/JIT.h> +#include <llvm/LLVMContext.h> +#include <llvm/Module.h> +#include <llvm/Target/TargetData.h> +#include <llvm/Target/TargetSelect.h> + +#include <boost/assert.hpp> + +namespace client +{ + class vmachine; + + /////////////////////////////////////////////////////////////////////////// + // A light wrapper to a function pointer returning int and accepting + // from 0 to 3 arguments, where arity is determined at runtime. + /////////////////////////////////////////////////////////////////////////// + class function + { + public: + + typedef int result_type; + + int operator()() const + { + BOOST_ASSERT(fptr != 0); + BOOST_ASSERT(arity() == 0); + int (*fp)() = (int(*)())(intptr_t)fptr; + return fp(); + } + + int operator()(int _1) const + { + BOOST_ASSERT(fptr != 0); + BOOST_ASSERT(arity() == 1); + int (*fp)(int) = (int(*)(int))(intptr_t)fptr; + return fp(_1); + } + + int operator()(int _1, int _2) const + { + BOOST_ASSERT(fptr != 0); + BOOST_ASSERT(arity() == 2); + int (*fp)(int, int) = (int(*)(int, int))(intptr_t)fptr; + return fp(_1, _2); + } + + int operator()(int _1, int _2, int _3) const + { + BOOST_ASSERT(fptr != 0); + BOOST_ASSERT(arity() == 3); + int (*fp)(int, int, int) = (int(*)(int, int, int))(intptr_t)fptr; + return fp(_1, _2, _3); + } + + unsigned arity() const { return arity_; } + bool operator!() const { return fptr == 0; } + + private: + + friend class vmachine; + function(void* fptr, unsigned arity_) + : fptr(fptr), arity_(arity_) {} + + void* fptr; + unsigned arity_; + }; + + /////////////////////////////////////////////////////////////////////////// + // The Virtual Machine (light wrapper over LLVM JIT) + /////////////////////////////////////////////////////////////////////////// + class vmachine + { + public: + + vmachine(); + + llvm::Module* module() const + { + return module_; + } + + llvm::ExecutionEngine* execution_engine() const + { + return execution_engine_; + } + + void print_assembler() const + { + module_->dump(); + } + + function get_function(char const* name) + { + llvm::Function* callee = module_->getFunction(name); + if (callee == 0) + return function(0, 0); + + // JIT the function + void *fptr = execution_engine_->getPointerToFunction(callee); + return function(fptr, callee->arg_size()); + } + + private: + + llvm::Module* module_; + llvm::ExecutionEngine* execution_engine_; + }; +} + +#endif + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/error.cnj b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/error.cnj new file mode 100644 index 00000000..2c7c0a94 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/error.cnj @@ -0,0 +1,16 @@ +/* conjure program with syntax error */ + +int foo(n) +{ + int a = 2; + if (n @ 3) /* we don't have @ operator in conjure */ + { + a = 3 + } + return a; +} + +int main() +{ + return foo(10); +}
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/factorial.cnj b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/factorial.cnj new file mode 100644 index 00000000..5ea05731 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/factorial.cnj @@ -0,0 +1,15 @@ +/* The factorial */ + +int factorial(n) +{ + if (n <= 0) + return 1; + else + return n * factorial(n-1); +} + +int main(n) +{ + return factorial(n); +} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/operators.cnj b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/operators.cnj new file mode 100644 index 00000000..8f8aad56 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/operators.cnj @@ -0,0 +1,137 @@ +/* More operators tests */ +/* Conjure >=3 only!!! */ + +// assign ops + +int aplus(x, y) +{ + int t = x; + t += y; + return t; +} + +int aminus(x, y) +{ + int t = x; + t -= y; + return t; +} + +int ashl(x, y) +{ + int t = x; + t <<= y; + return t; +} + +int adiv(x, y) +{ + int t = x; + t /= y; + return t; +} + +int amult(x, y) +{ + int t = x; + t *= y; + return t; +} + +int amod(x, y) +{ + int t = x; + t %= y; + return t; +} + +int aor(x, y) +{ + int t = x; + t |= y; + return t; +} + +int aand(x, y) +{ + int t = x; + t &= y; + return t; +} + +int axor(x, y) +{ + int t = x; + t ^= y; + return t; +} + +int ashr(x, y) +{ + int t = x; + t >>= y; + return t; +} + +// binary ops + +int plus(x, y) { return x + y; } +int minus(x, y) { return x - y; } +int shl(x, y) { return x << y; } +int div(x, y) { return x / y; } +int mult(x, y) { return x * y; } +int mod(x, y) { return x % y; } +int or(x, y) { return x | y; } +int and(x, y) { return x & y; } +int xor(x, y) { return x ^ y; } +int shr(x, y) { return x >> y; } + +int assign() +{ + int a = aplus(2, 3); // 5 + int b = ashl(a, 2); // 20 + int c = aminus(b, 2); // 18 + int d = adiv(c, 2); // 9 + int e = amult(d, c); // 162 + int f = amod(e, 10); // 2 + int g = aor(f, 45); // 47 + int h = aand(g, 48); // 32 + int j = axor(h, h); // 0 + int k = aor(j, 65535); // 65535 + int l = ashr(k, 3); // 8191 + return l; +} + +int binary() +{ + int a = plus(2, 3); // 5 + int b = shl(a, 2); // 20 + int c = minus(b, 2); // 18 + int d = div(c, 2); // 9 + int e = mult(d, c); // 162 + int f = mod(e, 10); // 2 + int g = or(f, 45); // 47 + int h = and(g, 48); // 32 + int j = xor(h, h); // 0 + int k = or(j, 65535); // 65535 + int l = shr(k, 3); // 8191 + return l; +} + +int zero() { return 0; } + +int unary() +{ + int i = ~zero(); // -1 + int j = -i; // 1 + ++j; // 2 + ++++j; // 4 + --j; // 3 + return j; +} + +int main() +{ + return assign() + binary() + unary(); // 16385 +} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/pow2.cnj b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/pow2.cnj new file mode 100644 index 00000000..4d22fd70 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/pow2.cnj @@ -0,0 +1,19 @@ +/* My first conjure program */ + +int pow2(n) +{ + int a = 2; + int i = 1; + while (i < n) + { + a = a * 2; + i = i + 1; + } + return a; +} + +int main() +{ + return pow2(10); +} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/precedence.cnj b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/precedence.cnj new file mode 100644 index 00000000..eb91b92e --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/conjure_samples/precedence.cnj @@ -0,0 +1,8 @@ +/* testing the shunting yard operator precedence algorithm */ + +int main() +{ + return 1 + 2 + 3 + 5 * 4 * 6 + 5; /* answer is 131 */ +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/annotation.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/annotation.hpp new file mode 100644 index 00000000..9e7e814e --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/annotation.hpp @@ -0,0 +1,95 @@ +/*============================================================================= + 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_MINIC_ANNOTATION_HPP) +#define BOOST_SPIRIT_MINIC_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) {} + + void operator()(ast::function_call& x) const + { + x.function_name.id = id; + } + + void operator()(ast::identifier& x) const + { + x.id = id; + } + + template <typename T> + void operator()(T& x) const + { + // no-op + } + }; + + 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::variable_declaration& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.lhs.id = id; + } + + void operator()(ast::assignment& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.lhs.id = id; + } + + void operator()(ast::return_statement& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.id = id; + } + + void operator()(ast::identifier& ast, Iterator pos) const + { + int id = iters.size(); + iters.push_back(pos); + ast.id = id; + } + }; +} + +#endif + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/ast.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/ast.hpp new file mode 100644 index 00000000..fc755064 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/ast.hpp @@ -0,0 +1,225 @@ +/*============================================================================= + 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_MINIC_AST_HPP) +#define BOOST_SPIRIT_MINIC_AST_HPP + +#include <boost/config/warning_disable.hpp> +#include <boost/variant/recursive_variant.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/fusion/include/io.hpp> +#include <boost/optional.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 unary; + struct function_call; + struct expression; + + struct identifier : tagged + { + identifier(std::string const& name = "") : name(name) {} + std::string name; + }; + + typedef boost::variant< + nil + , bool + , unsigned int + , identifier + , boost::recursive_wrapper<unary> + , boost::recursive_wrapper<function_call> + , boost::recursive_wrapper<expression> + > + operand; + + enum optoken + { + op_plus, + op_minus, + op_times, + op_divide, + op_positive, + op_negative, + op_not, + op_equal, + op_not_equal, + op_less, + op_less_equal, + op_greater, + op_greater_equal, + op_and, + op_or + }; + + struct unary + { + optoken operator_; + operand operand_; + }; + + struct operation + { + optoken operator_; + operand operand_; + }; + + struct function_call + { + identifier function_name; + std::list<expression> args; + }; + + struct expression + { + operand first; + std::list<operation> rest; + }; + + struct assignment + { + identifier lhs; + expression rhs; + }; + + struct variable_declaration + { + identifier lhs; + boost::optional<expression> rhs; + }; + + struct if_statement; + struct while_statement; + struct statement_list; + struct return_statement; + + typedef boost::variant< + variable_declaration + , assignment + , boost::recursive_wrapper<if_statement> + , boost::recursive_wrapper<while_statement> + , boost::recursive_wrapper<return_statement> + , boost::recursive_wrapper<statement_list> + > + statement; + + struct statement_list : std::list<statement> {}; + + struct if_statement + { + expression condition; + statement then; + boost::optional<statement> else_; + }; + + struct while_statement + { + expression condition; + statement body; + }; + + struct return_statement : tagged + { + boost::optional<expression> expr; + }; + + struct function + { + std::string return_type; + identifier function_name; + std::list<identifier> args; + statement_list body; + }; + + typedef std::list<function> function_list; + + // print functions for debugging + inline std::ostream& operator<<(std::ostream& out, nil) + { + out << "nil"; return out; + } + + inline std::ostream& operator<<(std::ostream& out, identifier const& id) + { + out << id.name; return out; + } +}} + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::unary, + (client::ast::optoken, operator_) + (client::ast::operand, operand_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::operation, + (client::ast::optoken, operator_) + (client::ast::operand, operand_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::function_call, + (client::ast::identifier, function_name) + (std::list<client::ast::expression>, args) +) + +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::identifier, lhs) + (boost::optional<client::ast::expression>, rhs) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::assignment, + (client::ast::identifier, lhs) + (client::ast::expression, rhs) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::if_statement, + (client::ast::expression, condition) + (client::ast::statement, then) + (boost::optional<client::ast::statement>, else_) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::while_statement, + (client::ast::expression, condition) + (client::ast::statement, body) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::return_statement, + (boost::optional<client::ast::expression>, expr) +) + +BOOST_FUSION_ADAPT_STRUCT( + client::ast::function, + (std::string, return_type) + (client::ast::identifier, function_name) + (std::list<client::ast::identifier>, args) + (client::ast::statement_list, body) +) + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/compiler.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/compiler.cpp new file mode 100644 index 00000000..5a17ee07 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/compiler.cpp @@ -0,0 +1,538 @@ +/*============================================================================= + 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> +#include <boost/lexical_cast.hpp> +#include <set> + +namespace client { namespace code_gen +{ + void function::op(int a) + { + code.push_back(a); + size_ += 1; + } + + void function::op(int a, int b) + { + code.push_back(a); + code.push_back(b); + size_ += 2; + } + + void function::op(int a, int b, int c) + { + code.push_back(a); + code.push_back(b); + code.push_back(c); + size_ += 3; + } + + int const* function::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 function::add_var(std::string const& name) + { + std::size_t n = variables.size(); + variables[name] = n; + } + + void function::link_to(std::string const& name, std::size_t address) + { + function_calls[address] = name; + } + + void function::print_assembler() const + { + std::vector<int>::const_iterator pc = code.begin() + address; + + 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; + } + + std::map<std::size_t, std::string> lines; + std::set<std::size_t> jumps; + + while (pc != (code.begin() + address + size_)) + { + std::string line; + std::size_t address = pc - code.begin(); + + switch (*pc++) + { + case op_neg: + line += " op_neg"; + break; + + case op_not: + line += " op_not"; + break; + + case op_add: + line += " op_add"; + break; + + case op_sub: + line += " op_sub"; + break; + + case op_mul: + line += " op_mul"; + break; + + case op_div: + line += " op_div"; + break; + + case op_eq: + line += " op_eq"; + break; + + case op_neq: + line += " op_neq"; + break; + + case op_lt: + line += " op_lt"; + break; + + case op_lte: + line += " op_lte"; + break; + + case op_gt: + line += " op_gt"; + break; + + case op_gte: + line += " op_gte"; + break; + + case op_and: + line += " op_and"; + break; + + case op_or: + line += " op_or"; + break; + + case op_load: + line += " op_load "; + line += locals[*pc++]; + break; + + case op_store: + line += " op_store "; + line += locals[*pc++]; + break; + + case op_int: + line += " op_int "; + line += boost::lexical_cast<std::string>(*pc++); + break; + + case op_true: + line += " op_true"; + break; + + case op_false: + line += " op_false"; + break; + + case op_jump: + { + line += " op_jump "; + std::size_t pos = (pc - code.begin()) + *pc++; + if (pos == code.size()) + line += "end"; + else + line += boost::lexical_cast<std::string>(pos); + jumps.insert(pos); + } + break; + + case op_jump_if: + { + line += " op_jump_if "; + std::size_t pos = (pc - code.begin()) + *pc++; + if (pos == code.size()) + line += "end"; + else + line += boost::lexical_cast<std::string>(pos); + jumps.insert(pos); + } + break; + + case op_call: + { + line += " op_call "; + int nargs = *pc++; + std::size_t jump = *pc++; + line += boost::lexical_cast<std::string>(nargs) + ", "; + BOOST_ASSERT(function_calls.find(jump) != function_calls.end()); + line += function_calls.find(jump)->second; + } + break; + + case op_stk_adj: + line += " op_stk_adj "; + line += boost::lexical_cast<std::string>(*pc++); + break; + + + case op_return: + line += " op_return"; + break; + } + lines[address] = line; + } + + std::cout << "start:" << std::endl; + typedef std::pair<std::size_t, std::string> line_info; + BOOST_FOREACH(line_info const& l, lines) + { + std::size_t pos = l.first; + if (jumps.find(pos) != jumps.end()) + std::cout << pos << ':' << std::endl; + std::cout << l.second << std::endl; + } + + std::cout << "end:" << std::endl << std::endl; + } + + bool compiler::operator()(unsigned int x) + { + BOOST_ASSERT(current != 0); + current->op(op_int, x); + return true; + } + + bool compiler::operator()(bool x) + { + BOOST_ASSERT(current != 0); + current->op(x ? op_true : op_false); + return true; + } + + bool compiler::operator()(ast::identifier const& x) + { + BOOST_ASSERT(current != 0); + int const* p = current->find_var(x.name); + if (p == 0) + { + error_handler(x.id, "Undeclared variable: " + x.name); + return false; + } + current->op(op_load, *p); + return true; + } + + bool compiler::operator()(ast::operation const& x) + { + BOOST_ASSERT(current != 0); + if (!boost::apply_visitor(*this, x.operand_)) + return false; + switch (x.operator_) + { + case ast::op_plus: current->op(op_add); break; + case ast::op_minus: current->op(op_sub); break; + case ast::op_times: current->op(op_mul); break; + case ast::op_divide: current->op(op_div); break; + + case ast::op_equal: current->op(op_eq); break; + case ast::op_not_equal: current->op(op_neq); break; + case ast::op_less: current->op(op_lt); break; + case ast::op_less_equal: current->op(op_lte); break; + case ast::op_greater: current->op(op_gt); break; + case ast::op_greater_equal: current->op(op_gte); break; + + case ast::op_and: current->op(op_and); break; + case ast::op_or: current->op(op_or); break; + default: BOOST_ASSERT(0); return false; + } + return true; + } + + bool compiler::operator()(ast::unary const& x) + { + BOOST_ASSERT(current != 0); + if (!boost::apply_visitor(*this, x.operand_)) + return false; + switch (x.operator_) + { + case ast::op_negative: current->op(op_neg); break; + case ast::op_not: current->op(op_not); break; + case ast::op_positive: break; + default: BOOST_ASSERT(0); return false; + } + return true; + } + + bool compiler::operator()(ast::function_call const& x) + { + BOOST_ASSERT(current != 0); + + if (functions.find(x.function_name.name) == functions.end()) + { + error_handler(x.function_name.id, "Function not found: " + x.function_name.name); + return false; + } + + boost::shared_ptr<code_gen::function> p = functions[x.function_name.name]; + + if (p->nargs() != x.args.size()) + { + error_handler(x.function_name.id, "Wrong number of arguments: " + x.function_name.name); + return false; + } + + BOOST_FOREACH(ast::expression const& expr, x.args) + { + if (!(*this)(expr)) + return false; + } + + current->op( + op_call, + p->nargs(), + p->get_address()); + current->link_to(x.function_name.name, p->get_address()); + + return true; + } + + bool compiler::operator()(ast::expression const& x) + { + BOOST_ASSERT(current != 0); + 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) + { + BOOST_ASSERT(current != 0); + if (!(*this)(x.rhs)) + return false; + int const* p = current->find_var(x.lhs.name); + if (p == 0) + { + error_handler(x.lhs.id, "Undeclared variable: " + x.lhs.name); + return false; + } + current->op(op_store, *p); + return true; + } + + bool compiler::operator()(ast::variable_declaration const& x) + { + BOOST_ASSERT(current != 0); + int const* p = current->find_var(x.lhs.name); + if (p != 0) + { + error_handler(x.lhs.id, "Duplicate variable: " + x.lhs.name); + return false; + } + if (x.rhs) // if there's an RHS initializer + { + bool r = (*this)(*x.rhs); + if (r) // don't add the variable if the RHS fails + { + current->add_var(x.lhs.name); + current->op(op_store, *current->find_var(x.lhs.name)); + } + return r; + } + else + { + current->add_var(x.lhs.name); + } + return true; + } + + bool compiler::operator()(ast::statement const& x) + { + BOOST_ASSERT(current != 0); + return boost::apply_visitor(*this, x); + } + + bool compiler::operator()(ast::statement_list const& x) + { + BOOST_ASSERT(current != 0); + BOOST_FOREACH(ast::statement const& s, x) + { + if (!(*this)(s)) + return false; + } + return true; + } + + bool compiler::operator()(ast::if_statement const& x) + { + BOOST_ASSERT(current != 0); + if (!(*this)(x.condition)) + return false; + current->op(op_jump_if, 0); // we shall fill this (0) in later + std::size_t skip = current->size()-1; // mark its position + if (!(*this)(x.then)) + return false; + (*current)[skip] = current->size()-skip; // now we know where to jump to (after the if branch) + + if (x.else_) // We got an else + { + (*current)[skip] += 2; // adjust for the "else" jump + current->op(op_jump, 0); // we shall fill this (0) in later + std::size_t exit = current->size()-1; // mark its position + if (!(*this)(*x.else_)) + return false; + (*current)[exit] = current->size()-exit;// now we know where to jump to (after the else branch) + } + + return true; + } + + bool compiler::operator()(ast::while_statement const& x) + { + BOOST_ASSERT(current != 0); + std::size_t loop = current->size(); // mark our position + if (!(*this)(x.condition)) + return false; + current->op(op_jump_if, 0); // we shall fill this (0) in later + std::size_t exit = current->size()-1; // mark its position + if (!(*this)(x.body)) + return false; + current->op(op_jump, + int(loop-1) - int(current->size())); // loop back + (*current)[exit] = current->size()-exit; // now we know where to jump to (to exit the loop) + return true; + } + + bool compiler::operator()(ast::return_statement const& x) + { + if (void_return) + { + if (x.expr) + { + error_handler(x.id, "'void' function returning a value: "); + return false; + } + } + else + { + if (!x.expr) + { + error_handler(x.id, current_function_name + " function must return a value: "); + return false; + } + } + + if (x.expr) + { + if (!(*this)(*x.expr)) + return false; + } + current->op(op_return); + return true; + } + + bool compiler::operator()(ast::function const& x) + { + void_return = x.return_type == "void"; + if (functions.find(x.function_name.name) != functions.end()) + { + error_handler(x.function_name.id, "Duplicate function: " + x.function_name.name); + return false; + } + boost::shared_ptr<code_gen::function>& p = functions[x.function_name.name]; + p.reset(new code_gen::function(code, x.args.size())); + current = p.get(); + current_function_name = x.function_name.name; + + // op_stk_adj 0 for now. we'll know how many variables + // we'll have later and add them + current->op(op_stk_adj, 0); + BOOST_FOREACH(ast::identifier const& arg, x.args) + { + current->add_var(arg.name); + } + + if (!(*this)(x.body)) + return false; + (*current)[1] = current->nvars(); // now store the actual number of variables + // this includes the arguments + return true; + } + + bool compiler::operator()(ast::function_list const& x) + { + // Jump to the main function + code.push_back(op_jump); + code.push_back(0); // we will fill this in later when we finish compiling + // and we know where the main function is + + BOOST_FOREACH(ast::function const& f, x) + { + if (!(*this)(f)) + { + code.clear(); + return false; + } + } + // find the main function + boost::shared_ptr<code_gen::function> p = + find_function("main"); + + if (!p) // main function not found + { + std::cerr << "Error: main function not defined" << std::endl; + return false; + } + code[1] = p->get_address()-1; // jump to this (main function) address + + return true; + } + + void compiler::print_assembler() const + { + typedef std::pair<std::string, boost::shared_ptr<code_gen::function> > pair; + BOOST_FOREACH(pair const& p, functions) + { + std::cout << ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" << std::endl; + std::cout << p.second->get_address() << ": function " << p.first << std::endl; + p.second->print_assembler(); + } + } + + boost::shared_ptr<code_gen::function> + compiler::find_function(std::string const& name) const + { + function_table::const_iterator i = functions.find(name); + if (i == functions.end()) + return boost::shared_ptr<code_gen::function>(); + else + return i->second; + } +}} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/compiler.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/compiler.hpp new file mode 100644 index 00000000..8a3eea72 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/compiler.hpp @@ -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) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_MINIC_COMPILER_HPP) +#define BOOST_SPIRIT_MINIC_COMPILER_HPP + +#include "ast.hpp" +#include "error_handler.hpp" +#include <vector> +#include <map> +#include <boost/function.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_function.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> + +namespace client { namespace code_gen +{ + /////////////////////////////////////////////////////////////////////////// + // The Function + /////////////////////////////////////////////////////////////////////////// + struct function + { + function(std::vector<int>& code, int nargs) + : code(code), address(code.size()), size_(0), nargs_(nargs) {} + + 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[address+i]; } + int const& operator[](std::size_t i) const { return code[address+i]; } + std::size_t size() const { return size_; } + std::size_t get_address() const { return address; } + + int nargs() const { return nargs_; } + int nvars() const { return variables.size(); } + int const* find_var(std::string const& name) const; + void add_var(std::string const& name); + void link_to(std::string const& name, std::size_t address); + + void print_assembler() const; + + private: + + std::map<std::string, int> variables; + std::map<std::size_t, std::string> function_calls; + std::vector<int>& code; + std::size_t address; + std::size_t size_; + std::size_t nargs_; + }; + + /////////////////////////////////////////////////////////////////////////// + // The Compiler + /////////////////////////////////////////////////////////////////////////// + struct compiler + { + typedef bool result_type; + + template <typename ErrorHandler> + compiler(ErrorHandler& error_handler_) + : current(0) + { + 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) { BOOST_ASSERT(0); return false; } + bool operator()(unsigned int x); + bool operator()(bool x); + bool operator()(ast::identifier const& x); + bool operator()(ast::operation const& x); + bool operator()(ast::unary const& x); + bool operator()(ast::function_call const& x); + bool operator()(ast::expression const& x); + bool operator()(ast::assignment const& x); + bool operator()(ast::variable_declaration const& x); + bool operator()(ast::statement_list const& x); + bool operator()(ast::statement const& x); + bool operator()(ast::if_statement const& x); + bool operator()(ast::while_statement const& x); + bool operator()(ast::return_statement const& x); + bool operator()(ast::function const& x); + bool operator()(ast::function_list const& x); + + void print_assembler() const; + + boost::shared_ptr<code_gen::function> + find_function(std::string const& name) const; + + std::vector<int>& get_code() { return code; } + std::vector<int> const& get_code() const { return code; } + + private: + + typedef std::map<std::string, boost::shared_ptr<code_gen::function> > function_table; + + std::vector<int> code; + code_gen::function* current; + std::string current_function_name; + function_table functions; + bool void_return; + + boost::function< + void(int tag, std::string const& what)> + error_handler; + }; +}} + +#endif diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/error_handler.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/error_handler.hpp new file mode 100644 index 00000000..4ba833c6 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/error_handler.hpp @@ -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_MINIC_ERROR_HANDLER_HPP) +#define BOOST_SPIRIT_MINIC_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 + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/expression.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/expression.cpp new file mode 100644 index 00000000..32f44adc --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/expression.cpp @@ -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>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/expression.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/expression.hpp new file mode 100644 index 00000000..60edceec --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/expression.hpp @@ -0,0 +1,79 @@ +/*============================================================================= + 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_MINIC_EXPRESSION_HPP) +#define BOOST_SPIRIT_MINIC_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 "skipper.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(), skipper<Iterator> > + { + expression(error_handler<Iterator>& error_handler); + + qi::rule<Iterator, ast::expression(), skipper<Iterator> > + expr, equality_expr, relational_expr, + logical_or_expr, logical_and_expr, + additive_expr, multiplicative_expr + ; + + qi::rule<Iterator, ast::operand(), skipper<Iterator> > + unary_expr, primary_expr + ; + + qi::rule<Iterator, ast::function_call(), skipper<Iterator> > + function_call + ; + + qi::rule<Iterator, std::list<ast::expression>(), skipper<Iterator> > + argument_list + ; + + qi::rule<Iterator, std::string(), skipper<Iterator> > + identifier + ; + + qi::symbols<char, ast::optoken> + logical_or_op, logical_and_op, + equality_op, relational_op, + additive_op, multiplicative_op, unary_op + ; + + qi::symbols<char> + keywords + ; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/expression_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/expression_def.hpp new file mode 100644 index 00000000..f6f7c5d6 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/expression_def.hpp @@ -0,0 +1,181 @@ +/*============================================================================= + 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/spirit/include/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; + qi::bool_type bool_; + + 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; + + /////////////////////////////////////////////////////////////////////// + // Tokens + logical_or_op.add + ("||", ast::op_or) + ; + + logical_and_op.add + ("&&", ast::op_and) + ; + + equality_op.add + ("==", ast::op_equal) + ("!=", ast::op_not_equal) + ; + + relational_op.add + ("<", ast::op_less) + ("<=", ast::op_less_equal) + (">", ast::op_greater) + (">=", ast::op_greater_equal) + ; + + additive_op.add + ("+", ast::op_plus) + ("-", ast::op_minus) + ; + + multiplicative_op.add + ("*", ast::op_times) + ("/", ast::op_divide) + ; + + unary_op.add + ("+", ast::op_positive) + ("-", ast::op_negative) + ("!", ast::op_not) + ; + + keywords.add + ("true") + ("false") + ("if") + ("else") + ("while") + ("int") + ("void") + ("return") + ; + + /////////////////////////////////////////////////////////////////////// + // Main expression grammar + expr = + logical_or_expr.alias() + ; + + logical_or_expr = + logical_and_expr + >> *(logical_or_op > logical_and_expr) + ; + + logical_and_expr = + equality_expr + >> *(logical_and_op > equality_expr) + ; + + equality_expr = + relational_expr + >> *(equality_op > relational_expr) + ; + + relational_expr = + additive_expr + >> *(relational_op > additive_expr) + ; + + additive_expr = + multiplicative_expr + >> *(additive_op > multiplicative_expr) + ; + + multiplicative_expr = + unary_expr + >> *(multiplicative_op > unary_expr) + ; + + unary_expr = + primary_expr + | (unary_op > unary_expr) + ; + + primary_expr = + uint_ + | function_call + | identifier + | bool_ + | '(' > expr > ')' + ; + + function_call = + (identifier >> '(') + > argument_list + > ')' + ; + + argument_list = -(expr % ','); + + identifier = + !lexeme[keywords >> !(alnum | '_')] + >> raw[lexeme[(alpha | '_') >> *(alnum | '_')]] + ; + + /////////////////////////////////////////////////////////////////////// + // Debugging and error handling and reporting support. + BOOST_SPIRIT_DEBUG_NODES( + (expr) + (logical_or_expr) + (logical_and_expr) + (equality_expr) + (relational_expr) + (additive_expr) + (multiplicative_expr) + (unary_expr) + (primary_expr) + (function_call) + (argument_list) + (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)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/function.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/function.cpp new file mode 100644 index 00000000..ababd4ad --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/function.cpp @@ -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 "function_def.hpp" + +typedef std::string::const_iterator iterator_type; +template struct client::parser::function<iterator_type>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/function.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/function.hpp new file mode 100644 index 00000000..cef1c82d --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/function.hpp @@ -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_MINIC_FUNCTION_HPP) +#define BOOST_SPIRIT_MINIC_FUNCTION_HPP + +#include "statement.hpp" + +namespace client { namespace parser +{ + /////////////////////////////////////////////////////////////////////////////// + // The function grammar + /////////////////////////////////////////////////////////////////////////////// + template <typename Iterator> + struct function : qi::grammar<Iterator, ast::function(), skipper<Iterator> > + { + function(error_handler<Iterator>& error_handler); + + statement<Iterator> body; + qi::rule<Iterator, std::string(), skipper<Iterator> > name; + qi::rule<Iterator, ast::identifier(), skipper<Iterator> > identifier; + qi::rule<Iterator, std::list<ast::identifier>(), skipper<Iterator> > argument_list; + qi::rule<Iterator, ast::function(), skipper<Iterator> > start; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/function_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/function_def.hpp new file mode 100644 index 00000000..bd2c7d02 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/function_def.hpp @@ -0,0 +1,71 @@ +/*============================================================================= + 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 "function.hpp" +#include "error_handler.hpp" +#include "annotation.hpp" + +namespace client { namespace parser +{ + template <typename Iterator> + function<Iterator>::function(error_handler<Iterator>& error_handler) + : function::base_type(start), body(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; + qi::string_type string; + + 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; + + name = + !body.expr.keywords + >> raw[lexeme[(alpha | '_') >> *(alnum | '_')]] + ; + + identifier = name; + argument_list = -(identifier % ','); + + start = + lexeme[(string("void") | string("int")) + >> !(alnum | '_')] // make sure we have whole words + > identifier + > '(' > argument_list > ')' + > '{' > body > '}' + ; + + // Debugging and error handling and reporting support. + BOOST_SPIRIT_DEBUG_NODES( + (identifier) + (argument_list) + (start) + ); + + // Error handling: on error in start, call error_handler. + on_error<fail>(start, + error_handler_function(error_handler)( + "Error! Expecting ", _4, _3)); + + // Annotation: on success in start, call annotation. + on_success(identifier, + annotation_function(error_handler.iters)(_val, _1)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/main.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/main.cpp new file mode 100644 index 00000000..27d4bee0 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/main.cpp @@ -0,0 +1,121 @@ +/*============================================================================= + 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) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Not a calculator anymore, right? :-) +// +// [ JDG April 10, 2007 ] spirit2 +// [ JDG February 18, 2011 ] Pure attributes. No semantic actions. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "function.hpp" +#include "skipper.hpp" +#include "vm.hpp" +#include "compiler.hpp" +#include <boost/lexical_cast.hpp> +#include <fstream> + +/////////////////////////////////////////////////////////////////////////////// +// 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 source_code; // 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(source_code)); + + typedef std::string::const_iterator iterator_type; + iterator_type iter = source_code.begin(); + iterator_type end = source_code.end(); + + client::vmachine vm; // Our virtual machine + client::ast::function_list ast; // Our AST + + client::error_handler<iterator_type> + error_handler(iter, end); // Our error handler + client::parser::function<iterator_type> + function(error_handler); // Our parser + client::parser::skipper<iterator_type> + skipper; // Our skipper + client::code_gen::compiler + compiler(error_handler); // Our compiler + + + bool success = phrase_parse(iter, end, +function, skipper, ast); + + std::cout << "-------------------------\n"; + + if (success && iter == end) + { + if (compiler(ast)) + { + boost::shared_ptr<client::code_gen::function> + p = compiler.find_function("main"); + if (!p) + return 1; + + int nargs = argc-2; + if (p->nargs() != nargs) + { + std::cerr << "Error: main function requires " << p->nargs() << " arguments." << std::endl; + std::cerr << nargs << "supplied." << std::endl; + return 1; + } + + std::cout << "Success\n"; + std::cout << "-------------------------\n"; + std::cout << "Assembler----------------\n\n"; + compiler.print_assembler(); + + // Push the arguments into our stack + for (int i = 0; i < nargs; ++i) + vm.get_stack()[i] = boost::lexical_cast<int>(argv[i+2]); + + // Call the interpreter + int r = vm.execute(compiler.get_code()); + + std::cout << "-------------------------\n"; + std::cout << "Result: " << r << std::endl; + std::cout << "-------------------------\n\n"; + } + else + { + std::cout << "Compile failure\n"; + } + } + else + { + std::cout << "Parse failure\n"; + } + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/skipper.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/skipper.hpp new file mode 100644 index 00000000..041fb9b0 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/skipper.hpp @@ -0,0 +1,40 @@ +/*============================================================================= + 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_MINIC_SKIPPER_HPP) +#define BOOST_SPIRIT_MINIC_SKIPPER_HPP + +#include <boost/spirit/include/qi.hpp> + +namespace client { namespace parser +{ + namespace qi = boost::spirit::qi; + namespace ascii = boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////////////// + // The skipper grammar + /////////////////////////////////////////////////////////////////////////////// + template <typename Iterator> + struct skipper : qi::grammar<Iterator> + { + skipper() : skipper::base_type(start) + { + qi::char_type char_; + ascii::space_type space; + + start = + space // tab/space/cr/lf + | "/*" >> *(char_ - "*/") >> "*/" // C-style comments + ; + } + + qi::rule<Iterator> start; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/statement.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/statement.cpp new file mode 100644 index 00000000..54bc9ac3 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/statement.cpp @@ -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>; diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/statement.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/statement.hpp new file mode 100644 index 00000000..366f8741 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/statement.hpp @@ -0,0 +1,38 @@ +/*============================================================================= + 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_MINIC_STATEMENT_HPP) +#define BOOST_SPIRIT_MINIC_STATEMENT_HPP + +#include "expression.hpp" + +namespace client { namespace parser +{ + /////////////////////////////////////////////////////////////////////////////// + // The statement grammar + /////////////////////////////////////////////////////////////////////////////// + template <typename Iterator> + struct statement : qi::grammar<Iterator, ast::statement_list(), skipper<Iterator> > + { + statement(error_handler<Iterator>& error_handler); + + expression<Iterator> expr; + qi::rule<Iterator, ast::statement_list(), skipper<Iterator> > + statement_list, compound_statement; + + qi::rule<Iterator, ast::statement(), skipper<Iterator> > statement_; + qi::rule<Iterator, ast::variable_declaration(), skipper<Iterator> > variable_declaration; + qi::rule<Iterator, ast::assignment(), skipper<Iterator> > assignment; + qi::rule<Iterator, ast::if_statement(), skipper<Iterator> > if_statement; + qi::rule<Iterator, ast::while_statement(), skipper<Iterator> > while_statement; + qi::rule<Iterator, ast::return_statement(), skipper<Iterator> > return_statement; + qi::rule<Iterator, std::string(), skipper<Iterator> > identifier; + }; +}} + +#endif + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/statement_def.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/statement_def.hpp new file mode 100644 index 00000000..f99b665a --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/statement_def.hpp @@ -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) +=============================================================================*/ +#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; + qi::lit_type lit; + + 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 = + +statement_ + ; + + statement_ = + variable_declaration + | assignment + | compound_statement + | if_statement + | while_statement + | return_statement + ; + + identifier = + !expr.keywords + >> raw[lexeme[(alpha | '_') >> *(alnum | '_')]] + ; + + variable_declaration = + lexeme["int" >> !(alnum | '_')] // make sure we have whole words + > identifier + > -('=' > expr) + > ';' + ; + + assignment = + identifier + > '=' + > expr + > ';' + ; + + if_statement = + lit("if") + > '(' + > expr + > ')' + > statement_ + > + -( + lexeme["else" >> !(alnum | '_')] // make sure we have whole words + > statement_ + ) + ; + + while_statement = + lit("while") + > '(' + > expr + > ')' + > statement_ + ; + + compound_statement = + '{' >> -statement_list >> '}' + ; + + return_statement = + lexeme["return" >> !(alnum | '_')] // make sure we have whole words + > -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 variable_declaration, + // assignment and return_statement, call annotation. + on_success(variable_declaration, + annotation_function(error_handler.iters)(_val, _1)); + on_success(assignment, + annotation_function(error_handler.iters)(_val, _1)); + on_success(return_statement, + annotation_function(error_handler.iters)(_val, _1)); + } +}} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/vm.cpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/vm.cpp new file mode 100644 index 00000000..8740bf9c --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/vm.cpp @@ -0,0 +1,159 @@ +/*============================================================================= + 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" + +#if defined(_MSC_VER) +# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' + // (performance warning) +#endif + +namespace client +{ + int vmachine::execute( + std::vector<int> const& code + , std::vector<int>::const_iterator pc + , std::vector<int>::iterator frame_ptr + ) + { + std::vector<int>::iterator stack_ptr = frame_ptr; + + while (true) + { + switch (*pc++) + { + case op_neg: + stack_ptr[-1] = -stack_ptr[-1]; + break; + + case op_not: + stack_ptr[-1] = !bool(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_eq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] == stack_ptr[0]); + break; + + case op_neq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] != stack_ptr[0]); + break; + + case op_lt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] < stack_ptr[0]); + break; + + case op_lte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] <= stack_ptr[0]); + break; + + case op_gt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] > stack_ptr[0]); + break; + + case op_gte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] >= stack_ptr[0]); + break; + + case op_and: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) && bool(stack_ptr[0]); + break; + + case op_or: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) || bool(stack_ptr[0]); + break; + + case op_load: + *stack_ptr++ = frame_ptr[*pc++]; + break; + + case op_store: + --stack_ptr; + frame_ptr[*pc++] = stack_ptr[0]; + break; + + case op_int: + *stack_ptr++ = *pc++; + break; + + case op_true: + *stack_ptr++ = true; + break; + + case op_false: + *stack_ptr++ = false; + break; + + case op_jump: + pc += *pc; + break; + + case op_jump_if: + if (!bool(stack_ptr[-1])) + pc += *pc; + else + ++pc; + --stack_ptr; + break; + + case op_stk_adj: + stack_ptr += *pc++; + break; + + case op_call: + { + int nargs = *pc++; + int jump = *pc++; + + // a function call is a recursive call to execute + int r = execute( + code + , code.begin() + jump + , stack_ptr - nargs + ); + + // cleanup after return from function + stack_ptr[-nargs] = r; // get return value + stack_ptr -= (nargs - 1); // the stack will now contain + // the return value + } + break; + + case op_return: + return stack_ptr[-1]; + } + } + } +} + + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/vm.hpp b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/vm.hpp new file mode 100644 index 00000000..eb4cc294 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c/vm.hpp @@ -0,0 +1,82 @@ +/*============================================================================= + 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_MINIC_VM_HPP) +#define BOOST_SPIRIT_MINIC_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_not, // boolean negate the top stack entry + op_eq, // compare the top two stack entries for == + op_neq, // compare the top two stack entries for != + op_lt, // compare the top two stack entries for < + op_lte, // compare the top two stack entries for <= + op_gt, // compare the top two stack entries for > + op_gte, // compare the top two stack entries for >= + + op_and, // logical and top two stack entries + op_or, // logical or top two stack entries + + op_load, // load a variable + op_store, // store a variable + + op_int, // push constant integer into the stack + op_true, // push constant 0 into the stack + op_false, // push constant 1 into the stack + + op_jump_if, // jump to a relative position in the code if top stack + // evaluates to false + op_jump, // jump to a relative position in the code + + op_stk_adj, // adjust the stack (for args and locals) + op_call, // function call + op_return // return from function + }; + + class vmachine + { + public: + + vmachine(unsigned stackSize = 4096) + : stack(stackSize) + { + } + + int execute(std::vector<int> const& code) + { + return execute(code, code.begin(), stack.begin()); + } + + std::vector<int> const& get_stack() const { return stack; }; + std::vector<int>& get_stack() { return stack; }; + + private: + + int execute( + std::vector<int> const& code // the program code + , std::vector<int>::const_iterator pc // program counter + , std::vector<int>::iterator frame_ptr // start of arguments and locals + ); + + std::vector<int> stack; + }; +} + +#endif + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c_samples/1.mini b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c_samples/1.mini new file mode 100644 index 00000000..bd22c869 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c_samples/1.mini @@ -0,0 +1,19 @@ +/* My first mini program */ + +int pow2(n) +{ + int a = 2; + int i = 1; + while (i < n) + { + a = a * 2; + i = i + 1; + } + return a; +} + +int main() +{ + return pow2(10); +} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c_samples/2.mini b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c_samples/2.mini new file mode 100644 index 00000000..5ea05731 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c_samples/2.mini @@ -0,0 +1,15 @@ +/* The factorial */ + +int factorial(n) +{ + if (n <= 0) + return 1; + else + return n * factorial(n-1); +} + +int main(n) +{ + return factorial(n); +} + diff --git a/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c_samples/3.mini b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c_samples/3.mini new file mode 100644 index 00000000..5c407d6e --- /dev/null +++ b/src/boost/libs/spirit/example/qi/compiler_tutorial/mini_c_samples/3.mini @@ -0,0 +1,17 @@ +/* mini program with syntax error */ + +int foo(n) +{ + int a = 2; + if (n @ 3) /* we don't have @ operator in mini_c */ + { + a = 3 + } + return a; +} + +int main() +{ + return foo(10); +} + diff --git a/src/boost/libs/spirit/example/qi/complex_number.cpp b/src/boost/libs/spirit/example/qi/complex_number.cpp new file mode 100644 index 00000000..a4400e0c --- /dev/null +++ b/src/boost/libs/spirit/example/qi/complex_number.cpp @@ -0,0 +1,101 @@ +/*============================================================================= + 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) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A complex number micro parser. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG May 9, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> + +#include <iostream> +#include <string> +#include <complex> + +/////////////////////////////////////////////////////////////////////////////// +// Our complex number parser/compiler +/////////////////////////////////////////////////////////////////////////////// +//[tutorial_complex_number +namespace client +{ + 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, + + // Begin grammar + ( + '(' >> double_[ref(rN) = _1] + >> -(',' >> double_[ref(iN) = _1]) >> ')' + | double_[ref(rN) = _1] + ), + // End grammar + + space); + + if (!r || first != last) // fail if we did not get a full match + return false; + c = std::complex<double>(rN, iN); + return r; + } +} +//] + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA complex number micro parser 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::cout << "Parsing succeeded\n"; + std::cout << "got: " << c << std::endl; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/custom_string.cpp b/src/boost/libs/spirit/example/qi/custom_string.cpp new file mode 100644 index 00000000..e5e78221 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/custom_string.cpp @@ -0,0 +1,113 @@ +// 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 the steps needed to integrate a custom container +// type to be usable with Spirit.Qi. It shows how to utilize the QString data +// type as a Qi attribute. + +// Note: you need to have Qt4 installed on your system and you have to make +// sure your compiler finds the includes and your linker finds the +// proper libraries. + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> + +#include <Qt/qstring.h> + +namespace boost { namespace spirit { namespace traits +{ + // Make Qi recognize QString as a container + template <> struct is_container<QString> : mpl::true_ {}; + + // Expose the container's (QString's) value_type + template <> struct container_value<QString> : mpl::identity<QChar> {}; + + // Define how to insert a new element at the end of the container (QString) + template <> + struct push_back_container<QString, QChar> + { + static bool call(QString& c, QChar const& val) + { + c.append(val); + return true; + } + }; + + // Test if a QString is empty (required for debug) + template <> + struct is_empty_container<QString> + { + static bool call(QString const& c) + { + return c.isEmpty(); + } + }; + + // Define how to stream a QString (required for debug) + template <typename Out, typename Enable> + struct print_attribute_debug<Out, QString, Enable> + { + static void call(Out& out, QString const& val) + { + out << val.toStdString(); + } + }; +}}} + +/////////////////////////////////////////////////////////////////////////////// +namespace client +{ + template <typename Iterator> + bool parse_qstring(Iterator first, Iterator last, QString& t) + { + using boost::spirit::qi::char_; + using boost::spirit::ascii::space; + using boost::spirit::qi::phrase_parse; + + bool r = phrase_parse(first, last, +char_, space, t); + if (!r || first != last) // fail if we did not get a full match + return false; + + return r; + } +} + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tParsing into a QString from 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; + + QString t; + if (client::parse_qstring(str.begin(), str.end(), t)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "got: " << t.toStdString() << std::endl; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/display_attribute_type.cpp b/src/boost/libs/spirit/example/qi/display_attribute_type.cpp new file mode 100644 index 00000000..11f086b5 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/display_attribute_type.cpp @@ -0,0 +1,22 @@ +// 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 implements a simple utility allowing to print the attribute +// type as it is exposed by an arbitrary Qi parser expression. Just insert +// your expression below, compile and run this example to see what Qi is +// seeing! + +#include "display_attribute_type.hpp" + +namespace qi = boost::spirit::qi; + +int main() +{ + tools::display_attribute_of_parser( + std::cerr, // put the required output stream here + qi::int_ >> qi::double_ // put your parser expression here + ); + return 0; +} diff --git a/src/boost/libs/spirit/example/qi/display_attribute_type.hpp b/src/boost/libs/spirit/example/qi/display_attribute_type.hpp new file mode 100644 index 00000000..84c2245d --- /dev/null +++ b/src/boost/libs/spirit/example/qi/display_attribute_type.hpp @@ -0,0 +1,59 @@ +// 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 implements a simple utility allowing to print the attribute +// type as it is exposed by an arbitrary Qi parser expression. Just insert +// your expression below, compile and run this example to see what Qi is +// seeing! + +#if !defined (DISPLAY_ATTRIBUTE_OF_PARSER_JAN_2010_30_0722PM) +#define DISPLAY_ATTRIBUTE_OF_PARSER_JAN_2010_30_0722PM + +#include <iostream> +#include <boost/spirit/include/qi.hpp> + +namespace tools +{ + namespace spirit = boost::spirit; + + template <typename Expr, typename Iterator = spirit::unused_type> + struct attribute_of_parser + { + typedef typename spirit::result_of::compile< + spirit::qi::domain, Expr + >::type parser_expression_type; + + typedef typename spirit::traits::attribute_of< + parser_expression_type, spirit::unused_type, Iterator + >::type type; + }; + + template <typename T> + void display_attribute_of_parser(T const &) + { + // Report invalid expression error as early as possible. + // If you got an error_invalid_expression error message here, + // then the expression (expr) is not a valid spirit qi expression. + BOOST_SPIRIT_ASSERT_MATCH(spirit::qi::domain, T); + + typedef typename attribute_of_parser<T>::type type; + std::cout << typeid(type).name() << std::endl; + } + + template <typename T> + void display_attribute_of_parser(std::ostream& os, T const &) + { + // Report invalid expression error as early as possible. + // If you got an error_invalid_expression error message here, + // then the expression (expr) is not a valid spirit qi expression. + BOOST_SPIRIT_ASSERT_MATCH(spirit::qi::domain, T); + + typedef typename attribute_of_parser<T>::type type; + os << typeid(type).name() << std::endl; + } +} + +#endif + diff --git a/src/boost/libs/spirit/example/qi/employee.cpp b/src/boost/libs/spirit/example/qi/employee.cpp new file mode 100644 index 00000000..c303ff8b --- /dev/null +++ b/src/boost/libs/spirit/example/qi/employee.cpp @@ -0,0 +1,151 @@ +/*============================================================================= + 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) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A parser for arbitrary tuples. This example presents a parser +// for an employee structure. +// +// [ JDG May 9, 2007 ] +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_object.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/fusion/include/io.hpp> + +#include <iostream> +#include <string> +#include <complex> + +namespace client +{ + namespace qi = boost::spirit::qi; + namespace ascii = boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////////// + // Our employee struct + /////////////////////////////////////////////////////////////////////////// + //[tutorial_employee_struct + struct employee + { + int age; + std::string surname; + std::string forename; + double salary; + }; + //] +} + +// We need to tell fusion about our employee struct +// to make it a first-class fusion citizen. This has to +// be in global scope. + +//[tutorial_employee_adapt_struct +BOOST_FUSION_ADAPT_STRUCT( + client::employee, + (int, age) + (std::string, surname) + (std::string, forename) + (double, salary) +) +//] + +namespace client +{ + /////////////////////////////////////////////////////////////////////////////// + // Our employee parser + /////////////////////////////////////////////////////////////////////////////// + //[tutorial_employee_parser + template <typename Iterator> + struct employee_parser : qi::grammar<Iterator, employee(), ascii::space_type> + { + employee_parser() : employee_parser::base_type(start) + { + using qi::int_; + using qi::lit; + using qi::double_; + using qi::lexeme; + using ascii::char_; + + quoted_string %= lexeme['"' >> +(char_ - '"') >> '"']; + + start %= + lit("employee") + >> '{' + >> int_ >> ',' + >> quoted_string >> ',' + >> quoted_string >> ',' + >> double_ + >> '}' + ; + } + + qi::rule<Iterator, std::string(), ascii::space_type> quoted_string; + qi::rule<Iterator, employee(), ascii::space_type> start; + }; + //] +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tAn employee parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout + << "Give me an employee of the form :" + << "employee{age, \"surname\", \"forename\", salary } \n"; + std::cout << "Type [q or Q] to quit\n\n"; + + using boost::spirit::ascii::space; + typedef std::string::const_iterator iterator_type; + typedef client::employee_parser<iterator_type> employee_parser; + + employee_parser g; // Our grammar + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + client::employee emp; + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = phrase_parse(iter, end, g, space, emp); + + if (r && iter == end) + { + std::cout << boost::fusion::tuple_open('['); + std::cout << boost::fusion::tuple_close(']'); + std::cout << boost::fusion::tuple_delimiter(", "); + + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "got: " << boost::fusion::as_vector(emp) << std::endl; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/expect.cpp b/src/boost/libs/spirit/example/qi/expect.cpp new file mode 100644 index 00000000..ecbc24aa --- /dev/null +++ b/src/boost/libs/spirit/example/qi/expect.cpp @@ -0,0 +1,112 @@ +/*============================================================================= +Copyright (c) 2016 Frank Hein, maxence business consulting gmbh + +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 <map> + +#include <boost/spirit/home/qi.hpp> +#include <boost/spirit/home/qi/nonterminal/grammar.hpp> +#include <boost/spirit/include/phoenix.hpp> +#include <boost/foreach.hpp> + +namespace qi = boost::spirit::qi; + +typedef std::string::const_iterator iterator_type; +typedef std::string result_type; + +template<typename Parser> +void parse(const std::string message, const std::string& input, const std::string& rule, const Parser& parser) { + iterator_type iter = input.begin(), end = input.end(); + + std::vector<result_type> parsed_result; + + std::cout << "-------------------------\n"; + std::cout << message << "\n"; + std::cout << "Rule: " << rule << std::endl; + std::cout << "Parsing: \"" << input << "\"\n"; + + bool result = qi::phrase_parse(iter, end, parser, qi::space, parsed_result); + if (result) + { + std::cout << "Parser succeeded.\n"; + std::cout << "Parsed " << parsed_result.size() << " elements:"; + BOOST_FOREACH(result_type const& str, parsed_result) + { + std::cout << "[" << str << "]"; + } + std::cout << std::endl; + } + else + { + std::cout << "Parser failed" << std::endl; + } + if (iter == end) { + std::cout << "EOI reached." << std::endl; + } + else { + std::cout << "EOI not reached. Unparsed: \"" << std::string(iter, end) << "\"" << std::endl; + } + std::cout << "-------------------------\n"; + +} + +namespace grammars { + namespace phx = boost::phoenix; + + template <typename Iterator> + struct ident : qi::grammar < Iterator, std::string(), qi::space_type> + { + ident(); + + qi::rule <iterator_type, std::string(), qi::space_type> + id, id_list, qualified_id; + }; + + template <typename Iterator> + ident<Iterator>::ident() : ident::base_type(id_list) { + + using qi::on_error; + using qi::fail; + using qi::expect; + + id = (qi::alpha | qi::char_('_')) >> *(qi::alnum | qi::char_('_')); + + id_list = expect[id >> qi::lit(';')]; + + on_error<fail>(id_list, + phx::ref(std::cout) + << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl + << "Error! Expecting " + << qi::_4 + << " here: " + << phx::construct<std::string>(qi::_3, qi::_2) << std::endl + << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl + ); + } +} + +int main() { + + grammars::ident<iterator_type> id; + + parse("expect directive, fail on first" + , "1234; id2;" + , "qi::expect[ id >> qi::lit(';') ]" + , id); + + parse("expect directive, fail on second" + , "id1, id2" + , "qi::expect[ id >> qi::lit(';') ]" + , id); + + parse("expect directive, success" + , "id1;" + , "qi::expect[ id >> qi::lit(';') ]" + , id); + + return 0; +} diff --git a/src/boost/libs/spirit/example/qi/german_floating_point.cpp b/src/boost/libs/spirit/example/qi/german_floating_point.cpp new file mode 100644 index 00000000..c091ca92 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/german_floating_point.cpp @@ -0,0 +1,48 @@ +// Copyright (c) 2001-2011 Hartmut Kaiser +// Copyright (c) 2011 Michael Caisse +// +// 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> + +namespace qi = boost::spirit::qi; + +template <typename T> +struct german_real_policies : qi::real_policies<T> +{ + template <typename Iterator> + static bool parse_dot(Iterator& first, Iterator const& last) + { + if (first == last || *first != ',') + return false; + ++first; + return true; + } +}; + +qi::real_parser<double, german_real_policies<double> > const german_double; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + std::string input("123,456"); + std::string::iterator begin = input.begin(); + std::string::iterator end = input.end(); + + double value = 0; + if (!qi::parse(begin, end, german_double, value)) + { + std::cout << "-------------------------------- \n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------------- \n"; + } + else + { + std::cout << "-------------------------------- \n"; + std::cout << "Parsing succeeded, got: " << value << "\n"; + std::cout << "---------------------------------\n"; + } + return 0; +} + diff --git a/src/boost/libs/spirit/example/qi/iter_pos.hpp b/src/boost/libs/spirit/example/qi/iter_pos.hpp new file mode 100644 index 00000000..b6259fd7 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/iter_pos.hpp @@ -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) + +#if !defined(ITER_POS_NOV_20_2009_1245PM) +#define ITER_POS_NOV_20_2009_1245PM + +#include <boost/spirit/include/qi_parse.hpp> + +/////////////////////////////////////////////////////////////////////////////// +// definition the place holder +namespace custom_parser +{ + BOOST_SPIRIT_TERMINAL(iter_pos) +} + +/////////////////////////////////////////////////////////////////////////////// +// implementation the enabler +namespace boost { namespace spirit +{ + // We want custom_parser::iter_pos to be usable as a terminal only, + // and only for parser expressions (qi::domain). + template <> + struct use_terminal<qi::domain, custom_parser::tag::iter_pos> + : mpl::true_ + {}; +}} + +/////////////////////////////////////////////////////////////////////////////// +// implementation of the parser +namespace custom_parser +{ + struct iter_pos_parser + : boost::spirit::qi::primitive_parser<iter_pos_parser> + { + // Define the attribute type exposed by this parser component + template <typename Context, typename Iterator> + struct attribute + { + typedef Iterator type; + }; + + // This function is called during the actual parsing process + template <typename Iterator, typename Context + , typename Skipper, typename Attribute> + bool parse(Iterator& first, Iterator const& last + , Context&, Skipper const& skipper, Attribute& attr) const + { + boost::spirit::qi::skip_over(first, last, skipper); + boost::spirit::traits::assign_to(first, attr); + return true; + } + + // 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&) const + { + return boost::spirit::info("iter_pos"); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +// instantiation of the parser +namespace boost { namespace spirit { namespace qi +{ + // This is the factory function object invoked in order to create + // an instance of our iter_pos_parser. + template <typename Modifiers> + struct make_primitive<custom_parser::tag::iter_pos, Modifiers> + { + typedef custom_parser::iter_pos_parser result_type; + + result_type operator()(unused_type, unused_type) const + { + return result_type(); + } + }; +}}} + +#endif diff --git a/src/boost/libs/spirit/example/qi/iter_pos_parser.cpp b/src/boost/libs/spirit/example/qi/iter_pos_parser.cpp new file mode 100644 index 00000000..92ff4b00 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/iter_pos_parser.cpp @@ -0,0 +1,51 @@ +// 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 primitive parser +// component can be written. We develop a custom parser exposing the current +// iterator position as its attribute. +// +// For more information see: http://spirit.sourceforge.net/home/?page_id=567 + +#include <boost/spirit/include/qi_parse_attr.hpp> +#include <boost/spirit/include/qi_char.hpp> +#include <boost/spirit/include/qi_operator.hpp> + +#include <string> +#include "iter_pos.hpp" + +namespace qi = boost::spirit::qi; + +int main() +{ + using custom_parser::iter_pos; + + std::string prefix, suffix; // attributes receiving the + std::string::iterator position; // parsed values + + std::string input("prefix1234567"); + std::string::iterator first = input.begin(); + bool result = + qi::parse(first, input.end() + , +qi::alpha >> iter_pos >> +qi::digit + , prefix, position, suffix); + + if (result) + { + std::cout << "-------------------------------- \n"; + std::cout << "Parsing succeeded\n"; + std::cout << "prefix is: " << prefix << "\n"; + std::cout << "suffix is: " << suffix << "\n"; + std::cout << "position is: " << std::distance(input.begin(), position) << "\n"; + std::cout << "-------------------------------- \n"; + } + else + { + std::cout << "-------------------------------- \n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------------- \n"; + } + return 0; +} diff --git a/src/boost/libs/spirit/example/qi/key_value_sequence.cpp b/src/boost/libs/spirit/example/qi/key_value_sequence.cpp new file mode 100644 index 00000000..3e75786d --- /dev/null +++ b/src/boost/libs/spirit/example/qi/key_value_sequence.cpp @@ -0,0 +1,78 @@ +// 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 to parse arbitrary key/value +// pairs delimited by some separator into a std::map. Parsing the URL query +// format is the example we use to demonstrate how this can be done +// (i.e. things like: key1=value1;key2=value2;...;keyN=valueN). +// +// For a more elaborate explanation see here: http://spirit.sourceforge.net/home/?p=371 + +#include <boost/spirit/include/qi.hpp> +#include <boost/fusion/include/std_pair.hpp> + +#include <iostream> +#include <map> + +namespace client +{ + namespace qi = boost::spirit::qi; + + typedef std::map<std::string, std::string> pairs_type; + + template <typename Iterator> + struct key_value_sequence + : qi::grammar<Iterator, pairs_type()> + { + key_value_sequence() + : key_value_sequence::base_type(query) + { + query = pair >> *((qi::lit(';') | '&') >> pair); + pair = key >> -('=' >> value); + key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9"); + value = +qi::char_("a-zA-Z_0-9"); + } + + qi::rule<Iterator, pairs_type()> query; + qi::rule<Iterator, std::pair<std::string, std::string>()> pair; + qi::rule<Iterator, std::string()> key, value; + }; +} + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + namespace qi = boost::spirit::qi; + + std::string input("key1=value1;key2;key3=value3"); + std::string::iterator begin = input.begin(); + std::string::iterator end = input.end(); + + client::key_value_sequence<std::string::iterator> p; + client::pairs_type m; + + if (!qi::parse(begin, end, p, m)) + { + std::cout << "-------------------------------- \n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------------- \n"; + } + else + { + std::cout << "-------------------------------- \n"; + std::cout << "Parsing succeeded, found entries:\n"; + client::pairs_type::iterator end = m.end(); + for (client::pairs_type::iterator it = m.begin(); it != end; ++it) + { + std::cout << (*it).first; + if (!(*it).second.empty()) + std::cout << "=" << (*it).second; + std::cout << std::endl; + } + std::cout << "---------------------------------\n"; + } + return 0; +} + diff --git a/src/boost/libs/spirit/example/qi/key_value_sequence_empty_value.cpp b/src/boost/libs/spirit/example/qi/key_value_sequence_empty_value.cpp new file mode 100644 index 00000000..8cb8e20d --- /dev/null +++ b/src/boost/libs/spirit/example/qi/key_value_sequence_empty_value.cpp @@ -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 to parse arbitrary key/value +// pairs delimited by some separator into a std::vector. The difference to +// the example 'key_value_sequence.cpp' is that we preserve the order of the +// elements in the parsed sequence as well as possibly existing duplicates. +// In addition to the example 'key_value_sequence_ordered.cpp' we allow for +// empty values, i.e. the grammar allows to distinguish between 'key=;' and +// 'key;", where the first stores an empty string as the value, while the +// second does not initialize the optional holding the value. +// +// For a more elaborate explanation see here: http://spirit.sourceforge.net/home/?p=371 + +#include <boost/spirit/include/qi.hpp> +#include <boost/fusion/include/std_pair.hpp> + +#include <iostream> +#include <map> + +namespace client +{ + namespace qi = boost::spirit::qi; + + typedef std::pair<std::string, boost::optional<std::string> > pair_type; + typedef std::vector<pair_type> pairs_type; + + template <typename Iterator> + struct key_value_sequence_empty_value + : qi::grammar<Iterator, pairs_type()> + { + key_value_sequence_empty_value() + : key_value_sequence_empty_value::base_type(query) + { + query = pair >> *((qi::lit(';') | '&') >> pair); + pair = key >> -('=' >> -value); + key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9"); + value = +qi::char_("a-zA-Z_0-9"); + } + + qi::rule<Iterator, pairs_type()> query; + qi::rule<Iterator, pair_type()> pair; + qi::rule<Iterator, std::string()> key, value; + }; +} + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + namespace qi = boost::spirit::qi; + + std::string input("key1=value1;key2;key3=value3;key4="); + std::string::iterator begin = input.begin(); + std::string::iterator end = input.end(); + + client::key_value_sequence_empty_value<std::string::iterator> p; + client::pairs_type m; + + if (!qi::parse(begin, end, p, m)) + { + std::cout << "-------------------------------- \n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------------- \n"; + } + else + { + std::cout << "-------------------------------- \n"; + std::cout << "Parsing succeeded, found entries:\n"; + client::pairs_type::iterator end = m.end(); + for (client::pairs_type::iterator it = m.begin(); it != end; ++it) + { + std::cout << (*it).first; + if ((*it).second) + std::cout << "=" << boost::get<std::string>((*it).second); + std::cout << std::endl; + } + std::cout << "---------------------------------\n"; + } + return 0; +} + diff --git a/src/boost/libs/spirit/example/qi/key_value_sequence_ordered.cpp b/src/boost/libs/spirit/example/qi/key_value_sequence_ordered.cpp new file mode 100644 index 00000000..21689239 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/key_value_sequence_ordered.cpp @@ -0,0 +1,78 @@ +// 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 to parse arbitrary key/value +// pairs delimited by some separator into a std::vector. The difference to +// the example 'key_value_sequence.cpp' is that we preserve the order of the +// elements in the parsed seqeunce as well as possibly existing duplicates. +// +// For a more elaborate explanation see here: http://spirit.sourceforge.net/home/?p=371 + +#include <boost/spirit/include/qi.hpp> +#include <boost/fusion/include/std_pair.hpp> + +#include <iostream> +#include <map> + +namespace client +{ + namespace qi = boost::spirit::qi; + + typedef std::vector<std::pair<std::string, std::string> > pairs_type; + + template <typename Iterator> + struct key_value_sequence_ordered + : qi::grammar<Iterator, pairs_type()> + { + key_value_sequence_ordered() + : key_value_sequence_ordered::base_type(query) + { + query = pair >> *((qi::lit(';') | '&') >> pair); + pair = key >> -('=' >> value); + key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9"); + value = +qi::char_("a-zA-Z_0-9"); + } + + qi::rule<Iterator, pairs_type()> query; + qi::rule<Iterator, std::pair<std::string, std::string>()> pair; + qi::rule<Iterator, std::string()> key, value; + }; +} + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + namespace qi = boost::spirit::qi; + + std::string input("key2=value2;key1;key3=value3"); + std::string::iterator begin = input.begin(); + std::string::iterator end = input.end(); + + client::key_value_sequence_ordered<std::string::iterator> p; + client::pairs_type v; + + if (!qi::parse(begin, end, p, v)) + { + std::cout << "-------------------------------- \n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------------- \n"; + } + else + { + std::cout << "-------------------------------- \n"; + std::cout << "Parsing succeeded, found entries:\n"; + client::pairs_type::iterator end = v.end(); + for (client::pairs_type::iterator it = v.begin(); it != end; ++it) + { + std::cout << (*it).first; + if (!(*it).second.empty()) + std::cout << "=" << (*it).second; + std::cout << std::endl; + } + std::cout << "---------------------------------\n"; + } + return 0; +} + diff --git a/src/boost/libs/spirit/example/qi/mini_xml1.cpp b/src/boost/libs/spirit/example/qi/mini_xml1.cpp new file mode 100644 index 00000000..1a27eda4 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/mini_xml1.cpp @@ -0,0 +1,242 @@ +/*============================================================================= + 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 mini XML-like parser +// +// [ JDG March 25, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_fusion.hpp> +#include <boost/spirit/include/phoenix_stl.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/variant/recursive_variant.hpp> +#include <boost/foreach.hpp> + +#include <iostream> +#include <fstream> +#include <string> +#include <vector> + +namespace client +{ + namespace fusion = boost::fusion; + namespace phoenix = boost::phoenix; + namespace qi = boost::spirit::qi; + namespace ascii = boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////////// + // Our mini XML tree representation + /////////////////////////////////////////////////////////////////////////// + //[tutorial_xml1_structures + 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 +//[tutorial_xml1_adapt_structures +BOOST_FUSION_ADAPT_STRUCT( + client::mini_xml, + (std::string, name) + (std::vector<client::mini_xml_node>, children) +) +//] + +namespace client +{ + /////////////////////////////////////////////////////////////////////////// + // Print out the mini xml tree + /////////////////////////////////////////////////////////////////////////// + int const tabsize = 4; + + void tab(int indent) + { + for (int i = 0; i < indent; ++i) + std::cout << ' '; + } + + struct mini_xml_printer + { + mini_xml_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const; + + int indent; + }; + + struct mini_xml_node_printer : boost::static_visitor<> + { + mini_xml_node_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const + { + mini_xml_printer(indent+tabsize)(xml); + } + + void operator()(std::string const& text) const + { + tab(indent+tabsize); + std::cout << "text: \"" << text << '"' << std::endl; + } + + int indent; + }; + + void mini_xml_printer::operator()(mini_xml const& xml) const + { + tab(indent); + std::cout << "tag: " << xml.name << std::endl; + tab(indent); + std::cout << '{' << std::endl; + + BOOST_FOREACH(mini_xml_node const& node, xml.children) + { + boost::apply_visitor(mini_xml_node_printer(indent), node); + } + + tab(indent); + std::cout << '}' << std::endl; + } + + /////////////////////////////////////////////////////////////////////////// + // Our mini XML grammar definition + /////////////////////////////////////////////////////////////////////////// + //[tutorial_xml1_grammar + template <typename Iterator> + struct mini_xml_grammar : qi::grammar<Iterator, mini_xml(), ascii::space_type> + { + mini_xml_grammar() : mini_xml_grammar::base_type(xml) + { + using qi::lit; + using qi::lexeme; + using ascii::char_; + using ascii::string; + using namespace qi::labels; + + using phoenix::at_c; + using phoenix::push_back; + + 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(), ascii::space_type> xml; + qi::rule<Iterator, mini_xml_node(), ascii::space_type> node; + qi::rule<Iterator, std::string(), ascii::space_type> text; + qi::rule<Iterator, std::string(), ascii::space_type> start_tag; + qi::rule<Iterator, void(std::string), ascii::space_type> end_tag; + }; + //] +} + +/////////////////////////////////////////////////////////////////////////////// +// 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 client::mini_xml_grammar<std::string::const_iterator> mini_xml_grammar; + mini_xml_grammar xml; // Our grammar + client::mini_xml ast; // Our tree + + using boost::spirit::ascii::space; + std::string::const_iterator iter = storage.begin(); + std::string::const_iterator end = storage.end(); + bool r = phrase_parse(iter, end, xml, space, ast); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + client::mini_xml_printer printer; + printer(ast); + return 0; + } + else + { + std::string::const_iterator some = iter + std::min(30, int(end - iter)); + std::string context(iter, (some>end)?end:some); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \"" << context << "...\"\n"; + std::cout << "-------------------------\n"; + return 1; + } +} + + diff --git a/src/boost/libs/spirit/example/qi/mini_xml2.cpp b/src/boost/libs/spirit/example/qi/mini_xml2.cpp new file mode 100644 index 00000000..6d56374b --- /dev/null +++ b/src/boost/libs/spirit/example/qi/mini_xml2.cpp @@ -0,0 +1,237 @@ +/*============================================================================= + 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 mini XML-like parser +// +// [ JDG March 25, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_fusion.hpp> +#include <boost/spirit/include/phoenix_stl.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/variant/recursive_variant.hpp> +#include <boost/foreach.hpp> + +#include <iostream> +#include <fstream> +#include <string> +#include <vector> + +namespace client +{ + namespace fusion = boost::fusion; + namespace phoenix = boost::phoenix; + namespace qi = boost::spirit::qi; + namespace ascii = boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////////// + // 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( + client::mini_xml, + (std::string, name) + (std::vector<client::mini_xml_node>, children) +) + +namespace client +{ + /////////////////////////////////////////////////////////////////////////// + // Print out the mini xml tree + /////////////////////////////////////////////////////////////////////////// + int const tabsize = 4; + + void tab(int indent) + { + for (int i = 0; i < indent; ++i) + std::cout << ' '; + } + + struct mini_xml_printer + { + mini_xml_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const; + + int indent; + }; + + struct mini_xml_node_printer : boost::static_visitor<> + { + mini_xml_node_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const + { + mini_xml_printer(indent+tabsize)(xml); + } + + void operator()(std::string const& text) const + { + tab(indent+tabsize); + std::cout << "text: \"" << text << '"' << std::endl; + } + + int indent; + }; + + void mini_xml_printer::operator()(mini_xml const& xml) const + { + tab(indent); + std::cout << "tag: " << xml.name << std::endl; + tab(indent); + std::cout << '{' << std::endl; + + BOOST_FOREACH(mini_xml_node const& node, xml.children) + { + boost::apply_visitor(mini_xml_node_printer(indent), node); + } + + tab(indent); + std::cout << '}' << std::endl; + } + + /////////////////////////////////////////////////////////////////////////// + // Our mini XML grammar definition + /////////////////////////////////////////////////////////////////////////// + //[tutorial_xml2_grammar + template <typename Iterator> + struct mini_xml_grammar + : qi::grammar<Iterator, mini_xml(), qi::locals<std::string>, ascii::space_type> + { + mini_xml_grammar() + : mini_xml_grammar::base_type(xml) + { + using qi::lit; + using qi::lexeme; + using ascii::char_; + using ascii::string; + using namespace qi::labels; + + text %= lexeme[+(char_ - '<')]; + node %= xml | text; + + start_tag %= + '<' + >> !lit('/') + >> lexeme[+(char_ - '>')] + >> '>' + ; + + end_tag = + "</" + >> lit(_r1) + >> '>' + ; + + xml %= + start_tag[_a = _1] + >> *node + >> end_tag(_a) + ; + } + + qi::rule<Iterator, mini_xml(), qi::locals<std::string>, ascii::space_type> xml; + qi::rule<Iterator, mini_xml_node(), ascii::space_type> node; + qi::rule<Iterator, std::string(), ascii::space_type> text; + qi::rule<Iterator, std::string(), ascii::space_type> start_tag; + qi::rule<Iterator, void(std::string), ascii::space_type> end_tag; + }; + //] +} + +/////////////////////////////////////////////////////////////////////////////// +// 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 client::mini_xml_grammar<std::string::const_iterator> mini_xml_grammar; + mini_xml_grammar xml; // Our grammar + client::mini_xml ast; // Our tree + + using boost::spirit::ascii::space; + std::string::const_iterator iter = storage.begin(); + std::string::const_iterator end = storage.end(); + bool r = phrase_parse(iter, end, xml, space, ast); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + client::mini_xml_printer printer; + printer(ast); + return 0; + } + else + { + std::string::const_iterator some = iter + std::min(30, int(end - iter)); + std::string context(iter, (some>end)?end:some); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \"" << context << "...\"\n"; + std::cout << "-------------------------\n"; + return 1; + } +} + + diff --git a/src/boost/libs/spirit/example/qi/mini_xml3.cpp b/src/boost/libs/spirit/example/qi/mini_xml3.cpp new file mode 100644 index 00000000..d8926c1a --- /dev/null +++ b/src/boost/libs/spirit/example/qi/mini_xml3.cpp @@ -0,0 +1,258 @@ +/*============================================================================= + 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 mini XML-like parser +// +// [ JDG March 25, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_fusion.hpp> +#include <boost/spirit/include/phoenix_stl.hpp> +#include <boost/spirit/include/phoenix_object.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/variant/recursive_variant.hpp> +#include <boost/foreach.hpp> + +#include <iostream> +#include <fstream> +#include <string> +#include <vector> + +namespace client +{ + namespace fusion = boost::fusion; + namespace phoenix = boost::phoenix; + namespace qi = boost::spirit::qi; + namespace ascii = boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////////// + // 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( + client::mini_xml, + (std::string, name) + (std::vector<client::mini_xml_node>, children) +) + +namespace client +{ + /////////////////////////////////////////////////////////////////////////// + // Print out the mini xml tree + /////////////////////////////////////////////////////////////////////////// + int const tabsize = 4; + + void tab(int indent) + { + for (int i = 0; i < indent; ++i) + std::cout << ' '; + } + + struct mini_xml_printer + { + mini_xml_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const; + + int indent; + }; + + struct mini_xml_node_printer : boost::static_visitor<> + { + mini_xml_node_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const + { + mini_xml_printer(indent+tabsize)(xml); + } + + void operator()(std::string const& text) const + { + tab(indent+tabsize); + std::cout << "text: \"" << text << '"' << std::endl; + } + + int indent; + }; + + void mini_xml_printer::operator()(mini_xml const& xml) const + { + tab(indent); + std::cout << "tag: " << xml.name << std::endl; + tab(indent); + std::cout << '{' << std::endl; + + BOOST_FOREACH(mini_xml_node const& node, xml.children) + { + boost::apply_visitor(mini_xml_node_printer(indent), node); + } + + tab(indent); + std::cout << '}' << std::endl; + } + + /////////////////////////////////////////////////////////////////////////// + // Our mini XML grammar definition + /////////////////////////////////////////////////////////////////////////// + //[tutorial_xml3_grammar + template <typename Iterator> + struct mini_xml_grammar + : qi::grammar<Iterator, mini_xml(), qi::locals<std::string>, ascii::space_type> + { + mini_xml_grammar() + : mini_xml_grammar::base_type(xml, "xml") + { + using qi::lit; + using qi::lexeme; + using qi::on_error; + using qi::fail; + using ascii::char_; + using ascii::string; + using namespace qi::labels; + + using phoenix::construct; + using phoenix::val; + + text %= lexeme[+(char_ - '<')]; + node %= xml | text; + + start_tag %= + '<' + >> !lit('/') + > lexeme[+(char_ - '>')] + > '>' + ; + + end_tag = + "</" + > lit(_r1) + > '>' + ; + + xml %= + start_tag[_a = _1] + > *node + > end_tag(_a) + ; + + xml.name("xml"); + node.name("node"); + text.name("text"); + start_tag.name("start_tag"); + end_tag.name("end_tag"); + + on_error<fail> + ( + xml + , std::cout + << val("Error! Expecting ") + << _4 // what failed? + << val(" here: \"") + << construct<std::string>(_3, _2) // iterators to error-pos, end + << val("\"") + << std::endl + ); + } + + qi::rule<Iterator, mini_xml(), qi::locals<std::string>, ascii::space_type> xml; + qi::rule<Iterator, mini_xml_node(), ascii::space_type> node; + qi::rule<Iterator, std::string(), ascii::space_type> text; + qi::rule<Iterator, std::string(), ascii::space_type> start_tag; + qi::rule<Iterator, void(std::string), ascii::space_type> end_tag; + }; + //] +} + +/////////////////////////////////////////////////////////////////////////////// +// 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 client::mini_xml_grammar<std::string::const_iterator> mini_xml_grammar; + mini_xml_grammar xml; // Our grammar + client::mini_xml ast; // Our tree + + using boost::spirit::ascii::space; + std::string::const_iterator iter = storage.begin(); + std::string::const_iterator end = storage.end(); + bool r = phrase_parse(iter, end, xml, space, ast); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + client::mini_xml_printer printer; + printer(ast); + return 0; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + return 1; + } +} + + diff --git a/src/boost/libs/spirit/example/qi/mini_xml_samples/1.toyxml b/src/boost/libs/spirit/example/qi/mini_xml_samples/1.toyxml new file mode 100644 index 00000000..54181ed4 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/mini_xml_samples/1.toyxml @@ -0,0 +1 @@ +<foo></foo>
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/qi/mini_xml_samples/2.toyxml b/src/boost/libs/spirit/example/qi/mini_xml_samples/2.toyxml new file mode 100644 index 00000000..266e4b1f --- /dev/null +++ b/src/boost/libs/spirit/example/qi/mini_xml_samples/2.toyxml @@ -0,0 +1 @@ +<foo><bar></bar></foo>
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/qi/mini_xml_samples/3.toyxml b/src/boost/libs/spirit/example/qi/mini_xml_samples/3.toyxml new file mode 100644 index 00000000..9d89cf7b --- /dev/null +++ b/src/boost/libs/spirit/example/qi/mini_xml_samples/3.toyxml @@ -0,0 +1,5 @@ +<foo> + <bar>bar 1</bar> + <bar>bar 2</bar> + <bar>bar 3</bar> +</foo>
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/qi/mini_xml_samples/4.toyxml b/src/boost/libs/spirit/example/qi/mini_xml_samples/4.toyxml new file mode 100644 index 00000000..0f220f1e --- /dev/null +++ b/src/boost/libs/spirit/example/qi/mini_xml_samples/4.toyxml @@ -0,0 +1 @@ +<foo><bar></foo></bar>
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/qi/nabialek.cpp b/src/boost/libs/spirit/example/qi/nabialek.cpp new file mode 100644 index 00000000..4795578c --- /dev/null +++ b/src/boost/libs/spirit/example/qi/nabialek.cpp @@ -0,0 +1,98 @@ +/*============================================================================= + Copyright (c) 2003 Sam Nabialek + 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) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// The Nabialek trick. +// +// [ Sam Nabialek; Somewhere, sometime in 2003... ] spirit1 +// [ JDG November 17, 2009 ] spirit2 +// [ JDG January 10, 2010 ] Updated to use rule pointers +// for efficiency. +// +/////////////////////////////////////////////////////////////////////////////// + +#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 nabialek_trick grammar + /////////////////////////////////////////////////////////////////////////////// + template <typename Iterator> + struct nabialek_trick : qi::grammar< + Iterator, ascii::space_type, qi::locals<qi::rule<Iterator, ascii::space_type>*> > + { + nabialek_trick() : nabialek_trick::base_type(start) + { + using ascii::alnum; + using qi::lexeme; + using qi::lazy; + using qi::_a; + using qi::_1; + + id = lexeme[*(ascii::alnum | '_')]; + one = id; + two = id >> ',' >> id; + + keyword.add + ("one", &one) + ("two", &two) + ; + + start = *(keyword[_a = _1] >> lazy(*_a)); + } + + qi::rule<Iterator, ascii::space_type> id, one, two; + qi::rule<Iterator, ascii::space_type, qi::locals<qi::rule<Iterator, ascii::space_type>*> > start; + qi::symbols<char, qi::rule<Iterator, ascii::space_type>*> keyword; + }; +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using boost::spirit::ascii::space; + typedef std::string::const_iterator iterator_type; + typedef client::nabialek_trick<iterator_type> nabialek_trick; + + nabialek_trick g; // Our grammar + + std::string str = "one only\none again\ntwo first,second"; + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = phrase_parse(iter, end, g, 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"; + } + + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/num_list1.cpp b/src/boost/libs/spirit/example/qi/num_list1.cpp new file mode 100644 index 00000000..dc2e696d --- /dev/null +++ b/src/boost/libs/spirit/example/qi/num_list1.cpp @@ -0,0 +1,90 @@ +/*============================================================================= + 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 demontrates a parser for a comma separated list of numbers. +// No actions. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> + +#include <iostream> +#include <string> +#include <vector> + +namespace client +{ + namespace qi = boost::spirit::qi; + namespace ascii = boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////////// + // Our number list parser + /////////////////////////////////////////////////////////////////////////// + //[tutorial_numlist1 + template <typename Iterator> + bool parse_numbers(Iterator first, Iterator last) + { + using qi::double_; + using qi::phrase_parse; + using ascii::space; + + bool r = phrase_parse( + first, /*< start iterator >*/ + last, /*< end iterator >*/ + double_ >> *(',' >> double_), /*< the parser >*/ + space /*< the skip-parser >*/ + ); + if (first != last) // fail if we did not get a full match + return false; + return r; + } + //] +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser 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; + + if (client::parse_numbers(str.begin(), str.end())) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/num_list2.cpp b/src/boost/libs/spirit/example/qi/num_list2.cpp new file mode 100644 index 00000000..9fb854e7 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/num_list2.cpp @@ -0,0 +1,109 @@ +/*============================================================================= + 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 demontrates a parser for a comma separated list of numbers. +// The numbers are inserted in a vector using phoenix. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_stl.hpp> + +#include <iostream> +#include <string> +#include <vector> + +namespace client +{ + namespace qi = boost::spirit::qi; + namespace ascii = boost::spirit::ascii; + namespace phoenix = boost::phoenix; + + /////////////////////////////////////////////////////////////////////////// + // Our number list compiler + /////////////////////////////////////////////////////////////////////////// + //[tutorial_numlist2 + template <typename Iterator> + bool parse_numbers(Iterator first, Iterator last, std::vector<double>& v) + { + using qi::double_; + using qi::phrase_parse; + using qi::_1; + using ascii::space; + using phoenix::push_back; + + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_[push_back(phoenix::ref(v), _1)] + >> *(',' >> double_[push_back(phoenix::ref(v), _1)]) + ) + , + // End grammar + + space); + + if (first != last) // fail if we did not get a full match + return false; + return r; + } + //] +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers will be inserted in a vector 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; + if (client::parse_numbers(str.begin(), str.end(), v)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + for (std::vector<double>::size_type i = 0; i < v.size(); ++i) + std::cout << i << ": " << v[i] << std::endl; + + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/num_list3.cpp b/src/boost/libs/spirit/example/qi/num_list3.cpp new file mode 100644 index 00000000..3ba09ffb --- /dev/null +++ b/src/boost/libs/spirit/example/qi/num_list3.cpp @@ -0,0 +1,108 @@ +/*============================================================================= + 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 demontrates a parser for a comma separated list of numbers. +// The numbers are inserted in a vector using phoenix. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_stl.hpp> + +#include <iostream> +#include <string> +#include <vector> + +namespace client +{ + namespace qi = boost::spirit::qi; + namespace ascii = boost::spirit::ascii; + namespace phoenix = boost::phoenix; + + /////////////////////////////////////////////////////////////////////////////// + // Our number list compiler + /////////////////////////////////////////////////////////////////////////////// + //[tutorial_numlist3 + template <typename Iterator> + bool parse_numbers(Iterator first, Iterator last, std::vector<double>& v) + { + using qi::double_; + using qi::phrase_parse; + using qi::_1; + using ascii::space; + using phoenix::push_back; + + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_[push_back(phoenix::ref(v), _1)] % ',' + ) + , + // End grammar + + space); + + if (first != last) // fail if we did not get a full match + return false; + return r; + } + //] +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers will be inserted in a vector 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; + if (client::parse_numbers(str.begin(), str.end(), v)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + for (std::vector<double>::size_type i = 0; i < v.size(); ++i) + std::cout << i << ": " << v[i] << std::endl; + + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/num_list4.cpp b/src/boost/libs/spirit/example/qi/num_list4.cpp new file mode 100644 index 00000000..552dd8d1 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/num_list4.cpp @@ -0,0 +1,106 @@ +/*============================================================================= + 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 demontrates a parser for a comma separated list of numbers. +// The numbers are inserted in a vector using phoenix. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/phoenix_stl.hpp> + +#include <iostream> +#include <string> +#include <vector> + +namespace client +{ + namespace qi = boost::spirit::qi; + namespace ascii = boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////////// + // Our number list compiler + /////////////////////////////////////////////////////////////////////////// + //[tutorial_numlist4 + template <typename Iterator> + bool parse_numbers(Iterator first, Iterator last, std::vector<double>& v) + { + using qi::double_; + using qi::phrase_parse; + using qi::_1; + using ascii::space; + + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_ % ',' + ) + , + // End grammar + + space, v); + + if (first != last) // fail if we did not get a full match + return false; + return r; + } + //] +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers will be inserted in a vector 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; + if (client::parse_numbers(str.begin(), str.end(), v)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + for (std::vector<double>::size_type i = 0; i < v.size(); ++i) + std::cout << i << ": " << v[i] << std::endl; + + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/parse_date.cpp b/src/boost/libs/spirit/example/qi/parse_date.cpp new file mode 100644 index 00000000..82b5092d --- /dev/null +++ b/src/boost/libs/spirit/example/qi/parse_date.cpp @@ -0,0 +1,125 @@ +/*============================================================================= + 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) +=============================================================================*/ + +// This example is not meant to be a sophisticated date parser. It's sole +// purpose is to demonstrate the intrinsic attribute transformation +// capabilities of a rule. +// +// Note how the rule exposes a fusion sequence, but gets passed an instance of +// a boost::gregorian::date as the attribute. In order to make these types +// compatible for the rule we define a specialization of the customization +// point called 'transform_attribute'. + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/fusion/include/vector.hpp> +#include <boost/date_time.hpp> + +// define custom transformation +namespace boost { namespace spirit { namespace traits +{ + // This specialization of the customization point transform_attribute + // allows to pass a boost::gregorian::date to a rule which is expecting + // a fusion sequence consisting out of three integers as its attribute. + template<> + struct transform_attribute< + boost::gregorian::date, fusion::vector<int, int, int>, qi::domain> + { + typedef fusion::vector<int, int, int> date_parts; + + // The embedded typedef 'type' exposes the attribute as it will be + // passed to the right hand side of the rule. + typedef date_parts type; + + // The function pre() is called for down-stream conversion of the + // attribute supplied to the rule to the attribute expected by the + // right hand side. + // The supplied attribute might have been pre-initialized by parsers + // (i.e. semantic actions) higher up the parser hierarchy (in the + // grammar), in which case we would need to properly initialize the + // returned value from the argument. In this example this is not + // required, so we just create a new instance of a date_parts. + static date_parts pre(boost::gregorian::date) + { + return date_parts(); + } + + // The function post() is called for up-stream conversion of the + // results returned from parsing the right hand side of the rule. + // We need to initialize the attribute supplied to the rule (referenced + // by the first argument) with the values taken from the parsing + // results (referenced by the second argument). + static void post(boost::gregorian::date& d, date_parts const& v) + { + d = boost::gregorian::date(fusion::at_c<0>(v), fusion::at_c<1>(v) + , fusion::at_c<2>(v)); + } + + // The function fail() is called whenever the parsing of the right hand + // side of the rule fails. We don't need to do anything here. + static void fail(boost::gregorian::date&) {} + }; +}}} + +/////////////////////////////////////////////////////////////////////////////// +namespace client +{ + namespace qi = boost::spirit::qi; + + template <typename Iterator> + bool parse_date(Iterator& first, Iterator last, boost::gregorian::date& d) + { + typedef boost::fusion::vector<int, int, int> date_parts; + qi::rule<Iterator, date_parts(), qi::space_type> date = + qi::int_ >> '-' >> qi::int_ >> '-' >> qi::int_; + + return phrase_parse(first, last, date, qi::space, d); + } +} + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA date parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a date of the form : year-month-day\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; + + boost::gregorian::date d; + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = client::parse_date(iter, end, d); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "got: " << d << std::endl; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/porting_guide_classic.cpp b/src/boost/libs/spirit/example/qi/porting_guide_classic.cpp new file mode 100644 index 00000000..b12f0f22 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/porting_guide_classic.cpp @@ -0,0 +1,108 @@ +/*============================================================================= + 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) +=============================================================================*/ +//[porting_guide_classic_includes +#include <boost/spirit/include/classic.hpp> +#include <boost/spirit/include/phoenix1.hpp> +#include <iostream> +#include <string> +//] + +//[porting_guide_classic_namespace +using namespace boost::spirit::classic; +//] + +//[porting_guide_classic_grammar +struct roman : public grammar<roman> +{ + template <typename ScannerT> + struct definition + { + definition(roman const& self) + { + hundreds.add + ("C" , 100)("CC" , 200)("CCC" , 300)("CD" , 400)("D" , 500) + ("DC" , 600)("DCC" , 700)("DCCC" , 800)("CM" , 900) ; + + tens.add + ("X" , 10)("XX" , 20)("XXX" , 30)("XL" , 40)("L" , 50) + ("LX" , 60)("LXX" , 70)("LXXX" , 80)("XC" , 90) ; + + ones.add + ("I" , 1)("II" , 2)("III" , 3)("IV" , 4)("V" , 5) + ("VI" , 6)("VII" , 7)("VIII" , 8)("IX" , 9) ; + + first = eps_p [phoenix::var(self.r) = phoenix::val(0)] + >> ( +ch_p('M') [phoenix::var(self.r) += phoenix::val(1000)] + || hundreds [phoenix::var(self.r) += phoenix::_1] + || tens [phoenix::var(self.r) += phoenix::_1] + || ones [phoenix::var(self.r) += phoenix::_1] + ) ; + } + + rule<ScannerT> first; + symbols<unsigned> hundreds; + symbols<unsigned> tens; + symbols<unsigned> ones; + + rule<ScannerT> const& start() const { return first; } + }; + + roman(unsigned& r_) : r(r_) {} + unsigned& r; +}; +//] + +int main() +{ + { + //[porting_guide_classic_parse + std::string input("1,1"); + parse_info<std::string::iterator> pi = parse(input.begin(), input.end(), int_p); + + if (pi.hit) + std::cout << "successful match!\n"; + + if (pi.full) + std::cout << "full match!\n"; + else + std::cout << "stopped at: " << std::string(pi.stop, input.end()) << "\n"; + + std::cout << "matched length: " << pi.length << "\n"; + //] + } + + { + //[porting_guide_classic_phrase_parse + std::string input(" 1, 1"); + parse_info<std::string::iterator> pi = parse(input.begin(), input.end(), int_p, space_p); + + if (pi.hit) + std::cout << "successful match!\n"; + + if (pi.full) + std::cout << "full match!\n"; + else + std::cout << "stopped at: " << std::string(pi.stop, input.end()) << "\n"; + + std::cout << "matched length: " << pi.length << "\n"; + //] + } + + { + //[porting_guide_classic_use_grammar + std::string input("MMIX"); // MMIX == 2009 + unsigned value = 0; + roman r(value); + parse_info<std::string::iterator> pi = parse(input.begin(), input.end(), r); + if (pi.hit) + std::cout << "successfully matched: " << value << "\n"; + //] + } + return 0; +} + diff --git a/src/boost/libs/spirit/example/qi/porting_guide_qi.cpp b/src/boost/libs/spirit/example/qi/porting_guide_qi.cpp new file mode 100644 index 00000000..d1ec0335 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/porting_guide_qi.cpp @@ -0,0 +1,106 @@ +/*============================================================================= + 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) +=============================================================================*/ +//[porting_guide_qi_includes +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <iostream> +#include <string> +#include <algorithm> +//] + +//[porting_guide_qi_namespace +using namespace boost::spirit; +//] + +//[porting_guide_qi_grammar +template <typename Iterator> +struct roman : qi::grammar<Iterator, unsigned()> +{ + roman() : roman::base_type(first) + { + hundreds.add + ("C" , 100)("CC" , 200)("CCC" , 300)("CD" , 400)("D" , 500) + ("DC" , 600)("DCC" , 700)("DCCC" , 800)("CM" , 900) ; + + tens.add + ("X" , 10)("XX" , 20)("XXX" , 30)("XL" , 40)("L" , 50) + ("LX" , 60)("LXX" , 70)("LXXX" , 80)("XC" , 90) ; + + ones.add + ("I" , 1)("II" , 2)("III" , 3)("IV" , 4)("V" , 5) + ("VI" , 6)("VII" , 7)("VIII" , 8)("IX" , 9) ; + + // qi::_val refers to the attribute of the rule on the left hand side + first = eps [qi::_val = 0] + >> ( +lit('M') [qi::_val += 1000] + || hundreds [qi::_val += qi::_1] + || tens [qi::_val += qi::_1] + || ones [qi::_val += qi::_1] + ) ; + } + + qi::rule<Iterator, unsigned()> first; + qi::symbols<char, unsigned> hundreds; + qi::symbols<char, unsigned> tens; + qi::symbols<char, unsigned> ones; +}; +//] + +int main() +{ + { + //[porting_guide_qi_parse + std::string input("1,1"); + std::string::iterator it = input.begin(); + bool result = qi::parse(it, input.end(), qi::int_); + + if (result) + std::cout << "successful match!\n"; + + if (it == input.end()) + std::cout << "full match!\n"; + else + std::cout << "stopped at: " << std::string(it, input.end()) << "\n"; + + // seldomly needed: use std::distance to calculate the length of the match + std::cout << "matched length: " << std::distance(input.begin(), it) << "\n"; + //] + } + + { + //[porting_guide_qi_phrase_parse + std::string input(" 1, 1"); + std::string::iterator it = input.begin(); + bool result = qi::phrase_parse(it, input.end(), qi::int_, ascii::space); + + if (result) + std::cout << "successful match!\n"; + + if (it == input.end()) + std::cout << "full match!\n"; + else + std::cout << "stopped at: " << std::string(it, input.end()) << "\n"; + + // seldomly needed: use std::distance to calculate the length of the match + std::cout << "matched length: " << std::distance(input.begin(), it) << "\n"; + //] + } + + { + //[porting_guide_qi_use_grammar + std::string input("MMIX"); // MMIX == 2009 + std::string::iterator it = input.begin(); + unsigned value = 0; + roman<std::string::iterator> r; + if (qi::parse(it, input.end(), r, value)) + std::cout << "successfully matched: " << value << "\n"; + //] + } + return 0; +} + diff --git a/src/boost/libs/spirit/example/qi/reference.cpp b/src/boost/libs/spirit/example/qi/reference.cpp new file mode 100644 index 00000000..4cd8a147 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/reference.cpp @@ -0,0 +1,1481 @@ +/*============================================================================= + Copyright (c) 2001-2011 Joel de Guzman + 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) +=============================================================================*/ + +// this code is not supposed to be executed, so the asserts are for +// demonstration purposes only +// boostinspect:naassert_macro + +//[reference_includes +#include <boost/spirit/include/support_utree.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/assert.hpp> +#include <boost/predef/other/endian.h> +#include <iostream> +#include <string> +#include <cstdlib> +//] + +//[reference_test +template <typename P> +void test_parser( + char const* input, P const& p, bool full_match = true) +{ + using boost::spirit::qi::parse; + + char const* f(input); + char const* l(f + strlen(f)); + if (parse(f, l, p) && (!full_match || (f == l))) + std::cout << "ok" << std::endl; + else + std::cout << "fail" << std::endl; +} + +template <typename P> +void test_phrase_parser( + char const* input, P const& p, bool full_match = true) +{ + using boost::spirit::qi::phrase_parse; + using boost::spirit::qi::ascii::space; + + char const* f(input); + char const* l(f + strlen(f)); + if (phrase_parse(f, l, p, space) && (!full_match || (f == l))) + std::cout << "ok" << std::endl; + else + std::cout << "fail" << std::endl; +} +//] + +//[reference_test_attr +template <typename P, typename T> +void test_parser_attr( + char const* input, P const& p, T& attr, bool full_match = true) +{ + using boost::spirit::qi::parse; + + char const* f(input); + char const* l(f + strlen(f)); + if (parse(f, l, p, attr) && (!full_match || (f == l))) + std::cout << "ok" << std::endl; + else + std::cout << "fail" << std::endl; +} + +template <typename P, typename T> +void test_phrase_parser_attr( + char const* input, P const& p, T& attr, bool full_match = true) +{ + using boost::spirit::qi::phrase_parse; + using boost::spirit::qi::ascii::space; + + char const* f(input); + char const* l(f + strlen(f)); + if (phrase_parse(f, l, p, space, attr) && (!full_match || (f == l))) + std::cout << "ok" << std::endl; + else + std::cout << "fail" << std::endl; +} +//] + +//[reference_print_info +struct printer +{ + typedef boost::spirit::utf8_string string; + + void element(string const& tag, string const& value, int depth) const + { + for (int i = 0; i < (depth*4); ++i) // indent to depth + std::cout << ' '; + + std::cout << "tag: " << tag; + if (value != "") + std::cout << ", value: " << value; + std::cout << std::endl; + } +}; + +void print_info(boost::spirit::info const& what) +{ + using boost::spirit::basic_info_walker; + + printer pr; + basic_info_walker<printer> walker(pr, what.tag, 0); + boost::apply_visitor(walker, what.value); +} +//] + +//[reference_test_real_policy +/////////////////////////////////////////////////////////////////////////////// +// These policies can be used to parse thousand separated +// numbers with at most 2 decimal digits after the decimal +// point. e.g. 123,456,789.01 +/////////////////////////////////////////////////////////////////////////////// +template <typename T> +struct ts_real_policies : boost::spirit::qi::ureal_policies<T> +{ + // 2 decimal places Max + template <typename Iterator, typename Attribute> + static bool + parse_frac_n(Iterator& first, Iterator const& last, Attribute& attr, + int& frac_digits) + { + Iterator savef = first; + bool r = boost::spirit::qi:: + extract_uint<T, 10, 1, 2, true>::call(first, last, attr); + if (r) { + // Optimization note: don't compute frac_digits if T is + // an unused_type. This should be optimized away by the compiler. + if (!boost::is_same<T, boost::spirit::unused_type>::value) + frac_digits = static_cast<int>(std::distance(savef, first)); + } + return r; + } + + // No exponent + template <typename Iterator> + static bool + parse_exp(Iterator&, Iterator const&) + { + return false; + } + + // No exponent + template <typename Iterator, typename Attribute> + static bool + parse_exp_n(Iterator&, Iterator const&, Attribute&) + { + return false; + } + + // Thousands separated numbers + template <typename Iterator, typename Accumulator> + static bool + parse_n(Iterator& first, Iterator const& last, Accumulator& result) + { + using boost::spirit::qi::uint_parser; + namespace qi = boost::spirit::qi; + + uint_parser<unsigned, 10, 1, 3> uint3; + uint_parser<unsigned, 10, 3, 3> uint3_3; + + if (parse(first, last, uint3, result)) + { + Accumulator n; + Iterator iter = first; + + while (qi::parse(iter, last, ',') && qi::parse(iter, last, uint3_3, n)) + { + result = result * 1000 + n; + first = iter; + } + + return true; + } + return false; + } +}; +//] + +//[reference_test_bool_policy +/////////////////////////////////////////////////////////////////////////////// +// These policies can be used to parse "eurt" (i.e. "true" spelled backwards) +// as `false` +/////////////////////////////////////////////////////////////////////////////// +struct backwards_bool_policies : boost::spirit::qi::bool_policies<> +{ + // we want to interpret a 'true' spelled backwards as 'false' + template <typename Iterator, typename Attribute> + static bool + parse_false(Iterator& first, Iterator const& last, Attribute& attr) + { + namespace qi = boost::spirit::qi; + if (qi::detail::string_parse("eurt", first, last, qi::unused)) + { + namespace traits = boost::spirit::traits; + traits::assign_to(false, attr); // result is false + return true; + } + return false; + } +}; +//] + +//[reference_qi_complex +// a simple complex number representation z = a + bi +struct complex +{ + complex (double a = 0.0, double b = 0.0) + : a(a), b(b) + {} + + double a; + double b; +}; +//] + +//[reference_qi_stream_complex +// define streaming operator for the type complex +std::istream& +operator>> (std::istream& is, complex& z) +{ + char lbrace = '\0', comma = '\0', rbrace = '\0'; + is >> lbrace >> z.a >> comma >> z.b >> rbrace; + if (lbrace != '{' || comma != ',' || rbrace != '}') + is.setstate(std::ios_base::failbit); + return is; +} +//] + +//[reference_qi_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 __qi__ sequence: + `'{' >> qi::double_ >> ',' >> qi::double_ >> '}'`. +*/ +BOOST_FUSION_ADAPT_STRUCT( + complex, + (double, a) + (double, b) +) + +/*`We add a specialization for the create_parser customization point + defining a custom output format for the complex type. Generally, any + specialization for create_parser is expected to return the proto + expression to be used to match input 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_parser<complex> + { + typedef proto::result_of::deep_copy< + BOOST_TYPEOF('{' >> qi::double_ >> ',' >> qi::double_ >> '}') + >::type type; + + static type call() + { + return proto::deep_copy( + '{' >> qi::double_ >> ',' >> qi::double_ >> '}'); + } + }; +}}} +//] + +//[reference_qi_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 +{ + // in this case we just expose the embedded 'int' as the attribute instance + // to use, allowing to leave the function 'post()' empty + template <> + struct transform_attribute<int_data, int, qi::domain> + { + typedef int& type; + static int& pre(int_data& d) { return d.i; } + static void post(int_data& val, int const& attr) {} + static void fail(int_data&) {} + }; +}}} +//] + +namespace client +{ + using boost::spirit::qi::grammar; + using boost::spirit::qi::rule; + using boost::spirit::ascii::space_type; + +//[reference_grammar_definition +/*`Basic grammar usage: + */ + struct num_list : grammar<char const*, space_type> + { + num_list() : base_type(start) + { + using boost::spirit::int_; + num = int_; + start = num >> *(',' >> num); + } + + rule<char const*, space_type> start, num; + }; +//] +} + +int +main() +{ + { + //[reference_using_declarations_lit_char + using boost::spirit::qi::lit; + using boost::spirit::ascii::char_; + //] + + //[reference_char_literals + test_parser("x", 'x'); // plain literal + test_parser("x", lit('x')); // explicit literal + test_parser("x", char_('x')); // ascii::char_ + //] + + //[reference_char_range + char ch; + test_parser_attr("5", char_('0','9'), ch); // ascii::char_ range + std::cout << ch << std::endl; // prints '5' + //] + + //[reference_char_set + test_parser_attr("5", char_("0-9"), ch); // ascii::char_ set + std::cout << ch << std::endl; // prints '5' + //] + + //[reference_char_phoenix + namespace phx = boost::phoenix; + test_parser("x", phx::val('x')); // direct + test_parser("5", + char_(phx::val('0'),phx::val('9'))); // ascii::char_ range + //] + } + + { + //[reference_using_declarations_lit_string + using boost::spirit::qi::lit; + using boost::spirit::ascii::string; + //] + + //[reference_string_literals + test_parser("boost", "boost"); // plain literal + test_parser("boost", lit("boost")); // explicit literal + test_parser("boost", string("boost")); // ascii::string + //] + } + + { + using boost::spirit::qi::lit; + using boost::spirit::ascii::string; + + //[reference_string_std_string + std::string s("boost"); + test_parser("boost", s); // direct + test_parser("boost", lit(s)); // explicit + test_parser("boost", string(s)); // ascii::string + //] + } + + { + using boost::spirit::qi::lit; + using boost::spirit::ascii::string; + + //[reference_string_phoenix + namespace phx = boost::phoenix; + test_parser("boost", phx::val("boost")); // direct + test_parser("boost", lit(phx::val("boost"))); // explicit + test_parser("boost", string(phx::val("boost"))); // ascii::string + //] + } + + { + //[reference_using_declarations_symbols + using boost::spirit::qi::symbols; + //] + + //[reference_symbols_with_data + symbols<char, int> sym; + + sym.add + ("Apple", 1) + ("Banana", 2) + ("Orange", 3) + ; + + int i; + test_parser_attr("Banana", sym, i); + std::cout << i << std::endl; + //] + } + + { + //[reference_using_declarations_lexeme + using boost::spirit::qi::lexeme; + using boost::spirit::qi::lit; + using boost::spirit::ascii::digit; + //] + + //[reference_lexeme + /*`The use of lexeme here will prevent skipping in between the + digits and the sign making inputs such as `"1 2 345"` erroneous.*/ + test_phrase_parser("12345", lexeme[ -(lit('+') | '-') >> +digit ]); + //] + } + + // as + { + //[reference_using_declarations_as + using boost::spirit::utree; + using boost::spirit::utree_type; + using boost::spirit::utf8_symbol_type; + using boost::spirit::qi::as; + using boost::spirit::qi::as_string; + using boost::spirit::qi::char_; + //] + + //[reference_as + /*`To properly handle string concatenation with __utree__, we + make use of `as_string[]`. We also use `as<T>` to explicitly create + a __utree__ symbol node.*/ + utree ut; + + typedef as<utf8_symbol_type> as_symbol_type; + as_symbol_type const as_symbol = as_symbol_type(); + + test_parser_attr("foo", as_string[*char_], ut); + std::cout << ut << std::endl; // will output >"foo"< + BOOST_ASSERT(ut.which() == utree_type::string_type); + ut.clear(); + + test_parser_attr("foo", as<std::string>()[*char_], ut); + std::cout << ut << std::endl; // will output >"foo"< + BOOST_ASSERT(ut.which() == utree_type::string_type); + ut.clear(); + + test_parser_attr("foo", as_symbol[*char_], ut); + std::cout << ut << std::endl; // will output >foo< + BOOST_ASSERT(ut.which() == utree_type::symbol_type); + ut.clear(); + + test_parser_attr("foo", as<utf8_symbol_type>()[*char_], ut); + std::cout << ut << std::endl; // will output >foo< + BOOST_ASSERT(ut.which() == utree_type::symbol_type); + //] + } + + { + //[reference_using_declarations_no_skip + using boost::spirit::qi::no_skip; + using boost::spirit::qi::char_; + //] + + //[reference_no_skip + /*`The use of no_skip here will prevent skipping of whitespace in front + and in between the characters of the string `' abc '`.*/ + + std::string str; + test_phrase_parser_attr("' abc '", + '\'' >> no_skip[+~char_('\'')] >> '\'', str); + std::cout << str << std::endl; // will output: > abc < + //] + } + + { + //[reference_using_declarations_hold + using boost::spirit::qi::hold; + using boost::spirit::qi::int_; + using boost::spirit::qi::attr; + //] + + //[reference_hold + /*`The use of `hold[]` here will make sure the changes to the attribute + caused by the (failing) first alternative will not be visible after + the whole parsing succeeded. */ + + std::vector<int> v; + test_phrase_parser_attr("123", + hold[int_ >> ':' >> int_] | int_ >> attr(0), v); + std::cout << v[0] << "," << v[1] << std::endl; // will output: >123,0< + //] + } + + { + //[reference_using_declarations_no_case + using boost::spirit::ascii::no_case; + using boost::spirit::ascii::char_; + using boost::spirit::ascii::alnum; + using boost::spirit::qi::symbols; + //] + + //[reference_no_case + test_parser("X", no_case[char_('x')]); + test_parser("6", no_case[alnum]); + //] + + //[reference_symbols_with_no_case + symbols<char, int> sym; + + sym.add + ("apple", 1) // symbol strings are added in lowercase... + ("banana", 2) + ("orange", 3) + ; + + int i; + // ...because sym is used for case-insensitive parsing + test_parser_attr("Apple", no_case[ sym ], i); + std::cout << i << std::endl; + test_parser_attr("ORANGE", no_case[ sym ], i); + std::cout << i << std::endl; + //] + } + + { + //[reference_using_declarations_omit + using boost::spirit::qi::omit; + using boost::spirit::qi::int_; + using boost::spirit::ascii::char_; + //] + + //[reference_omit + /*`This parser ignores the first two characters + and extracts the succeeding `int`:*/ + int i; + test_parser_attr("xx345", omit[char_ >> char_] >> int_, i); + std::cout << i << std::endl; // should print 345 + //] + } + + { + //[reference_using_declarations_matches + using boost::spirit::qi::matches; + using boost::spirit::qi::int_; + //] + + //[reference_matches + /*`This parser tries to match an `int` and returns `true` a its + attribute as it succeeded matching: */ + bool result = false; + test_parser_attr("345", matches[int_], result); + std::cout << std::boolalpha << result << std::endl; // should print: true + + /*`This parser tries to match an `int` as well and returns `false` as + its attribute as it fails matching: */ + result = true; + test_parser_attr("abc", matches[int_], result); + std::cout << std::boolalpha << result << std::endl; // should print: false + //] + } + + { + //[reference_using_declarations_raw + using boost::spirit::qi::raw; + using boost::spirit::ascii::alpha; + using boost::spirit::ascii::alnum; + //] + + //[reference_raw + //`This parser matches and extracts C++ identifiers: + std::string id; + test_parser_attr("James007", raw[(alpha | '_') >> *(alnum | '_')], id); + std::cout << id << std::endl; // should print James007 + //] + } + + { + //[reference_using_declarations_repeat + using boost::spirit::qi::repeat; + using boost::spirit::qi::lit; + using boost::spirit::qi::uint_parser; + using boost::spirit::qi::_1; + using boost::spirit::ascii::char_; + namespace phx = boost::phoenix; + //] + + //[reference_repeat + //`A parser for a file name with a maximum of 255 characters: + test_parser("batman.jpeg", repeat(1, 255)[char_("a-zA-Z_./")]); + + /*`A parser for a specific bitmap file format which has exactly 4096 RGB color information. + (for the purpose of this example, we will be testing only 3 RGB color information.) + */ + uint_parser<unsigned, 16, 6, 6> rgb; + std::vector<unsigned> colors; + test_parser_attr("ffffff0000003f3f3f", repeat(3)[rgb], colors); + std::cout + << std::hex + << colors[0] << ',' + << colors[1] << ',' + << colors[2] << std::endl; + + /*`A 256 bit binary string (1..256 1s or 0s). (For the purpose of this example, + we will be testing only 16 bits.) + */ + test_parser("1011101011110010", repeat(16)[lit('1') | '0']); + //] + + std::cout << std::dec; // reset to decimal + + //[reference_repeat_pascal + /*`This trivial example cannot be practically defined in traditional EBNF. + Although some EBNF variants allow more powerful repetition constructs other + than the Kleene Star, we are still limited to parsing fixed strings. + The nature of EBNF forces the repetition factor to be a constant. + On the other hand, Spirit allows the repetition factor to be variable at + run time. We could write a grammar that accepts the input string above. + Example using phoenix: + */ + std::string str; + int n; + test_parser_attr("\x0bHello World", + char_[phx::ref(n) = _1] >> repeat(phx::ref(n))[char_], str); + std::cout << n << ',' << str << std::endl; // will print "11,Hello World" + //] + } + + { + //[reference_using_declarations_skip + using boost::spirit::qi::skip; + using boost::spirit::qi::int_; + using boost::spirit::ascii::space; + //] + + //[reference_skip + /*`Explicitly specify a skip parser. This parser parses comma + delimited numbers, ignoring spaces.*/ + test_parser("1, 2, 3, 4, 5", skip(space)[int_ >> *(',' >> int_)]); + //] + } + + // attr() + { + //[reference_using_declarations_attr + namespace phx = boost::phoenix; + using boost::spirit::qi::attr; + //] + + //[reference_attr + std::string str; + test_parser_attr("", attr("boost"), str); + std::cout << str << std::endl; // will print 'boost' + + double d; + test_parser_attr("", attr(1.0), d); + std::cout << d << std::endl; // will print '1.0' + //] + + //[reference_attr_phoenix + d = 0.0; + double d1 = 1.2; + test_parser_attr("", attr(phx::ref(d1)), d); + std::cout << d << std::endl; // will print '1.2' + //] + } + + // attr_cast + { + //[reference_qi_using_declarations_attr_cast + using boost::spirit::qi::int_; + //] + + //[reference_qi_attr_cast1 + int_data d = { 0 }; + test_parser_attr("1", boost::spirit::qi::attr_cast(int_), d); + std::cout << d.i << std::endl; + //] + } + + // eol + { + //[reference_using_declarations_eol + using boost::spirit::qi::eol; + //] + + //[reference_eol + test_parser("\n", eol); + //] + } + + // eoi + { + //[reference_using_declarations_eoi + using boost::spirit::qi::eoi; + //] + + //[reference_eoi + test_parser("", eoi); + //] + } + + // eps + { + //[reference_using_declarations_eps + using boost::spirit::qi::eps; + using boost::spirit::qi::int_; + using boost::spirit::qi::_1; + namespace phx = boost::phoenix; + //] + + //[reference_eps + //`Basic `eps`: + test_parser("", eps); // always matches + //] + + //[reference_eps_if + /*`This example simulates the "classic" `if_p` parser. Here, `int_` will be + tried only if the condition, `c`, is true. + */ + bool c = true; // a flag + test_parser("1234", eps(phx::ref(c) == true) >> int_); + //] + + //[reference_eps_while + /*`This example simulates the "classic" `while_p` parser. Here, the kleene loop + will exit once the condition, `c`, becomes true. Notice that the condition, `c`, + is turned to `false` when we get to parse `4`. + */ + test_phrase_parser("1 2 3 4", + *(eps(phx::ref(c) == true) >> int_[phx::ref(c) = (_1 == 4)])); + //] + } + + // lazy + { + //[reference_using_declarations_lazy + using boost::spirit::qi::lazy; + using boost::spirit::ascii::string; + using boost::phoenix::val; + //] + + //[reference_lazy + /*` Here, the phoenix::val expression creates a function + that returns its argument when invoked. The lazy expression + defers the invocation of this function at parse time. Then, + this parser (string parser) is called into action. All this + takes place at parse time. + */ + test_parser("Hello", lazy(val(string("Hello")))); + + //` The above is equivalent to: + test_parser("Hello", string("Hello")); + //] + } + + // char class + { + //[reference_using_declarations_char_class + using boost::spirit::ascii::alnum; + using boost::spirit::ascii::blank; + using boost::spirit::ascii::digit; + using boost::spirit::ascii::lower; + //] + + //[reference_char_class + test_parser("1", alnum); + test_parser(" ", blank); + test_parser("1", digit); + test_parser("a", lower); + //] + } + + // uint + { + //[reference_using_declarations_uint + using boost::phoenix::val; + using boost::spirit::qi::lit; + using boost::spirit::qi::uint_; + using boost::spirit::qi::uint_parser; + //] + + //[reference_uint + // unsigned int + test_parser("12345", uint_); + test_parser("12345", uint_(12345)); + test_parser("12345", uint_(val(12345))); + + // literals + test_parser("12345", lit(12345)); + test_parser("12345", lit(val(12345))); + //] + + //[reference_thousand_separated + //`Thousand separated number parser: + uint_parser<unsigned, 10, 1, 3> uint3_p; // 1..3 digits + uint_parser<unsigned, 10, 3, 3> uint3_3_p; // exactly 3 digits + test_parser("12,345,678", uint3_p >> *(',' >> uint3_3_p)); + //] + } + + // int + { + //[reference_using_declarations_int + using boost::phoenix::val; + using boost::spirit::qi::lit; + using boost::spirit::qi::int_; + //] + + //[reference_int + // signed int + test_parser("+12345", int_); + test_parser("-12345", int_); + test_parser("+12345", int_(12345)); + test_parser("-12345", int_(-12345)); + test_parser("+12345", int_(val(12345))); + test_parser("-12345", int_(val(-12345))); + + // literals + test_parser("+12345", lit(12345)); + test_parser("-12345", lit(-12345)); + test_parser("+12345", lit(val(12345))); + test_parser("-12345", lit(val(-12345))); + //] + } + + // real + { + //[reference_using_declarations_real + using boost::phoenix::val; + using boost::spirit::qi::double_; + using boost::spirit::qi::real_parser; + using boost::spirit::qi::lit; + //] + + //[reference_real + // double + test_parser("+12345e6", double_); + test_parser("-12345e6", double_); + test_parser("+12345e6", double_(12345e6)); + test_parser("-12345e6", double_(-123456e6)); + test_parser("+12345e6", double_(val(12345e6))); + test_parser("-12345e6", double_(val(-123456e6))); + + // literals + test_parser("+12345e6", lit(12345e6)); + test_parser("-12345e6", lit(-123456e6)); + test_parser("+12345e6", lit(val(12345e6))); + test_parser("-12345e6", lit(val(-123456e6))); + //] + + //[reference_custom_real + real_parser<double, ts_real_policies<double> > ts_real; + test_parser("123,456,789.01", ts_real); + test_parser("123,456,789.01", ts_real(123456789.01)); + //] + } + + // bool_ + { + //[reference_using_declarations_bool + using boost::phoenix::val; + using boost::spirit::qi::bool_; + using boost::spirit::qi::bool_parser; + using boost::spirit::qi::lit; + //] + + //[reference_bool + // bool + test_parser("true", bool_); + test_parser("false", bool_); + test_parser("true", bool_(true)); + test_parser("false", bool_(false)); + test_parser("true", bool_(val(true))); + test_parser("false", bool_(val(false))); + + // literals + test_parser("true", lit(true)); + test_parser("false", lit(false)); + test_parser("true", lit(val(true))); + test_parser("false", lit(val(false))); + //] + + //[reference_custom_bool + bool_parser<bool, backwards_bool_policies> backwards_bool; + test_parser("true", backwards_bool); + test_parser("eurt", backwards_bool); + test_parser("true", backwards_bool(true)); + test_parser("eurt", backwards_bool(false)); + //] + } + + // sequence + { + //[reference_using_declarations_sequence + using boost::spirit::ascii::char_; + using boost::spirit::qi::_1; + using boost::spirit::qi::_2; + namespace bf = boost::fusion; + //] + + //[reference_sequence + //`Simple usage: + test_parser("xy", char_ >> char_); + + //`Extracting the attribute tuple (using __fusion__): + bf::vector<char, char> attr; + test_parser_attr("xy", char_ >> char_, attr); + std::cout << bf::at_c<0>(attr) << ',' << bf::at_c<1>(attr) << std::endl; + + //`Extracting the attribute vector (using __stl__): + std::vector<char> vec; + test_parser_attr("xy", char_ >> char_, vec); + std::cout << vec[0] << ',' << vec[1] << std::endl; + + //`Extracting the attributes using __qi_semantic_actions__ (using __phoenix__): + test_parser("xy", (char_ >> char_)[std::cout << _1 << ',' << _2 << std::endl]); + //] + } + + // sequential_or + { + //[reference_using_declarations_sequential_or + using boost::spirit::qi::int_; + //] + + //[reference_sequential_or + //`Correctly parsing a number with optional fractional digits: + test_parser("123.456", int_ || ('.' >> int_)); // full + test_parser("123", int_ || ('.' >> int_)); // just the whole number + test_parser(".456", int_ || ('.' >> int_)); // just the fraction + + /*`A naive but incorrect solution would try to do this using optionals (e.g.): + + int_ >> -('.' >> int_) // will not match ".456" + -int_ >> ('.' >> int_) // will not match "123" + -int_ >> -('.' >> int_) // will match empty strings! Ooops. + */ + //] + } + + // alternative + { + //[reference_using_declarations_alternative + using boost::spirit::ascii::string; + using boost::spirit::qi::int_; + using boost::spirit::qi::_1; + using boost::variant; + //] + + //[reference_alternative + //`Simple usage: + test_parser("Hello", string("Hello") | int_); + test_parser("123", string("Hello") | int_); + + //`Extracting the attribute variant (using __boost_variant__): + variant<std::string, int> attr; + test_parser_attr("Hello", string("Hello") | int_, attr); + + /*`This should print `"Hello"`. Note: There are better ways to extract the value + from the variant. See __boost_variant__ visitation. This code is solely + for demonstration. + */ + if (boost::get<int>(&attr)) + std::cout << boost::get<int>(attr) << std::endl; + else + std::cout << boost::get<std::string>(attr) << std::endl; + + /*`Extracting the attributes using __qi_semantic_actions__ with __phoenix__ + (this should print `123`): + */ + test_parser("123", (string("Hello") | int_)[std::cout << _1 << std::endl]); + //] + + } + + // permutation + { + //[reference_using_declarations_permutation + using boost::spirit::ascii::char_; + //] + + //[reference_permutation + //`Parse a string containing DNA codes (ACTG) + test_parser("ACTGGCTAGACT", *(char_('A') ^ 'C' ^ 'T' ^ 'G')); + //] + } + + // expect + { + //[reference_using_declarations_expect + using boost::spirit::ascii::char_; + using boost::spirit::qi::expectation_failure; + //] + + //[reference_expect + /*`The code below uses an expectation operator to throw an __qi_expectation_failure__ + with a deliberate parsing error when `"o"` is expected and `"i"` is what is + found in the input. The `catch` block prints the information related to the + error. Note: This is low level code that demonstrates the /bare-metal/. Typically, + you use an __qi_error_handler__ to deal with the error. + */ + try + { + test_parser("xi", char_('x') > char_('o')); // should throw an exception + } + catch (expectation_failure<char const*> const& x) + { + std::cout << "expected: "; print_info(x.what_); + std::cout << "got: \"" << std::string(x.first, x.last) << '"' << std::endl; + } + /*`The code above will print:[teletype] + + expected: tag: literal-char, value: o + got: "i"``[c++]`` + */ + //] + } + + // expectd + { + //[reference_using_declarations_expectd + using boost::spirit::ascii::char_; + using boost::spirit::qi::expect; + using boost::spirit::qi::expectation_failure; + //] + + //[reference_expectd + /*`The code below uses an expectation operator to throw an __qi_expectation_failure__ + with a deliberate parsing error when `"o"` is expected and `"x"` is what is + found in the input. The `catch` block prints the information related to the + error. Note: This is low level code that demonstrates the /bare-metal/. Typically, + you use an __qi_error_handler__ to deal with the error. + */ + try + { + test_parser("xi", expect[char_('o')]); // should throw an exception + } + catch (expectation_failure<char const*> const& x) + { + std::cout << "expected: "; print_info(x.what_); + std::cout << "got: \"" << std::string(x.first, x.last) << '"' << std::endl; + } + /*`The code above will print:[teletype] + + expected: tag: literal-char, value: o + got: "x"``[c++]`` + */ + //] + } + + // and-predicate + { + //[reference_and_predicate + //`Some using declarations: + using boost::spirit::lit; + + /*`Basic look-ahead example: make sure that the last character is a + semicolon, but don't consume it, just peek at the next character: + */ + test_phrase_parser("Hello ;", lit("Hello") >> &lit(';'), false); + //] + } + + // not-predicate + { + //[reference_not_predicate + //`Some using declarations: + using boost::spirit::ascii::char_; + using boost::spirit::ascii::alpha; + using boost::spirit::qi::lit; + using boost::spirit::qi::symbols; + + /*`Here's an alternative to the `*(r - x) >> x` idiom using the + not-predicate instead. This parses a list of characters terminated + by a ';': + */ + test_parser("abcdef;", *(!lit(';') >> char_) >> ';'); + + /*`The following parser ensures that we match distinct keywords + (stored in a symbol table). To do this, we make sure that the + keyword does not follow an alpha or an underscore: + */ + symbols<char, int> keywords; + keywords = "begin", "end", "for"; + + // This should fail: + test_parser("beginner", keywords >> !(alpha | '_')); + + // This is ok: + test_parser("end ", keywords >> !(alpha | '_'), false); + + // This is ok: + test_parser("for()", keywords >> !(alpha | '_'), false); + //] + } + + // difference + { + //[reference_difference + //`Some using declarations: + using boost::spirit::ascii::char_; + + /*`Parse a C/C++ style comment: + */ + test_parser("/*A Comment*/", "/*" >> *(char_ - "*/") >> "*/"); + //] + } + + // kleene + { + //[reference_kleene + //`Some using declarations: + using boost::spirit::qi::int_; + + /*`Parse a comma separated list of numbers and put them in a vector: + */ + std::vector<int> attr; + test_phrase_parser_attr( + "111, 222, 333, 444, 555", int_ >> *(',' >> int_), attr); + std::cout + << attr[0] << ',' << attr[1] << ',' << attr[2] << ',' + << attr[3] << ',' << attr[4] + << std::endl; + //] + } + + // plus + { + //[reference_plus + //`Some using declarations: + using boost::spirit::ascii::alpha; + using boost::spirit::qi::lexeme; + + /*`Parse one or more strings containing one or more alphabetic + characters and put them in a vector: + */ + std::vector<std::string> attr; + test_phrase_parser_attr("yaba daba doo", +lexeme[+alpha], attr); + std::cout << attr[0] << ',' << attr[1] << ',' << attr[2] << std::endl; + //] + } + + // optional + { + //[reference_optional + //`Some using declarations: + using boost::spirit::ascii::char_; + using boost::spirit::qi::lexeme; + using boost::spirit::qi::int_; + using boost::fusion::vector; + using boost::fusion::at_c; + using boost::optional; + + /*`Parse a person info with name (in quotes) optional age [footnote + James Bond is shy about his age :-)] and optional sex, all + separated by comma. + */ + vector<std::string, optional<int>, optional<char> > attr; + + test_phrase_parser_attr( + "\"James Bond\", M" + , lexeme['"' >> +(char_ - '"') >> '"'] // name + >> -(',' >> int_) // optional age + >> -(',' >> char_) // optional sex + , attr); + + // Should print: James Bond,M + std::cout << at_c<0>(attr); // print name + if (at_c<1>(attr)) // print optional age + std::cout << ',' << *at_c<1>(attr); + if (at_c<2>(attr)) // print optional sex + std::cout << ',' << *at_c<2>(attr); + std::cout << std::endl; + //] + } + + // list + { + //[reference_list + //`Some using declarations: + using boost::spirit::qi::int_; + + /*`Parse a comma separated list of numbers and put them in a vector: + */ + std::vector<int> attr; + test_phrase_parser_attr( + "111, 222, 333, 444, 555", int_ % ',', attr); + std::cout + << attr[0] << ',' << attr[1] << ',' << attr[2] << ',' + << attr[3] << ',' << attr[4] + << std::endl; + //] + } + + // stream + { + //[reference_qi_stream + //`Using declarations and variables: + using boost::spirit::qi::stream; + using boost::spirit::qi::stream_parser; + + /*`Parse a simple string using the operator>>(istream&, std::string&); + */ + std::string str; + test_parser_attr("abc", stream, str); + std::cout << str << std::endl; // prints: abc + + /*`Parse our complex type using the operator>>(istream&, complex&); + */ + complex c; + test_parser_attr("{1.0,2.5}", stream_parser<char, complex>(), c); + std::cout << c.a << "," << c.b << std::endl; // prints: 1.0,2.5 + //] + } + + /////////////////////////////////////////////////////////////////////////// + // auto module + { + //[reference_qi_using_declarations_auto + using boost::spirit::qi::auto_; + //] + + //[reference_qi_auto + /*`Parse a simple integer using the generated parser component `int_`: + */ + int i = 0; + test_parser_attr("123", auto_, i); + std::cout << i << std::endl; // prints: 123 + + /*`Parse an instance of the `complex` data type as defined above using + the parser as generated by the defined customization point: + */ + complex c; + test_parser_attr("{1.2,2.4}", auto_, c); + std::cout << c.a << "," << c.b << std::endl; // prints: 1.2,2.4 + //] + } + + // native binary + { + //[reference_qi_native_binary + //`Using declarations and variables: + using boost::spirit::qi::byte_; + using boost::spirit::qi::word; + using boost::spirit::qi::dword; + using boost::spirit::qi::qword; + + boost::uint8_t uc; + boost::uint16_t us; + boost::uint32_t ui; +//<- +#ifdef BOOST_HAS_LONG_LONG +//-> + boost::uint64_t ul; +//<- +#endif + +#if BOOST_ENDIAN_LITTLE_BYTE +//-> + //`Basic usage of the native binary parsers for little endian platforms: + test_parser_attr("\x01", byte_, uc); assert(uc == 0x01); + test_parser_attr("\x01\x02", word, us); assert(us == 0x0201); + test_parser_attr("\x01\x02\x03\x04", dword, ui); assert(ui == 0x04030201); +//<- +#ifdef BOOST_HAS_LONG_LONG +//-> + test_parser_attr("\x01\x02\x03\x04\x05\x06\x07\x08", qword, ul); + assert(ul == 0x0807060504030201LL); + +//<- +#endif +//-> + test_parser("\x01", byte_(0x01)); + test_parser("\x01\x02", word(0x0201)); + test_parser("\x01\x02\x03\x04", dword(0x04030201)); +//<- +#ifdef BOOST_HAS_LONG_LONG +//-> + test_parser("\x01\x02\x03\x04\x05\x06\x07\x08", + qword(0x0807060504030201LL)); +//<- +#endif +#else +//-> + //`Basic usage of the native binary parsers for big endian platforms: + test_parser_attr("\x01", byte_, uc); assert(uc == 0x01); + test_parser_attr("\x01\x02", word, us); assert(us == 0x0102); + test_parser_attr("\x01\x02\x03\x04", dword, ui); assert(ui == 0x01020304); +//<- +#ifdef BOOST_HAS_LONG_LONG +//-> + test_parser_attr("\x01\x02\x03\x04\x05\x06\x07\x08", qword, ul); + assert(0x0102030405060708LL); + +//<- +#endif +//-> + test_parser("\x01", byte_(0x01)); + test_parser("\x01\x02", word(0x0102)); + test_parser("\x01\x02\x03\x04", dword(0x01020304)); +//<- +#ifdef BOOST_HAS_LONG_LONG +//-> + test_parser("\x01\x02\x03\x04\x05\x06\x07\x08", + qword(0x0102030405060708LL)); +//<- +#endif +#endif +//-> + //] + } + + // little binary + { + //[reference_qi_little_binary + //`Using declarations and variables: + using boost::spirit::qi::little_word; + using boost::spirit::qi::little_dword; + using boost::spirit::qi::little_qword; + + boost::uint16_t us; + boost::uint32_t ui; +//<- +#ifdef BOOST_HAS_LONG_LONG +//-> + boost::uint64_t ul; +//<- +#endif + +//-> + //`Basic usage of the little endian binary parsers: + test_parser_attr("\x01\x02", little_word, us); assert(us == 0x0201); + test_parser_attr("\x01\x02\x03\x04", little_dword, ui); assert(ui == 0x04030201); +//<- +#ifdef BOOST_HAS_LONG_LONG +//-> + test_parser_attr("\x01\x02\x03\x04\x05\x06\x07\x08", little_qword, ul); + assert(ul == 0x0807060504030201LL); + +//<- +#endif +//-> + test_parser("\x01\x02", little_word(0x0201)); + test_parser("\x01\x02\x03\x04", little_dword(0x04030201)); +//<- +#ifdef BOOST_HAS_LONG_LONG +//-> + test_parser("\x01\x02\x03\x04\x05\x06\x07\x08", + little_qword(0x0807060504030201LL)); +//<- +#endif +//-> + //] + } + + // big binary + { + //[reference_qi_big_binary + //`Using declarations and variables: + using boost::spirit::qi::big_word; + using boost::spirit::qi::big_dword; + using boost::spirit::qi::big_qword; + + boost::uint16_t us; + boost::uint32_t ui; +//<- +#ifdef BOOST_HAS_LONG_LONG +//-> + boost::uint64_t ul; +//<- +#endif + +//-> + //`Basic usage of the big endian binary parsers: + test_parser_attr("\x01\x02", big_word, us); assert(us == 0x0102); + test_parser_attr("\x01\x02\x03\x04", big_dword, ui); assert(ui == 0x01020304); +//<- +#ifdef BOOST_HAS_LONG_LONG +//-> + test_parser_attr("\x01\x02\x03\x04\x05\x06\x07\x08", big_qword, ul); + assert(0x0102030405060708LL); + +//<- +#endif +//-> + test_parser("\x01\x02", big_word(0x0102)); + test_parser("\x01\x02\x03\x04", big_dword(0x01020304)); +//<- +#ifdef BOOST_HAS_LONG_LONG +//-> + test_parser("\x01\x02\x03\x04\x05\x06\x07\x08", + big_qword(0x0102030405060708LL)); +//<- +#endif +//-> + //] + } + + // rule + { + //[reference_rule + //`Some using declarations: + using boost::spirit::qi::rule; + using boost::spirit::qi::int_; + using boost::spirit::qi::locals; + using boost::spirit::qi::_1; + using boost::spirit::qi::_a; + using boost::spirit::ascii::alpha; + using boost::spirit::ascii::char_; + using boost::spirit::ascii::space_type; + + /*`Basic rule: + */ + rule<char const*> r; + r = int_; + test_parser("123", r); + + /*`Rule with synthesized attribute: + */ + rule<char const*, int()> ra; + ra = int_; + int i; + test_parser_attr("123", ra, i); + assert(i == 123); + + /*`Rule with skipper and synthesized attribute: + */ + rule<char const*, std::vector<int>(), space_type> rs; + rs = *int_; + std::vector<int> v; + test_phrase_parser_attr("123 456 789", rs, v); + assert(v[0] == 123); + assert(v[1] == 456); + assert(v[2] == 789); + + /*`Rule with one local variable: + */ + rule<char const*, locals<char> > rl; + rl = alpha[_a = _1] >> char_(_a); // get two identical characters + test_parser("aa", rl); // pass + test_parser("ax", rl); // fail + //] + } + + // grammar + { + using client::num_list; + + //[reference_grammar_using + //`Some using declarations: + using boost::spirit::ascii::space_type; + using boost::spirit::int_; + using boost::spirit::qi::grammar; + using boost::spirit::qi::rule; + //] + + //[reference_grammar + //`How to use the example grammar: + num_list nlist; + test_phrase_parser("123, 456, 789", nlist); + //] + } + + return 0; +} diff --git a/src/boost/libs/spirit/example/qi/reorder_struct.cpp b/src/boost/libs/spirit/example/qi/reorder_struct.cpp new file mode 100644 index 00000000..5f5a50b1 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/reorder_struct.cpp @@ -0,0 +1,130 @@ +// 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 filled from a parsed input of the elements in different sequences + +#include <boost/config/warning_disable.hpp> +#include <boost/mpl/print.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/fusion/include/struct.hpp> +#include <boost/fusion/include/nview.hpp> +#include <boost/foreach.hpp> + +namespace fusion = boost::fusion; +namespace qi = boost::spirit::qi; + +/////////////////////////////////////////////////////////////////////////////// +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::string::const_iterator iterator_type; + + // This is the output routine taking a format description and the data to + // print + template <typename Parser, typename Sequence> + bool parse(std::string const& input, Parser const& p, Sequence& s) + { + iterator_type begin = input.begin(); + return qi::parse(begin, input.end(), p, s); + } +} + +// 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) +) + +/////////////////////////////////////////////////////////////////////////////// +// that's the different types we need to reorder the attributes +typedef fusion::result_of::as_nview<client::employee, 2, 0>::type name_and_age; +typedef fusion::result_of::as_nview<client::employee, 1, 2, 4>::type names_and_salary; +typedef fusion::result_of::as_nview<client::employee, 2, 0, 3>::type name_age_and_department; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + std::string str; + + // some employees + client::employee john; + client::employee mary; + client::employee tom; + + // print data about employees in different formats + { + // parse forename and age only + name_and_age johnview(fusion::as_nview<2, 0>(john)); + bool r = client::parse( + "John, 25", + *(qi::char_ - ',') >> ", " >> qi::int_, + johnview); + if (r) { + std::cout << "Parsed: " << john.forename << ", " << john.age + << std::endl; + } + + // parse surname, forename, and salary + names_and_salary maryview(fusion::as_nview<1, 2, 4>(mary)); + r = client::parse( + "Higgins, Mary: 2200.36", + *(qi::char_ - ',') >> ", " >> *(qi::char_ - ':') >> ": " >> qi::double_, + maryview); + if (r) { + std::cout << "Parsed: " << mary.forename << ", " << mary.surname + << ", " << mary.salary << std::endl; + } + + // parse forename, age, and department + name_age_and_department tomview(fusion::as_nview<2, 0, 3>(tom)); + client::parse( + "Tom: 48 (Boss)", + *(qi::char_ - ':') >> ": " >> qi::int_ >> " (" >> *(qi::char_ - ')') >> ')', + tomview); + if (r) { + std::cout << "Parsed: " << tom.forename << ", " << tom.age + << ", " << tom.department << std::endl; + } + } + + // now parse a list of several employees and print them all + std::vector<client::employee> employees; + + // parse surname, forename, and salary for all employees + { + qi::rule<client::iterator_type, names_and_salary()> r = + *(qi::char_ - ',') >> ", " >> *(qi::char_ - ',') >> ", " >> qi::double_; + + bool result = client::parse( + "John, Smith, 2000.50\n" "Mary, Higgins, 2200.36\n" "Tom, Taylor, 3200.00\n", + r % qi::eol, employees); + + std::cout << "Parsed: " << std::endl; + BOOST_FOREACH(client::employee const& e, employees) + { + std::cout << e.forename << ", " << e.surname << ", " << e.salary + << std::endl; + } + } + return 0; +} + diff --git a/src/boost/libs/spirit/example/qi/roman.cpp b/src/boost/libs/spirit/example/qi/roman.cpp new file mode 100644 index 00000000..e98c2a8a --- /dev/null +++ b/src/boost/libs/spirit/example/qi/roman.cpp @@ -0,0 +1,188 @@ +/*============================================================================= + 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 Roman Numerals Parser (demonstrating the symbol table). This is +// discussed in the "Symbols" chapter in the Spirit User's Guide. +// +// [ JDG August 22, 2002 ] spirit1 +// [ JDG March 13, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// + +#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; + + /////////////////////////////////////////////////////////////////////////////// + // Parse roman hundreds (100..900) numerals using the symbol table. + // Notice that the data associated with each slot is the parser's attribute + // (which is passed to attached semantic actions). + /////////////////////////////////////////////////////////////////////////////// + //[tutorial_roman_hundreds + struct hundreds_ : qi::symbols<char, unsigned> + { + hundreds_() + { + add + ("C" , 100) + ("CC" , 200) + ("CCC" , 300) + ("CD" , 400) + ("D" , 500) + ("DC" , 600) + ("DCC" , 700) + ("DCCC" , 800) + ("CM" , 900) + ; + } + + } hundreds; + //] + + /////////////////////////////////////////////////////////////////////////////// + // Parse roman tens (10..90) numerals using the symbol table. + /////////////////////////////////////////////////////////////////////////////// + //[tutorial_roman_tens + struct tens_ : qi::symbols<char, unsigned> + { + tens_() + { + add + ("X" , 10) + ("XX" , 20) + ("XXX" , 30) + ("XL" , 40) + ("L" , 50) + ("LX" , 60) + ("LXX" , 70) + ("LXXX" , 80) + ("XC" , 90) + ; + } + + } tens; + //] + + /////////////////////////////////////////////////////////////////////////////// + // Parse roman ones (1..9) numerals using the symbol table. + /////////////////////////////////////////////////////////////////////////////// + //[tutorial_roman_ones + struct ones_ : qi::symbols<char, unsigned> + { + ones_() + { + add + ("I" , 1) + ("II" , 2) + ("III" , 3) + ("IV" , 4) + ("V" , 5) + ("VI" , 6) + ("VII" , 7) + ("VIII" , 8) + ("IX" , 9) + ; + } + + } ones; + //] + + /////////////////////////////////////////////////////////////////////////////// + // roman (numerals) grammar + // + // Note the use of the || operator. The expression + // a || b reads match a or b and in sequence. Try + // defining the roman numerals grammar in YACC or + // PCCTS. Spirit rules! :-) + /////////////////////////////////////////////////////////////////////////////// + //[tutorial_roman_grammar + template <typename Iterator> + struct roman : qi::grammar<Iterator, unsigned()> + { + roman() : roman::base_type(start) + { + using qi::eps; + using qi::lit; + using qi::_val; + using qi::_1; + using ascii::char_; + + start = eps [_val = 0] >> + ( + +lit('M') [_val += 1000] + || hundreds [_val += _1] + || tens [_val += _1] + || ones [_val += _1] + ) + ; + } + + qi::rule<Iterator, unsigned()> start; + }; + //] +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tRoman Numerals Parser\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type a Roman Numeral ...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef client::roman<iterator_type> roman; + + roman roman_parser; // Our grammar + + std::string str; + unsigned 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(); + //[tutorial_roman_grammar_parse + bool r = parse(iter, end, roman_parser, 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; +} + + diff --git a/src/boost/libs/spirit/example/qi/sum.cpp b/src/boost/libs/spirit/example/qi/sum.cpp new file mode 100644 index 00000000..33310aa5 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/sum.cpp @@ -0,0 +1,106 @@ +/*============================================================================= + 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) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A parser for summing a comma-separated list of numbers using phoenix. +// +// [ JDG June 28, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +//[tutorial_adder_includes +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <iostream> +#include <string> +//] + +namespace client +{ + //[tutorial_adder_using + namespace qi = boost::spirit::qi; + namespace ascii = boost::spirit::ascii; + namespace phoenix = boost::phoenix; + + using qi::double_; + using qi::_1; + using ascii::space; + using phoenix::ref; + //] + + /////////////////////////////////////////////////////////////////////////// + // Our adder + /////////////////////////////////////////////////////////////////////////// + + //[tutorial_adder + template <typename Iterator> + bool adder(Iterator first, Iterator last, double& n) + { + bool r = qi::phrase_parse(first, last, + + // Begin grammar + ( + double_[ref(n) = _1] >> *(',' >> double_[ref(n) += _1]) + ) + , + // End grammar + + space); + + if (first != last) // fail if we did not get a full match + return false; + return r; + } + //] +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA parser for summing a list of numbers...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers are added using Phoenix.\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; + + double n; + if (client::adder(str.begin(), str.end(), n)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + std::cout << "sum = " << n; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/typeof.cpp b/src/boost/libs/spirit/example/qi/typeof.cpp new file mode 100644 index 00000000..90e8fb0e --- /dev/null +++ b/src/boost/libs/spirit/example/qi/typeof.cpp @@ -0,0 +1,65 @@ +/*============================================================================= + 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/config/warning_disable.hpp> +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/qi_copy.hpp> +#include <boost/spirit/include/support_auto.hpp> +#include <iostream> +#include <string> + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// + +int +main() +{ + using boost::spirit::ascii::space; + using boost::spirit::ascii::char_; + using boost::spirit::qi::parse; + typedef std::string::const_iterator iterator_type; + +/////////////////////////////////////////////////////////////////////////////// +// this works for non-c++11 compilers +#ifdef BOOST_NO_CXX11_AUTO_DECLARATIONS + + BOOST_SPIRIT_AUTO(qi, comment, "/*" >> *(char_ - "*/") >> "*/"); + +/////////////////////////////////////////////////////////////////////////////// +// but this is better for c++11 compilers with auto +#else + + using boost::spirit::qi::copy; + + auto comment = copy("/*" >> *(char_ - "*/") >> "*/"); + +#endif + + std::string str = "/*This is a comment*/"; + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = parse(iter, end, comment); + + 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"; + } + + return 0; +} + + diff --git a/src/boost/libs/spirit/example/qi/unescaped_string.cpp b/src/boost/libs/spirit/example/qi/unescaped_string.cpp new file mode 100644 index 00000000..4a0d6002 --- /dev/null +++ b/src/boost/libs/spirit/example/qi/unescaped_string.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2010 Jeroen Habraken +// +// 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 <iostream> +#include <ostream> +#include <string> + +namespace client +{ + namespace qi = boost::spirit::qi; + + template <typename InputIterator> + struct unescaped_string + : qi::grammar<InputIterator, std::string(char const*)> + { + unescaped_string() + : unescaped_string::base_type(unesc_str) + { + unesc_char.add("\\a", '\a')("\\b", '\b')("\\f", '\f')("\\n", '\n') + ("\\r", '\r')("\\t", '\t')("\\v", '\v')("\\\\", '\\') + ("\\\'", '\'')("\\\"", '\"') + ; + + unesc_str = qi::lit(qi::_r1) + >> *(unesc_char | qi::alnum | "\\x" >> qi::hex) + >> qi::lit(qi::_r1) + ; + } + + qi::rule<InputIterator, std::string(char const*)> unesc_str; + qi::symbols<char const, char const> unesc_char; + }; + +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + namespace qi = boost::spirit::qi; + + typedef std::string::const_iterator iterator_type; + + std::string parsed; + + std::string str("'''string\\x20to\\x20unescape\\x3a\\x20\\n\\r\\t\\\"\\'\\x41'''"); + char const* quote = "'''"; + + iterator_type iter = str.begin(); + iterator_type end = str.end(); + + client::unescaped_string<iterator_type> p; + if (!qi::parse(iter, end, p(quote), parsed)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsed: " << parsed << "\n"; + std::cout << "-------------------------\n"; + } + + return 0; +} diff --git a/src/boost/libs/spirit/example/support/Jamfile b/src/boost/libs/spirit/example/support/Jamfile new file mode 100644 index 00000000..273e341b --- /dev/null +++ b/src/boost/libs/spirit/example/support/Jamfile @@ -0,0 +1,16 @@ +#============================================================================== +# Copyright (c) 2001-2011 Joel de Guzman +# Copyright (c) 2001-2011 Hartmut Kaiser +# Copyright (C) 2011 Bryce Lelbach +# +# 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-support-example + : requirements <toolset>gcc:<c++-template-depth>300 + : + : + ; + +exe multi_pass : multi_pass.cpp ; +exe parse_sexpr : utree/parse_sexpr.cpp ; diff --git a/src/boost/libs/spirit/example/support/multi_pass.cpp b/src/boost/libs/spirit/example/support/multi_pass.cpp new file mode 100644 index 00000000..27e529c7 --- /dev/null +++ b/src/boost/libs/spirit/example/support/multi_pass.cpp @@ -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) + +#include <iostream> +#include <fstream> +#include <vector> + +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/support_multi_pass.hpp> + +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +//[tutorial_multi_pass +int main() +{ + namespace spirit = boost::spirit; + using spirit::ascii::space; + using spirit::ascii::char_; + using spirit::qi::double_; + using spirit::qi::eol; + + std::ifstream in("multi_pass.txt"); // we get our input from this file + if (!in.is_open()) { + std::cout << "Could not open input file: 'multi_pass.txt'" << std::endl; + return -1; + } + + typedef std::istreambuf_iterator<char> base_iterator_type; + spirit::multi_pass<base_iterator_type> first = + spirit::make_default_multi_pass(base_iterator_type(in)); + + std::vector<double> v; + bool result = spirit::qi::phrase_parse(first + , spirit::make_default_multi_pass(base_iterator_type()) + , double_ >> *(',' >> double_) // recognize list of doubles + , space | '#' >> *(char_ - eol) >> eol // comment skipper + , v); // data read from file + + if (!result) { + std::cout << "Failed parsing input file!" << std::endl; + return -2; + } + + std::cout << "Successfully parsed input file!" << std::endl; + return 0; +} +//] diff --git a/src/boost/libs/spirit/example/support/multi_pass.txt b/src/boost/libs/spirit/example/support/multi_pass.txt new file mode 100644 index 00000000..54368f38 --- /dev/null +++ b/src/boost/libs/spirit/example/support/multi_pass.txt @@ -0,0 +1,6 @@ +# 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) + +1.0,2.0,3.0 diff --git a/src/boost/libs/spirit/example/support/utree/error_handler.hpp b/src/boost/libs/spirit/example/support/utree/error_handler.hpp new file mode 100644 index 00000000..6f5ba1bc --- /dev/null +++ b/src/boost/libs/spirit/example/support/utree/error_handler.hpp @@ -0,0 +1,112 @@ +/*============================================================================== + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2010-2011 Bryce Lelbach + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file BOOST_LICENSE_1_0.rst or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#if !defined(BOOST_SPIRIT_UTREE_EXAMPLE_ERROR_HANDLER_HPP) +#define BOOST_SPIRIT_UTREE_EXAMPLE_ERROR_HANDLER_HPP + +#include <string> +#include <sstream> + +#include <boost/config.hpp> +#include <boost/spirit/home/support/info.hpp> +#include <boost/spirit/include/support_line_pos_iterator.hpp> + +namespace sexpr +{ + +using boost::spirit::info; + +template <typename Out> +struct print_info +{ + typedef boost::spirit::utf8_string string; + + print_info(Out& out) : out(out), first(true) {} + + void element(string const& tag, string const& value, int) const + { + if (!first) { + out << ' '; + first = false; + } + + if (value == "") + out << tag; + else + out << "\"" << value << '"'; + } + + Out& out; + mutable bool first; +}; + +struct expected_component : std::exception +{ + std::string msg; + + expected_component(std::string const& source, std::size_t line + , info const& w) + { + using boost::spirit::basic_info_walker; + + std::ostringstream oss; + oss << "(exception \"" << source << "\" "; + + if (line == -1) + oss << -1; + else + oss << line; + + oss << " '(expected_component ("; + + print_info<std::ostringstream> pr(oss); + basic_info_walker<print_info<std::ostringstream> > + walker(pr, w.tag, 0); + + boost::apply_visitor(walker, w.value); + + oss << ")))"; + + msg = oss.str(); + } + + virtual ~expected_component() BOOST_NOEXCEPT_OR_NOTHROW {} + + virtual char const* what() const BOOST_NOEXCEPT_OR_NOTHROW + { + return msg.c_str(); + } +}; + +template <typename Iterator> +struct error_handler +{ + template <typename, typename, typename, typename> + struct result + { + typedef void type; + }; + + std::string source; + + error_handler(std::string const& source_ = "<string>") : source(source_) {} + + void operator()(Iterator first, Iterator last, Iterator err_pos + , info const& what) const + { + using boost::spirit::get_line; + Iterator eol = err_pos; + std::size_t line = get_line(err_pos); + throw expected_component(source, line, what); + } +}; + +} // sexpr + +#endif // BOOST_SPIRIT_UTREE_EXAMPLE_ERROR_HANDLER_HPP + diff --git a/src/boost/libs/spirit/example/support/utree/parse_sexpr.cpp b/src/boost/libs/spirit/example/support/utree/parse_sexpr.cpp new file mode 100644 index 00000000..72d49ea5 --- /dev/null +++ b/src/boost/libs/spirit/example/support/utree/parse_sexpr.cpp @@ -0,0 +1,62 @@ +/*============================================================================== + Copyright (c) 2001-2011 Hartmut Kaiser + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2010-2011 Bryce Lelbach + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file BOOST_LICENSE_1_0.rst or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include <boost/spirit/include/support_istream_iterator.hpp> +#include <boost/spirit/include/support_line_pos_iterator.hpp> +#include <boost/spirit/include/qi_parse.hpp> + +#include "sexpr_parser.hpp" + +int +main() +{ + using boost::spirit::qi::phrase_parse; + + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "sexpr 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 sexpr::parser<iterator_type> parser; + typedef sexpr::whitespace<iterator_type> space; + + parser p; + space ws; + + 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, p, ws); + + 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; +} + diff --git a/src/boost/libs/spirit/example/support/utree/sexpr_generator.hpp b/src/boost/libs/spirit/example/support/utree/sexpr_generator.hpp new file mode 100644 index 00000000..5dc56223 --- /dev/null +++ b/src/boost/libs/spirit/example/support/utree/sexpr_generator.hpp @@ -0,0 +1,128 @@ +/*============================================================================== + Copyright (c) 2001-2011 Hartmut Kaiser + Copyright (c) 2010-2011 Bryce Lelbach + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file BOOST_LICENSE_1_0.rst or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#if !defined(BOOST_SPIRIT_UTREE_EXAMPLE_SEXPR_GENERATOR_HPP) +#define BOOST_SPIRIT_UTREE_EXAMPLE_SEXPR_GENERATOR_HPP + +#include <boost/spirit/include/support_utree.hpp> +#include <boost/spirit/include/karma.hpp> + +namespace boost { +namespace spirit { +namespace traits { + +template<> +struct transform_attribute<utree::nil_type, unused_type, karma::domain> { + typedef unused_type type; + + static unused_type pre (utree::nil_type&) { return unused_type(); } +}; + +} // traits +} // spirit +} // boost + +namespace sexpr +{ + +namespace karma = boost::spirit::karma; +namespace standard = boost::spirit::standard; + +using boost::spirit::utree; +using boost::spirit::utf8_symbol_range_type; +using boost::spirit::utf8_string_range_type; +using boost::spirit::binary_range_type; + +struct bool_output_policies : karma::bool_policies<> +{ + template <typename CharEncoding, typename Tag, typename Iterator> + static bool generate_true(Iterator& sink, bool) + { + return string_inserter<CharEncoding, Tag>::call(sink, "#t"); + } + + template <typename CharEncoding, typename Tag, typename Iterator> + static bool generate_false(Iterator& sink, bool) + { + return string_inserter<CharEncoding, Tag>::call(sink, "#f"); + } +}; + +template <typename Iterator> +struct generator : karma::grammar<Iterator, utree()> +{ + typedef boost::iterator_range<utree::const_iterator> utree_list; + + karma::rule<Iterator, utree()> + start, ref_; + + karma::rule<Iterator, utree_list()> + list; + + karma::rule<Iterator, utf8_symbol_range_type()> + symbol; + + karma::rule<Iterator, utree::nil_type()> + nil_; + + karma::rule<Iterator, utf8_string_range_type()> + utf8; + + karma::rule<Iterator, binary_range_type()> + binary; + + generator() : generator::base_type(start) + { + using standard::char_; + using standard::string; + using karma::bool_generator; + using karma::uint_generator; + using karma::double_; + using karma::int_; + using karma::lit; + using karma::right_align; + + uint_generator<unsigned char, 16> hex2; + bool_generator<bool, bool_output_policies> boolean; + + start = nil_ + | double_ + | int_ + | boolean + | utf8 + | symbol + | binary + | list + | ref_; + + ref_ = start; + + list = '(' << -(start % ' ') << ')'; + + utf8 = '"' << *(&char_('"') << "\\\"" | char_) << '"'; + + symbol = string; + + binary = '#' << *right_align(2, '0')[hex2] << '#'; + + nil_ = karma::attr_cast(lit("nil")); + + start.name("sexpr"); + ref_.name("ref"); + list.name("list"); + utf8.name("string"); + symbol.name("symbol"); + binary.name("binary"); + nil_.name("nil"); + } +}; + +} // sexpr + +#endif // BOOST_SPIRIT_UTREE_EXAMPLE_SEXPR_GENERATOR_HPP + diff --git a/src/boost/libs/spirit/example/support/utree/sexpr_parser.hpp b/src/boost/libs/spirit/example/support/utree/sexpr_parser.hpp new file mode 100644 index 00000000..15dfdfbd --- /dev/null +++ b/src/boost/libs/spirit/example/support/utree/sexpr_parser.hpp @@ -0,0 +1,250 @@ +/*============================================================================== + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2010-2011 Bryce Lelbach + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file BOOST_LICENSE_1_0.rst or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#if !defined(BOOST_SPIRIT_UTREE_EXAMPLE_SEXPR_PARSER_HPP) +#define BOOST_SPIRIT_UTREE_EXAMPLE_SEXPR_PARSER_HPP + +#include "utf8_parser.hpp" +#include "error_handler.hpp" + +namespace boost { +namespace spirit { +namespace traits { + +template<> +struct transform_attribute<utree::nil_type, unused_type, qi::domain> { + typedef unused_type type; + + static unused_type pre (utree::nil_type&) { return unused_type(); } + static void post (utree::nil_type&, unused_type) { } + static void fail (utree::nil_type&) { } +}; + +} // traits +} // spirit +} // boost + +namespace sexpr +{ + +namespace qi = boost::spirit::qi; +namespace px = boost::phoenix; +namespace standard = boost::spirit::standard; + +using boost::spirit::utree; +using boost::spirit::utf8_symbol_type; +using boost::spirit::utf8_string_type; +using boost::spirit::binary_string_type; + +struct bool_input_policies +{ + template <typename Iterator, typename Attribute> + static bool + parse_true(Iterator& first, Iterator const& last, Attribute& attr) + { + using boost::spirit::qi::detail::string_parse; + using boost::spirit::qi::bool_policies; + using boost::spirit::qi::unused; + using boost::spirit::traits::assign_to; + if (string_parse("#t", first, last, unused)) + { + assign_to(true, attr); // result is true + return true; + } + return bool_policies<bool>::parse_true(first, last, attr); + } + + template <typename Iterator, typename Attribute> + static bool + parse_false(Iterator& first, Iterator const& last, Attribute& attr) + { + using boost::spirit::qi::detail::string_parse; + using boost::spirit::qi::bool_policies; + using boost::spirit::qi::unused; + using boost::spirit::traits::assign_to; + if (string_parse("#f", first, last, unused)) + { + assign_to(false, attr); // result is false + return true; + } + return bool_policies<bool>::parse_false(first, last, attr); + } +}; + +struct save_line_pos +{ + template <typename, typename> + struct result + { + typedef void type; + }; + + template <typename Range> + void operator()(utree& ast, Range const& rng) const + { + using boost::spirit::get_line; + std::size_t n = get_line(rng.begin()); + if (n != -1) + { + BOOST_ASSERT(n <= (std::numeric_limits<short>::max)()); + ast.tag(n); + } + else + ast.tag(-1); + } +}; + +template <typename Iterator, typename F> +struct tagger : qi::grammar<Iterator, void(utree&, char)> +{ + qi::rule<Iterator, void(utree&, char)> + start; + + qi::rule<Iterator, void(utree&)> + epsilon; + + px::function<F> + f; + + tagger(F f_ = F()) : tagger::base_type(start), f(f_) + { + using qi::omit; + using qi::raw; + using qi::eps; + using qi::lit; + using qi::_1; + using qi::_r1; + using qi::_r2; + + start = omit[raw[lit(_r2)] [f(_r1, _1)]]; + + epsilon = omit[raw[eps] [f(_r1, _1)]]; + } +}; + +template <typename Iterator> +struct whitespace : qi::grammar<Iterator> { + qi::rule<Iterator> + start; + + whitespace() : whitespace::base_type(start) + { + using standard::space; + using standard::char_; + using qi::eol; + + start = space | (';' >> *(char_ - eol) >> eol); + } +}; + +} // sexpr + +//[utree_sexpr_parser +namespace sexpr +{ + +template <typename Iterator, typename ErrorHandler = error_handler<Iterator> > +struct parser : qi::grammar<Iterator, utree(), whitespace<Iterator> > +{ + qi::rule<Iterator, utree(), whitespace<Iterator> > + start, element, list; + + qi::rule<Iterator, utree()> + atom; + + qi::rule<Iterator, int()> + integer; + + qi::rule<Iterator, utf8_symbol_type()> + symbol; + + qi::rule<Iterator, utree::nil_type()> + nil_; + + qi::rule<Iterator, binary_string_type()> + binary; + + utf8::parser<Iterator> + string; + + px::function<ErrorHandler> const + error; + + tagger<Iterator, save_line_pos> + pos; + + parser(std::string const& source_file = "<string>"): + parser::base_type(start), error(ErrorHandler(source_file)) + { + using standard::char_; + using qi::unused_type; + using qi::lexeme; + using qi::hex; + using qi::oct; + using qi::no_case; + using qi::real_parser; + using qi::strict_real_policies; + using qi::uint_parser; + using qi::bool_parser; + using qi::on_error; + using qi::fail; + using qi::int_; + using qi::lit; + using qi::_val; + using qi::_1; + using qi::_2; + using qi::_3; + using qi::_4; + + real_parser<double, strict_real_policies<double> > strict_double; + uint_parser<unsigned char, 16, 2, 2> hex2; + bool_parser<bool, sexpr::bool_input_policies> boolean; + + start = element.alias(); + + element = atom | list; + + list = pos(_val, '(') > *element > ')'; + + atom = nil_ + | strict_double + | integer + | boolean + | string + | symbol + | binary; + + nil_ = qi::attr_cast(lit("nil")); + + integer = lexeme[ no_case["#x"] > hex] + | lexeme[ no_case["#o"] >> oct] + | lexeme[-no_case["#d"] >> int_]; + + std::string exclude = std::string(" ();\"\x01-\x1f\x7f") + '\0'; + symbol = lexeme[+(~char_(exclude))]; + + binary = lexeme['#' > *hex2 > '#']; + + start.name("sexpr"); + element.name("element"); + list.name("list"); + atom.name("atom"); + nil_.name("nil"); + integer.name("integer"); + symbol.name("symbol"); + binary.name("binary"); + + on_error<fail>(start, error(_1, _2, _3, _4)); + } +}; + +} // sexpr +//] + +#endif // BOOST_SPIRIT_UTREE_EXAMPLE_SEXPR_PARSER_HPP + diff --git a/src/boost/libs/spirit/example/support/utree/utf8_parser.hpp b/src/boost/libs/spirit/example/support/utree/utf8_parser.hpp new file mode 100644 index 00000000..b06046ae --- /dev/null +++ b/src/boost/libs/spirit/example/support/utree/utf8_parser.hpp @@ -0,0 +1,138 @@ +/*============================================================================== + Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2010-2011 Bryce Lelbach + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file BOOST_LICENSE_1_0.rst or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#if !defined(BOOST_SPIRIT_UTREE_EXAMPLE_UTF8_PARSER_HPP) +#define BOOST_SPIRIT_UTREE_EXAMPLE_UTF8_PARSER_HPP + +#include <string> + +#include <boost/cstdint.hpp> + +#include <boost/regex/pending/unicode_iterator.hpp> + +#include <boost/spirit/include/qi.hpp> +#include <boost/spirit/include/phoenix_core.hpp> +#include <boost/spirit/include/phoenix_container.hpp> +#include <boost/spirit/include/phoenix_statement.hpp> +#include <boost/spirit/include/phoenix_operator.hpp> +#include <boost/spirit/include/support_utree.hpp> + +namespace utf8 +{ + +namespace qi = boost::spirit::qi; +namespace px = boost::phoenix; +namespace standard = boost::spirit::standard; + +using boost::spirit::utree; +using boost::spirit::utf8_symbol_type; +using boost::spirit::utf8_string_type; +using boost::spirit::binary_string_type; + +typedef boost::uint32_t uchar; + +struct push_string +{ + template <typename, typename> + struct result + { + typedef void type; + }; + + void operator()(std::string& s, uchar code_point) const + { + typedef std::back_insert_iterator<std::string> insert_iter; + insert_iter out_iter(s); + boost::utf8_output_iterator<insert_iter> s_iter(out_iter); + *s_iter++ = code_point; + } +}; + +struct push_escaped_string +{ + template <typename, typename> + struct result + { + typedef void type; + }; + + void operator()(std::string& s, uchar c) const + { + switch (c) { + case 'b': + s += '\b'; + break; + case 't': + s += '\t'; + break; + case 'n': + s += '\n'; + break; + case 'f': + s += '\f'; + break; + case 'r': + s += '\r'; + break; + case '"': + s += '"'; + break; + case '\\': + s += '\\'; + break; + } + } +}; + +template <typename Iterator> +struct parser : qi::grammar<Iterator, std::string()> +{ + qi::rule<Iterator, void(std::string&)> + escaped; + + qi::rule<Iterator, std::string()> + start; + + px::function<push_string> + push_str; + + px::function<push_escaped_string> + push_esc; + + parser() : parser::base_type (start) + { + using standard::char_; + using qi::uint_parser; + using qi::_val; + using qi::_r1; + using qi::_1; + + uint_parser<uchar, 16, 4, 4> hex4; + uint_parser<uchar, 16, 8, 8> hex8; + + escaped + = '\\' + > ( ('u' > hex4) [push_str(_r1, _1)] + | ('U' > hex8) [push_str(_r1, _1)] + | char_("btnfr\\\"'") [push_esc(_r1, _1)] + ); + + start + = '"' + > *(escaped(_val) | (~char_('"')) [_val += _1]) + > '"'; + + escaped.name("escaped_string"); + start.name("string"); + } +}; + +} // utf8 + +#endif // BOOST_SPIRIT_UTREE_EXAMPLE_UTF8_PARSER_HPP + diff --git a/src/boost/libs/spirit/example/x3/Jamfile b/src/boost/libs/spirit/example/x3/Jamfile new file mode 100644 index 00000000..1e8bb8f3 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/Jamfile @@ -0,0 +1,65 @@ +#============================================================================== +# Copyright (c) 2001-2014 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-x3-example + : requirements + <include>. + <c++-template-depth>512 + <toolset>gcc:<cxxflags>-std=c++1y + <toolset>clang:<cxxflags>-std=c++1y + <toolset>darwin:<cxxflags>-std=c++1y + : + : + ; + +exe x3_num_list1 : num_list/num_list1.cpp ; +exe x3_num_list2 : num_list/num_list2.cpp ; +exe x3_num_list3 : num_list/num_list3.cpp ; +exe x3_num_list4 : num_list/num_list4.cpp ; + +exe x3_actions : actions.cpp ; +exe x3_complex_number : complex_number.cpp ; +exe x3_sum : sum.cpp ; +exe x3_roman : roman.cpp ; +exe x3_employee : employee.cpp ; + +exe x3_rexpr : rexpr/rexpr_min/rexpr.cpp ; + +build-project rexpr/rexpr_full ; + +exe x3_calc1 : calc/calc1.cpp ; +exe x3_calc2 : calc/calc2.cpp ; +exe x3_calc4 : calc/calc4.cpp ; +exe x3_calc4b : calc/calc4b.cpp ; +exe x3_calc5 : calc/calc5.cpp ; +exe x3_calc6 : calc/calc6.cpp ; + +exe x3_calc7 : + calc/calc7/vm.cpp + calc/calc7/compiler.cpp + calc/calc7/expression.cpp + calc/calc7/main.cpp +; + +exe x3_calc8 : + /boost//filesystem + calc/calc8/vm.cpp + calc/calc8/compiler.cpp + calc/calc8/expression.cpp + calc/calc8/statement.cpp + calc/calc8/main.cpp +; + +exe x3_calc9 : + /boost//filesystem + calc/calc9/vm.cpp + calc/calc9/compiler.cpp + calc/calc9/expression.cpp + calc/calc9/statement.cpp + calc/calc9/main.cpp +; + +exe x3_tuple : attributes/tuple.cpp ;
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/x3/actions.cpp b/src/boost/libs/spirit/example/x3/actions.cpp new file mode 100644 index 00000000..7a80fe83 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/actions.cpp @@ -0,0 +1,52 @@ +/*============================================================================= + Copyright (c) 2001-2015 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/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> + +#include <iostream> + +// Presented are various ways to attach semantic actions +// * Using plain function pointer +// * Using simple function object + +namespace client +{ + namespace x3 = boost::spirit::x3; + using x3::_attr; + + struct print_action + { + template <typename Context> + void operator()(Context const& ctx) const + { + std::cout << _attr(ctx) << std::endl; + } + }; +} + +int main() +{ + using boost::spirit::x3::int_; + using boost::spirit::x3::parse; + using client::print_action; + + { // example using function object + + char const *first = "{43}", *last = first + std::strlen(first); + parse(first, last, '{' >> int_[print_action()] >> '}'); + } + + { // example using C++14 lambda + + using boost::spirit::x3::_attr; + char const *first = "{44}", *last = first + std::strlen(first); + auto f = [](auto& ctx){ std::cout << _attr(ctx) << std::endl; }; + parse(first, last, '{' >> int_[f] >> '}'); + } + + return 0; +} diff --git a/src/boost/libs/spirit/example/x3/annotation.cpp b/src/boost/libs/spirit/example/x3/annotation.cpp new file mode 100644 index 00000000..12dacfbb --- /dev/null +++ b/src/boost/libs/spirit/example/x3/annotation.cpp @@ -0,0 +1,246 @@ +/*============================================================================= + Copyright (c) 2002-2018 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) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Based on the employee parser (see employee.cpp), this example shows how +// to annotate the AST with the iterator positions for access to the source +// code when post processing. This example also shows how to "inject" client +// data, using the "with" directive, that the handlers can access. +// +// [ JDG May 9, 2007 ] +// [ JDG May 13, 2015 ] spirit X3 +// [ JDG Feb 22, 2018 ] Parser annotations for spirit X3 +// +// I would like to thank Rainbowverse, llc (https://primeorbial.com/) +// for sponsoring this work and donating it to the community. +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/ast/position_tagged.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/fusion/include/io.hpp> + +#include <iostream> +#include <string> + +namespace client { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // Our AST (employee and person structs) + /////////////////////////////////////////////////////////////////////////// + namespace x3 = boost::spirit::x3; + + struct person : x3::position_tagged + { + person( + std::string const& first_name = "" + , std::string const& last_name = "" + ) + : first_name(first_name) + , last_name(last_name) + {} + + std::string first_name, last_name; + }; + + struct employee : x3::position_tagged + { + int age; + person who; + double salary; + }; + + using boost::fusion::operator<<; +}} + +// We need to tell fusion about our employee struct +// to make it a first-class fusion citizen. This has to +// be in global scope. + +BOOST_FUSION_ADAPT_STRUCT(client::ast::person, + first_name, last_name +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::employee, + age, who, salary +) + +namespace client +{ + namespace parser + { + namespace x3 = boost::spirit::x3; + namespace ascii = boost::spirit::x3::ascii; + + /////////////////////////////////////////////////////////////////////// + // Our annotation handler + /////////////////////////////////////////////////////////////////////// + + // tag used to get the position cache from the context + struct position_cache_tag; + + struct annotate_position + { + template <typename T, typename Iterator, typename Context> + inline void on_success(Iterator const& first, Iterator const& last + , T& ast, Context const& context) + { + auto& position_cache = x3::get<position_cache_tag>(context).get(); + position_cache.annotate(ast, first, last); + } + }; + + /////////////////////////////////////////////////////////////////////// + // Our employee parser + /////////////////////////////////////////////////////////////////////// + + using x3::int_; + using x3::double_; + using x3::lexeme; + using ascii::char_; + + struct quoted_string_class; + struct person_class; + struct employee_class; + + x3::rule<quoted_string_class, std::string> const quoted_string = "quoted_string"; + x3::rule<person_class, ast::person> const person = "person"; + x3::rule<employee_class, ast::employee> const employee = "employee"; + + auto const quoted_string_def = lexeme['"' >> +(char_ - '"') >> '"']; + auto const person_def = quoted_string >> ',' >> quoted_string; + + auto const employee_def = + '{' + >> int_ >> ',' + >> person >> ',' + >> double_ + >> '}' + ; + + auto const employees = employee >> *(',' >> employee); + + BOOST_SPIRIT_DEFINE(quoted_string, person, employee); + + struct quoted_string_class {}; + struct person_class : annotate_position {}; + struct employee_class : annotate_position {}; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Our main parse entry point +/////////////////////////////////////////////////////////////////////////////// + +using iterator_type = std::string::const_iterator; +using position_cache = boost::spirit::x3::position_cache<std::vector<iterator_type>>; + +std::vector<client::ast::employee> +parse(std::string const& input, position_cache& positions) +{ + using boost::spirit::x3::ascii::space; + + std::vector<client::ast::employee> ast; + iterator_type iter = input.begin(); + iterator_type const end = input.end(); + + using boost::spirit::x3::with; + + // Our parser + using client::parser::employees; + using client::parser::position_cache_tag; + + auto const parser = + // we pass our position_cache to the parser so we can access + // it later in our on_sucess handlers + with<position_cache_tag>(std::ref(positions)) + [ + employees + ]; + + bool r = phrase_parse(iter, end, parser, space, ast); + + if (r && iter == end) + { + std::cout << boost::fusion::tuple_open('['); + std::cout << boost::fusion::tuple_close(']'); + std::cout << boost::fusion::tuple_delimiter(", "); + + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + + for (auto const& emp : ast) + { + std::cout << "got: " << emp << std::endl; + } + std::cout << "\n-------------------------\n"; + + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + ast.clear(); + } + return ast; +} + +// Sample input: + +std::string input = R"( +{ + 23, + "Amanda", + "Stefanski", + 1000.99 +}, +{ + 35, + "Angie", + "Chilcote", + 2000.99 +}, +{ + 43, + "Dannie", + "Dillinger", + 3000.99 +}, +{ + 22, + "Dorene", + "Dole", + 2500.99 +}, +{ + 38, + "Rossana", + "Rafferty", + 5000.99 +} +)"; + +int +main() +{ + position_cache positions{input.begin(), input.end()}; + auto ast = parse(input, positions); + + // Get the source of the 2nd employee and print it + auto pos = positions.position_of(ast[1]); // zero based of course! + std::cout << "Here's the 2nd employee:" << std::endl; + std::cout << std::string(pos.begin(), pos.end()) << std::endl; + std::cout << "-------------------------\n"; + return 0; +} diff --git a/src/boost/libs/spirit/example/x3/attributes/tuple.cpp b/src/boost/libs/spirit/example/x3/attributes/tuple.cpp new file mode 100644 index 00000000..979127a7 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/attributes/tuple.cpp @@ -0,0 +1,51 @@ + +/*============================================================================= + Copyright (c) 2019 Tom Tan + + 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/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> + +#include <boost/fusion/adapted/std_tuple.hpp> + +#include <iostream> +#include <tuple> +#include <string> + +// +// X3 does not support more than one attribute anymore in the parse function, +// this example show how to wrap multiple attributes into one leveraging std::tuple. +// + +std::tuple<uint32_t, uint32_t, uint32_t> parse_message_prefix_revision(const std::string &s) +{ + + namespace x3 = boost::spirit::x3; + + auto const uint_3_digits = x3::uint_parser<std::uint32_t, 10, 3, 3>{}; + auto const uint_4_digits = x3::uint_parser<std::uint32_t, 10, 4, 4>{}; + + auto iter = s.cbegin(); + auto end_iter = s.cend(); + + std::tuple<uint32_t, uint32_t, uint32_t> length_id_revision; + + x3::parse(iter, end_iter, + uint_4_digits >> uint_4_digits >> uint_3_digits, + length_id_revision); + + return length_id_revision; +} + +int main() +{ + std::string s = "00200060001"; + + std::cout << "parsing " << s << '\n'; + auto [len, id, rev] = parse_message_prefix_revision(s); + std::cout << "length = " << len << '\n' + << "id = " << id << '\n' + << "revision =" << rev; +}
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/x3/calc/calc1.cpp b/src/boost/libs/spirit/example/x3/calc/calc1.cpp new file mode 100644 index 00000000..0a8e0c96 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc1.cpp @@ -0,0 +1,123 @@ +/*============================================================================= + Copyright (c) 2001-2014 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 ] spirit 1 +// [ JDG March 4, 2007 ] spirit 2 +// [ JDG February 21, 2011 ] spirit 2.5 +// [ JDG June 6, 2014 ] spirit x3 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/ast/variant.hpp> +#include <boost/fusion/include/adapt_struct.hpp> + +#include <iostream> +#include <string> +#include <list> +#include <numeric> + +namespace x3 = boost::spirit::x3; + +namespace client +{ + /////////////////////////////////////////////////////////////////////////////// + // The calculator grammar + /////////////////////////////////////////////////////////////////////////////// + namespace calculator_grammar + { + using x3::uint_; + using x3::char_; + + x3::rule<class expression> const expression("expression"); + x3::rule<class term> const term("term"); + x3::rule<class factor> const factor("factor"); + + auto const expression_def = + term + >> *( ('+' >> term) + | ('-' >> term) + ) + ; + + auto const term_def = + factor + >> *( ('*' >> factor) + | ('/' >> factor) + ) + ; + + auto const factor_def = + uint_ + | '(' >> expression >> ')' + | ('-' >> factor) + | ('+' >> factor) + ; + + BOOST_SPIRIT_DEFINE( + expression + , term + , factor + ); + + auto calculator = expression; + } + + using calculator_grammar::calculator; + +} + +/////////////////////////////////////////////////////////////////////////////// +// 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; + + std::string str; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + auto& calc = client::calculator; // Our grammar + + iterator_type iter = str.begin(); + iterator_type end = str.end(); + boost::spirit::x3::ascii::space_type space; + 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; +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc2.cpp b/src/boost/libs/spirit/example/x3/calc/calc2.cpp new file mode 100644 index 00000000..6c0a2444 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc2.cpp @@ -0,0 +1,139 @@ +/*============================================================================= + Copyright (c) 2001-2014 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 lambda functions. The parser prints code suitable for a stack +// based virtual machine. +// +// [ JDG May 10, 2002 ] spirit 1 +// [ JDG March 4, 2007 ] spirit 2 +// [ JDG February 21, 2011 ] spirit 2.5 +// [ JDG June 6, 2014 ] spirit x3 (from qi calc2, but using lambda functions) +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/ast/variant.hpp> +#include <boost/fusion/include/adapt_struct.hpp> + +#include <iostream> +#include <string> +#include <list> +#include <numeric> + +namespace x3 = boost::spirit::x3; + +namespace client +{ + /////////////////////////////////////////////////////////////////////////////// + // Semantic actions + ////////////////////////////////////////////////////////1/////////////////////// + namespace + { + using x3::_attr; + + auto do_int = [](auto& ctx) { std::cout << "push " << _attr(ctx) << std::endl; }; + auto do_add = []{ std::cout << "add\n"; }; + auto do_subt = []{ std::cout << "subtract\n"; }; + auto do_mult = []{ std::cout << "mult\n"; }; + auto do_div = []{ std::cout << "divide\n"; }; + auto do_neg = []{ std::cout << "negate\n"; }; + } + + /////////////////////////////////////////////////////////////////////////////// + // The calculator grammar + /////////////////////////////////////////////////////////////////////////////// + namespace calculator_grammar + { + using x3::uint_; + using x3::char_; + + x3::rule<class expression> const expression("expression"); + x3::rule<class term> const term("term"); + x3::rule<class factor> const factor("factor"); + + auto const expression_def = + term + >> *( ('+' >> term [do_add]) + | ('-' >> term [do_subt]) + ) + ; + + auto const term_def = + factor + >> *( ('*' >> factor [do_mult]) + | ('/' >> factor [do_div]) + ) + ; + + auto const factor_def = + uint_ [do_int] + | '(' >> expression >> ')' + | ('-' >> factor [do_neg]) + | ('+' >> factor) + ; + + BOOST_SPIRIT_DEFINE( + expression + , term + , factor + ); + + auto calculator = expression; + } + + using calculator_grammar::calculator; + +} + +/////////////////////////////////////////////////////////////////////////////// +// 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; + + std::string str; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + auto& calc = client::calculator; // Our grammar + + iterator_type iter = str.begin(); + iterator_type end = str.end(); + boost::spirit::x3::ascii::space_type space; + 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; +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc4.cpp b/src/boost/libs/spirit/example/x3/calc/calc4.cpp new file mode 100644 index 00000000..289e2cd9 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc4.cpp @@ -0,0 +1,273 @@ +/*============================================================================= + Copyright (c) 2001-2014 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. +// [ JDG January 9, 2013 ] Spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/ast/variant.hpp> +#include <boost/fusion/include/adapt_struct.hpp> + +#include <iostream> +#include <string> +#include <list> +#include <numeric> + +namespace x3 = boost::spirit::x3; + +namespace client { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // The AST + /////////////////////////////////////////////////////////////////////////// + struct nil {}; + struct signed_; + struct program; + + struct operand : x3::variant< + nil + , unsigned int + , x3::forward_ast<signed_> + , x3::forward_ast<program> + > + { + using base_type::base_type; + using base_type::operator=; + }; + + 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_, + sign, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::operation, + operator_, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::program, + first, 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); + for (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()(int lhs, operation const& x) 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 + { + return std::accumulate( + x.rest.begin(), x.rest.end() + , boost::apply_visitor(*this, x.first) + , *this); + } + }; +}} + +namespace client +{ + /////////////////////////////////////////////////////////////////////////////// + // The calculator grammar + /////////////////////////////////////////////////////////////////////////////// + namespace calculator_grammar + { + using x3::uint_; + using x3::char_; + + x3::rule<class expression, ast::program> const expression("expression"); + x3::rule<class term, ast::program> const term("term"); + x3::rule<class factor, ast::operand> const factor("factor"); + + auto const expression_def = + term + >> *( (char_('+') >> term) + | (char_('-') >> term) + ) + ; + + auto const term_def = + factor + >> *( (char_('*') >> factor) + | (char_('/') >> factor) + ) + ; + + auto const factor_def = + uint_ + | '(' >> expression >> ')' + | (char_('-') >> factor) + | (char_('+') >> factor) + ; + + BOOST_SPIRIT_DEFINE( + expression + , term + , factor + ); + + auto calculator = expression; + } + + using calculator_grammar::calculator; + +} + +/////////////////////////////////////////////////////////////////////////////// +// 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::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; + + auto& calc = client::calculator; // Our grammar + ast_program program; // Our program (AST) + ast_print print; // Prints the program + ast_eval eval; // Evaluates the program + + iterator_type iter = str.begin(); + iterator_type end = str.end(); + boost::spirit::x3::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; +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc4b.cpp b/src/boost/libs/spirit/example/x3/calc/calc4b.cpp new file mode 100644 index 00000000..176359ab --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc4b.cpp @@ -0,0 +1,273 @@ +/*============================================================================= + Copyright (c) 2001-2014 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. +// [ JDG January 9, 2013 ] Spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +#if defined(_MSC_VER) +# pragma warning(disable: 4345) +#endif + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/ast/variant.hpp> +#include <boost/fusion/include/adapt_struct.hpp> + +#include <iostream> +#include <string> +#include <list> +#include <numeric> + +namespace x3 = boost::spirit::x3; + +namespace client { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // The AST + /////////////////////////////////////////////////////////////////////////// + struct nil {}; + struct signed_; + struct program; + + struct operand : x3::variant< + nil + , unsigned int + , x3::forward_ast<signed_> + , x3::forward_ast<program> + > + { + using base_type::base_type; + using base_type::operator=; + }; + + 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_, + sign, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::operation, + operator_, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::program, + first, 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); + for (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()(int lhs, operation const& x) 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 + { + return std::accumulate( + x.rest.begin(), x.rest.end() + , boost::apply_visitor(*this, x.first) + , *this); + } + }; +}} + +namespace client +{ + /////////////////////////////////////////////////////////////////////////////// + // The calculator grammar + /////////////////////////////////////////////////////////////////////////////// + namespace calculator_grammar + { + using x3::uint_; + using x3::char_; + + x3::rule<class expression, ast::program> const expression("expression"); + x3::rule<class term, ast::program> const term("term"); + x3::rule<class factor, ast::operand> const factor("factor"); + + auto const expression_def = + term + >> *( (char_('+') >> term) + | (char_('-') >> term) + ) + ; + + auto const term_def = + factor + >> *( (char_('*') >> factor) + | (char_('/') >> factor) + ) + ; + + auto const factor_def = + uint_ + | '(' >> expression >> ')' + | (char_('-') >> factor) + | (char_('+') >> factor) + ; + + BOOST_SPIRIT_DEFINE(expression, term, factor); + + auto calculator = expression; + } + + using calculator_grammar::calculator; + +} + +/////////////////////////////////////////////////////////////////////////////// +// 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::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; + + auto& calc = client::calculator; // Our grammar + ast_program program; // Our program (AST) + ast_print print; // Prints the program + ast_eval eval; // Evaluates the program + + iterator_type iter = str.begin(); + iterator_type end = str.end(); + boost::spirit::x3::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; +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc4c/calc4c.cpp b/src/boost/libs/spirit/example/x3/calc/calc4c/calc4c.cpp new file mode 100644 index 00000000..91d396b5 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc4c/calc4c.cpp @@ -0,0 +1,77 @@ +/*============================================================================= + Copyright (c) 2001-2014 Joel de Guzman + Copyright (c) 2013-2014 Agustin Berge + + 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. +// [ JDG January 9, 2013 ] Spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +#include "grammar.hpp" + +#include <iostream> +#include <string> + +/////////////////////////////////////////////////////////////////////////////// +// 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::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; + + auto& calc = client::calculator; // Our grammar + ast_program program; // Our program (AST) + ast_print print; // Prints the program + ast_eval eval; // Evaluates the program + + iterator_type iter = str.begin(); + iterator_type end = str.end(); + boost::spirit::x3::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; +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc4c/grammar.cpp b/src/boost/libs/spirit/example/x3/calc/calc4c/grammar.cpp new file mode 100644 index 00000000..8d1526b2 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc4c/grammar.cpp @@ -0,0 +1,68 @@ +/*============================================================================= + Copyright (c) 2001-2014 Joel de Guzman + Copyright (c) 2013-2014 Agustin Berge + + 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. +// [ JDG January 9, 2013 ] Spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +#include "grammar.hpp" + +namespace client +{ + /////////////////////////////////////////////////////////////////////////////// + // The calculator grammar + /////////////////////////////////////////////////////////////////////////////// + namespace calculator_grammar + { + using x3::uint_; + using x3::char_; + + x3::rule<class expression, ast::program> const expression("expression"); + x3::rule<class term, ast::program> const term("term"); + x3::rule<class factor, ast::operand> const factor("factor"); + + auto const expression_def = + term + >> *( (char_('+') >> term) + | (char_('-') >> term) + ) + ; + + auto const term_def = + factor + >> *( (char_('*') >> factor) + | (char_('/') >> factor) + ) + ; + + auto const factor_def = + uint_ + | '(' >> expression >> ')' + | (char_('-') >> factor) + | (char_('+') >> factor) + ; + + BOOST_SPIRIT_DEFINE( + expression + , term + , factor + ); + + parser_type calculator() + { + return expression; + } + } +}
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/x3/calc/calc4c/grammar.hpp b/src/boost/libs/spirit/example/x3/calc/calc4c/grammar.hpp new file mode 100644 index 00000000..89d867c6 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc4c/grammar.hpp @@ -0,0 +1,192 @@ +/*============================================================================= + Copyright (c) 2001-2014 Joel de Guzman + Copyright (c) 2013-2014 Agustin Berge + + 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. +// [ JDG January 9, 2013 ] Spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +#if defined(_MSC_VER) +# pragma warning(disable: 4345) +#endif + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/ast/variant.hpp> +#include <boost/variant/recursive_variant.hpp> +#include <boost/variant/apply_visitor.hpp> +#include <boost/fusion/include/adapt_struct.hpp> + +#include <list> +#include <numeric> + +namespace x3 = boost::spirit::x3; + +namespace client { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // The AST + /////////////////////////////////////////////////////////////////////////// + struct nil {}; + struct signed_; + struct program; + + typedef x3::variant< + nil + , unsigned int + , x3::forward_ast<signed_> + , x3::forward_ast<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_, + sign, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::operation, + operator_, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::program, + first, 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); + for (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()(int lhs, operation const& x) 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 + { + return std::accumulate( x.rest.begin(), x.rest.end() + , boost::apply_visitor(*this, x.first) + , *this); + } + }; +}} + +namespace client +{ + /////////////////////////////////////////////////////////////////////////////// + // The calculator grammar + /////////////////////////////////////////////////////////////////////////////// + namespace calculator_grammar + { + using parser_type = + x3::any_parser< + std::string::const_iterator + , ast::program + , decltype(x3::make_context<x3::skipper_tag>(x3::ascii::space)) + >; + + parser_type calculator(); + } + + auto const calculator = calculator_grammar::calculator(); +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc5.cpp b/src/boost/libs/spirit/example/x3/calc/calc5.cpp new file mode 100644 index 00000000..f6cd6a63 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc5.cpp @@ -0,0 +1,301 @@ +/*============================================================================= + Copyright (c) 2001-2014 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. +// [ JDG April 9, 2014 ] Spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment this if you want to enable debugging +//#define BOOST_SPIRIT_X3_DEBUG + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/ast/variant.hpp> +#include <boost/fusion/include/adapt_struct.hpp> + +#include <iostream> +#include <string> +#include <list> +#include <numeric> + +namespace x3 = boost::spirit::x3; + +namespace client { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // The AST + /////////////////////////////////////////////////////////////////////////// + struct nil {}; + struct signed_; + struct program; + + struct operand : x3::variant< + nil + , unsigned int + , x3::forward_ast<signed_> + , x3::forward_ast<program> + > + { + using base_type::base_type; + using base_type::operator=; + }; + + 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_, + sign, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::operation, + operator_, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::program, + first, 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); + for (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); + for (operation const& oper : x.rest) + { + state = (*this)(oper, state); + } + return state; + } + }; +}} + +namespace client +{ + /////////////////////////////////////////////////////////////////////////////// + // The calculator grammar + /////////////////////////////////////////////////////////////////////////////// + namespace calculator_grammar + { + using x3::uint_; + using x3::char_; + + struct expression_class; + struct term_class; + struct factor_class; + + x3::rule<expression_class, ast::program> const expression("expression"); + x3::rule<term_class, ast::program> const term("term"); + x3::rule<factor_class, ast::operand> const factor("factor"); + + auto const expression_def = + term + >> *( (char_('+') > term) + | (char_('-') > term) + ) + ; + + auto const term_def = + factor + >> *( (char_('*') > factor) + | (char_('/') > factor) + ) + ; + + auto const factor_def = + uint_ + | '(' > expression > ')' + | (char_('-') > factor) + | (char_('+') > factor) + ; + + BOOST_SPIRIT_DEFINE( + expression + , term + , factor + ); + + struct expression_class + { + // Our error handler + template <typename Iterator, typename Exception, typename Context> + x3::error_handler_result + on_error(Iterator&, Iterator const& last, Exception const& x, Context const& context) + { + std::cout + << "Error! Expecting: " + << x.which() + << " here: \"" + << std::string(x.where(), last) + << "\"" + << std::endl + ; + return x3::error_handler_result::fail; + } + }; + + auto calculator = expression; + } + + using calculator_grammar::calculator; +} + +/////////////////////////////////////////////////////////////////////////////// +// 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::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; + + auto& calc = client::calculator; // Our grammar + ast_program program; // Our program (AST) + ast_print print; // Prints the program + ast_eval eval; // Evaluates the program + + iterator_type iter = str.begin(); + iterator_type end = str.end(); + boost::spirit::x3::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::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc6.cpp b/src/boost/libs/spirit/example/x3/calc/calc6.cpp new file mode 100644 index 00000000..f9554aa6 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc6.cpp @@ -0,0 +1,341 @@ +/*============================================================================= + Copyright (c) 2001-2014 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 (and X3). +// +// [ JDG Sometime 2000 ] pre-boost +// [ JDG September 18, 2002 ] spirit1 +// [ JDG April 8, 2007 ] spirit2 +// [ JDG February 18, 2011 ] Pure attributes. No semantic actions. +// [ JDG April 9, 2014 ] Spirit X3 (from qi calc6) +// +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment this if you want to enable debugging +//#define BOOST_SPIRIT_X3_DEBUG + +#if defined(_MSC_VER) +# pragma warning(disable: 4345) +#endif + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/ast/variant.hpp> +#include <boost/fusion/include/adapt_struct.hpp> + +#include <iostream> +#include <string> +#include <list> +#include <numeric> + +namespace x3 = boost::spirit::x3; + +namespace client { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // The AST + /////////////////////////////////////////////////////////////////////////// + struct nil {}; + struct signed_; + struct expression; + + struct operand : x3::variant< + nil + , unsigned int + , x3::forward_ast<signed_> + , x3::forward_ast<expression> + > + { + using base_type::base_type; + using base_type::operator=; + }; + + 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_, + sign, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::operation, + operator_, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::expression, + first, 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); + for (ast::operation const& oper : x.rest) + { + (*this)(oper); + } + } + }; + + /////////////////////////////////////////////////////////////////////////////// + // The calculator grammar + /////////////////////////////////////////////////////////////////////////////// + namespace calculator_grammar + { + using x3::uint_; + using x3::char_; + + struct expression_class; + struct term_class; + struct factor_class; + + x3::rule<expression_class, ast::expression> const expression("expression"); + x3::rule<term_class, ast::expression> const term("term"); + x3::rule<factor_class, ast::operand> const factor("factor"); + + auto const expression_def = + term + >> *( (char_('+') > term) + | (char_('-') > term) + ) + ; + + auto const term_def = + factor + >> *( (char_('*') > factor) + | (char_('/') > factor) + ) + ; + + auto const factor_def = + uint_ + | '(' > expression > ')' + | (char_('-') > factor) + | (char_('+') > factor) + ; + + BOOST_SPIRIT_DEFINE( + expression + , term + , factor + ); + + struct expression_class + { + // Our error handler + template <typename Iterator, typename Exception, typename Context> + x3::error_handler_result + on_error(Iterator&, Iterator const& last, Exception const& x, Context const& context) + { + std::cout + << "Error! Expecting: " + << x.which() + << " here: \"" + << std::string(x.where(), last) + << "\"" + << std::endl + ; + return x3::error_handler_result::fail; + } + }; + + auto calculator = expression; + } + + using calculator_grammar::calculator; +} + +/////////////////////////////////////////////////////////////////////////////// +// 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::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 + auto& calc = client::calculator; // Our grammar + ast_expression expression; // Our program (AST) + compiler compile(code); // Compiles the program + + iterator_type iter = str.begin(); + iterator_type const end = str.end(); + boost::spirit::x3::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; +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc7/ast.hpp b/src/boost/libs/spirit/example/x3/calc/calc7/ast.hpp new file mode 100644 index 00000000..600350ec --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc7/ast.hpp @@ -0,0 +1,63 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC7_AST_HPP) +#define BOOST_SPIRIT_X3_CALC7_AST_HPP + +#include <boost/spirit/home/x3/support/ast/variant.hpp> +#include <boost/spirit/home/x3/support/ast/position_tagged.hpp> +#include <boost/fusion/include/io.hpp> +#include <list> + +namespace client { namespace ast +{ + namespace x3 = boost::spirit::x3; + + /////////////////////////////////////////////////////////////////////////// + // The AST + /////////////////////////////////////////////////////////////////////////// + struct nil {}; + struct signed_; + struct expression; + + struct operand : x3::variant< + nil + , unsigned int + , x3::forward_ast<signed_> + , x3::forward_ast<expression> + > + { + using base_type::base_type; + using base_type::operator=; + }; + + 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; + } +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc7/ast_adapted.hpp b/src/boost/libs/spirit/example/x3/calc/calc7/ast_adapted.hpp new file mode 100644 index 00000000..bd56cfc7 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc7/ast_adapted.hpp @@ -0,0 +1,25 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC7_AST_ADAPTED_HPP) +#define BOOST_SPIRIT_X3_CALC7_AST_ADAPTED_HPP + +#include "ast.hpp" +#include <boost/fusion/include/adapt_struct.hpp> + +BOOST_FUSION_ADAPT_STRUCT(client::ast::signed_, + sign, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::operation, + operator_, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::expression, + first, rest +) + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc7/compiler.cpp b/src/boost/libs/spirit/example/x3/calc/calc7/compiler.cpp new file mode 100644 index 00000000..8f6d6f09 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc7/compiler.cpp @@ -0,0 +1,55 @@ +/*============================================================================= + Copyright (c) 2001-2014 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" + +namespace client +{ + void compiler::operator()(ast::nil) const + { + BOOST_ASSERT(0); + } + + void compiler::operator()(unsigned int n) const + { + code.push_back(op_int); + code.push_back(n); + } + + void compiler::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 compiler::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 compiler::operator()(ast::expression const& x) const + { + boost::apply_visitor(*this, x.first); + for (ast::operation const& oper : x.rest) + { + (*this)(oper); + } + } +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc7/compiler.hpp b/src/boost/libs/spirit/example/x3/calc/calc7/compiler.hpp new file mode 100644 index 00000000..3cf0ed84 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc7/compiler.hpp @@ -0,0 +1,33 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC7_COMPILER_HPP) +#define BOOST_SPIRIT_X3_CALC7_COMPILER_HPP + +#include "ast.hpp" + +namespace client +{ + /////////////////////////////////////////////////////////////////////////// + // The Compiler + /////////////////////////////////////////////////////////////////////////// + struct compiler + { + typedef void result_type; + + std::vector<int>& code; + compiler(std::vector<int>& code) + : code(code) {} + + void operator()(ast::nil) const; + void operator()(unsigned int n) const; + void operator()(ast::operation const& x) const; + void operator()(ast::signed_ const& x) const; + void operator()(ast::expression const& x) const; + }; +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc7/error_handler.hpp b/src/boost/libs/spirit/example/x3/calc/calc7/error_handler.hpp new file mode 100644 index 00000000..9517220d --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc7/error_handler.hpp @@ -0,0 +1,40 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC7_ERROR_HANDLER_HPP) +#define BOOST_SPIRIT_X3_CALC7_ERROR_HANDLER_HPP + +#include <boost/spirit/home/x3.hpp> +#include <iostream> + +namespace client { namespace calculator_grammar +{ + //////////////////////////////////////////////////////////////////////////// + // Our error handler + //////////////////////////////////////////////////////////////////////////// + namespace x3 = boost::spirit::x3; + + struct error_handler + { + // Our error handler + template <typename Iterator, typename Exception, typename Context> + x3::error_handler_result + on_error(Iterator&, Iterator const& last, Exception const& x, Context const& context) + { + std::cout + << "Error! Expecting: " + << x.which() + << " here: \"" + << std::string(x.where(), last) + << "\"" + << std::endl + ; + return x3::error_handler_result::fail; + } + }; +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc7/expression.cpp b/src/boost/libs/spirit/example/x3/calc/calc7/expression.cpp new file mode 100644 index 00000000..3dcb694e --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc7/expression.cpp @@ -0,0 +1,15 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_def.hpp" + +namespace client { namespace calculator_grammar +{ + typedef std::string::const_iterator iterator_type; + typedef x3::phrase_parse_context<x3::ascii::space_type>::type context_type; + + BOOST_SPIRIT_INSTANTIATE(expression_type, iterator_type, context_type); +}} diff --git a/src/boost/libs/spirit/example/x3/calc/calc7/expression.hpp b/src/boost/libs/spirit/example/x3/calc/calc7/expression.hpp new file mode 100644 index 00000000..93568eaf --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc7/expression.hpp @@ -0,0 +1,26 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC7_EXPRESSION_HPP) +#define BOOST_SPIRIT_X3_CALC7_EXPRESSION_HPP + +#include <boost/spirit/home/x3.hpp> +#include "ast.hpp" + +namespace client +{ + namespace x3 = boost::spirit::x3; + namespace calculator_grammar + { + struct expression_class; + typedef x3::rule<expression_class, ast::expression> expression_type; + BOOST_SPIRIT_DECLARE(expression_type); + } + + calculator_grammar::expression_type expression(); +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc7/expression_def.hpp b/src/boost/libs/spirit/example/x3/calc/calc7/expression_def.hpp new file mode 100644 index 00000000..dc73c288 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc7/expression_def.hpp @@ -0,0 +1,71 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC7_EXPRESSION_DEF_HPP) +#define BOOST_SPIRIT_X3_CALC7_EXPRESSION_DEF_HPP + +#include <boost/spirit/home/x3.hpp> +#include "ast.hpp" +#include "ast_adapted.hpp" +#include "expression.hpp" +#include "error_handler.hpp" + +namespace client { namespace calculator_grammar +{ + using x3::uint_; + using x3::char_; + + struct expression_class; + struct term_class; + struct factor_class; + + typedef x3::rule<expression_class, ast::expression> expression_type; + typedef x3::rule<term_class, ast::expression> term_type; + typedef x3::rule<factor_class, ast::operand> factor_type; + + expression_type const expression = "expression"; + term_type const term = "term"; + factor_type const factor = "factor"; + + auto const expression_def = + term + >> *( (char_('+') > term) + | (char_('-') > term) + ) + ; + + auto const term_def = + factor + >> *( (char_('*') > factor) + | (char_('/') > factor) + ) + ; + + auto const factor_def = + uint_ + | '(' > expression > ')' + | (char_('-') > factor) + | (char_('+') > factor) + ; + + BOOST_SPIRIT_DEFINE( + expression + , term + , factor + ); + + struct expression_class : error_handler {}; +}} + +namespace client +{ + calculator_grammar::expression_type expression() + { + return calculator_grammar::expression; + } +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc7/main.cpp b/src/boost/libs/spirit/example/x3/calc/calc7/main.cpp new file mode 100644 index 00000000..5f71230d --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc7/main.cpp @@ -0,0 +1,81 @@ +/*============================================================================= + Copyright (c) 2001-2014 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 calc6, but this version also shows off grammar modularization. +// Here you will see how expressions is built as a modular grammars. +// +// [ JDG Sometime 2000 ] pre-boost +// [ JDG September 18, 2002 ] spirit1 +// [ JDG April 8, 2007 ] spirit2 +// [ JDG February 18, 2011 ] Pure attributes. No semantic actions. +// [ JDG April 9, 2014 ] Spirit X3 (from qi calc6) +// [ JDG May 2, 2014 ] Modular grammar using BOOST_SPIRIT_DEFINE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "ast.hpp" +#include "vm.hpp" +#include "compiler.hpp" +#include "expression.hpp" +#include "error_handler.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// 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::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; + + using boost::spirit::x3::ascii::space; + + client::vmachine mach; // Our virtual machine + std::vector<int> code; // Our VM code + auto calc = client::expression(); // grammar + + ast_expression ast; // Our program (AST) + compiler compile(code); // Compiles the program + + iterator_type iter = str.begin(); + iterator_type const end = str.end(); + bool r = phrase_parse(iter, end, calc, space, ast); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + compile(ast); + 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; +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc7/vm.cpp b/src/boost/libs/spirit/example/x3/calc/calc7/vm.cpp new file mode 100644 index 00000000..323d7217 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc7/vm.cpp @@ -0,0 +1,50 @@ +/*============================================================================= + Copyright (c) 2001-2014 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(); + 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; + } + } + } +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc7/vm.hpp b/src/boost/libs/spirit/example/x3/calc/calc7/vm.hpp new file mode 100644 index 00000000..2836efd3 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc7/vm.hpp @@ -0,0 +1,48 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC7_VM_HPP) +#define BOOST_SPIRIT_X3_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_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; + }; + +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/ast.hpp b/src/boost/libs/spirit/example/x3/calc/calc8/ast.hpp new file mode 100644 index 00000000..aacde561 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/ast.hpp @@ -0,0 +1,98 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC8_AST_HPP) +#define BOOST_SPIRIT_X3_CALC8_AST_HPP + +#include <boost/spirit/home/x3/support/ast/variant.hpp> +#include <boost/spirit/home/x3/support/ast/position_tagged.hpp> +#include <boost/fusion/include/io.hpp> +#include <list> + +namespace client { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // The AST + /////////////////////////////////////////////////////////////////////////// + namespace x3 = boost::spirit::x3; + + struct nil {}; + struct signed_; + struct expression; + + struct variable : x3::position_tagged + { + variable(std::string const& name = "") : name(name) {} + std::string name; + }; + + struct operand : + x3::variant< + nil + , unsigned int + , variable + , x3::forward_ast<signed_> + , x3::forward_ast<expression> + > + { + using base_type::base_type; + using base_type::operator=; + }; + + struct signed_ + { + char sign; + operand operand_; + }; + + struct operation : x3::position_tagged + { + char operator_; + operand operand_; + }; + + struct expression : x3::position_tagged + { + operand first; + std::list<operation> rest; + }; + + struct assignment : x3::position_tagged + { + variable lhs; + expression rhs; + }; + + struct variable_declaration + { + assignment assign; + }; + + struct statement : + x3::variant< + variable_declaration + , assignment> + { + using base_type::base_type; + using base_type::operator=; + }; + + 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; + } +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/ast_adapted.hpp b/src/boost/libs/spirit/example/x3/calc/calc8/ast_adapted.hpp new file mode 100644 index 00000000..ca776109 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/ast_adapted.hpp @@ -0,0 +1,33 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC8_AST_ADAPTED_HPP) +#define BOOST_SPIRIT_X3_CALC8_AST_ADAPTED_HPP + +#include "ast.hpp" +#include <boost/fusion/include/adapt_struct.hpp> + +BOOST_FUSION_ADAPT_STRUCT(client::ast::signed_, + sign, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::operation, + operator_, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::expression, + first, rest +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::variable_declaration, + assign +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::assignment, + lhs, rhs +) + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/common.hpp b/src/boost/libs/spirit/example/x3/calc/calc8/common.hpp new file mode 100644 index 00000000..0a559573 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/common.hpp @@ -0,0 +1,28 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC8_COMMON_HPP) +#define BOOST_SPIRIT_X3_CALC8_COMMON_HPP + +#include <boost/spirit/home/x3.hpp> + +namespace client { namespace parser +{ + using x3::raw; + using x3::lexeme; + using x3::alpha; + using x3::alnum; + + struct identifier_class; + typedef x3::rule<identifier_class, std::string> identifier_type; + identifier_type const identifier = "identifier"; + + auto const identifier_def = raw[lexeme[(alpha | '_') >> *(alnum | '_')]]; + + BOOST_SPIRIT_DEFINE(identifier); +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/compiler.cpp b/src/boost/libs/spirit/example/x3/calc/calc8/compiler.cpp new file mode 100644 index 00000000..6410a981 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/compiler.cpp @@ -0,0 +1,217 @@ +/*============================================================================= + Copyright (c) 2001-2014 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/variant/apply_visitor.hpp> +#include <boost/assert.hpp> +#include <iostream> + +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 + { + auto 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] = int(n); + } + + void program::print_variables(std::vector<int> const& stack) const + { + for (auto const& p : variables) + { + std::cout << " " << p.first << ": " << stack[p.second] << std::endl; + } + } + + void program::print_assembler() const + { + auto pc = code.begin(); + + std::vector<std::string> locals(variables.size()); + typedef std::pair<std::string, int> pair; + for (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) + { + error_handler(x, "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; + for (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) + { + error_handler(x.lhs, "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) + { + error_handler(x.assign.lhs, "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); + for (ast::statement const& s : x) + { + if (!boost::apply_visitor(*this, s)) + { + program.clear(); + return false; + } + } + program[1] = int(program.nvars()); // now store the actual number of variables + return true; + } +}} diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/compiler.hpp b/src/boost/libs/spirit/example/x3/calc/calc8/compiler.hpp new file mode 100644 index 00000000..509298c9 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/compiler.hpp @@ -0,0 +1,80 @@ +/*============================================================================= + 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_X3_CALC8_COMPILER_HPP) +#define BOOST_SPIRIT_X3_CALC8_COMPILER_HPP + +#include "ast.hpp" +#include "error_handler.hpp" +#include <vector> +#include <map> + +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 operator[](std::size_t i) const { return code[i]; } + void clear() { code.clear(); variables.clear(); } + std::vector<int> const& operator()() const { return code; } + + std::size_t 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; + typedef std::function< + void(x3::position_tagged, std::string const&)> + error_handler_type; + + template <typename ErrorHandler> + compiler( + client::code_gen::program& program + , ErrorHandler const& error_handler) + : program(program) + , error_handler( + [&](x3::position_tagged pos, std::string const& msg) + { error_handler(pos, msg); } + ) + {} + + 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; + error_handler_type error_handler; + }; +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/config.hpp b/src/boost/libs/spirit/example/x3/calc/calc8/config.hpp new file mode 100644 index 00000000..d6664564 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/config.hpp @@ -0,0 +1,26 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC8_CONFIG_HPP) +#define BOOST_SPIRIT_X3_CALC8_CONFIG_HPP + +#include <boost/spirit/home/x3.hpp> +#include "error_handler.hpp" + +namespace client { namespace parser +{ + typedef std::string::const_iterator iterator_type; + typedef x3::phrase_parse_context<x3::ascii::space_type>::type phrase_context_type; + typedef error_handler<iterator_type> error_handler_type; + + typedef x3::context< + error_handler_tag + , std::reference_wrapper<error_handler_type> + , phrase_context_type> + context_type; +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/error_handler.hpp b/src/boost/libs/spirit/example/x3/calc/calc8/error_handler.hpp new file mode 100644 index 00000000..25cb268c --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/error_handler.hpp @@ -0,0 +1,44 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC8_ERROR_HANDLER_HPP) +#define BOOST_SPIRIT_X3_CALC8_ERROR_HANDLER_HPP + +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/utility/error_reporting.hpp> +#include <boost/spirit/home/x3/support/ast/position_tagged.hpp> +#include "expression.hpp" +#include "statement.hpp" + +namespace client { namespace parser +{ + namespace x3 = boost::spirit::x3; + + //////////////////////////////////////////////////////////////////////////// + // Our error handler + //////////////////////////////////////////////////////////////////////////// + template <typename Iterator> + using error_handler = x3::error_handler<Iterator>; + + // tag used to get our error handler from the context + using error_handler_tag = x3::error_handler_tag; + + struct error_handler_base + { + template <typename Iterator, typename Exception, typename Context> + x3::error_handler_result on_error( + Iterator& first, Iterator const& last + , Exception const& x, Context const& context) + { + std::string message = "Error! Expecting: " + x.which() + " here:"; + auto& error_handler = x3::get<error_handler_tag>(context).get(); + error_handler(x.where(), message); + return x3::error_handler_result::fail; + } + }; +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/expression.cpp b/src/boost/libs/spirit/example/x3/calc/calc8/expression.cpp new file mode 100644 index 00000000..e7463cd3 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/expression.cpp @@ -0,0 +1,13 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_def.hpp" +#include "config.hpp" + +namespace client { namespace parser +{ + BOOST_SPIRIT_INSTANTIATE(expression_type, iterator_type, context_type); +}} diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/expression.hpp b/src/boost/libs/spirit/example/x3/calc/calc8/expression.hpp new file mode 100644 index 00000000..d619510c --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/expression.hpp @@ -0,0 +1,26 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC8_EXPRESSION_HPP) +#define BOOST_SPIRIT_X3_CALC8_EXPRESSION_HPP + +#include <boost/spirit/home/x3.hpp> +#include "ast.hpp" + +namespace client +{ + namespace x3 = boost::spirit::x3; + namespace parser + { + struct expression_class; + typedef x3::rule<expression_class, ast::expression> expression_type; + BOOST_SPIRIT_DECLARE(expression_type); + } + + parser::expression_type const& expression(); +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/expression_def.hpp b/src/boost/libs/spirit/example/x3/calc/calc8/expression_def.hpp new file mode 100644 index 00000000..fe9eaee9 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/expression_def.hpp @@ -0,0 +1,91 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC8_EXPRESSION_DEF_HPP) +#define BOOST_SPIRIT_X3_CALC8_EXPRESSION_DEF_HPP + +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp> +#include "ast.hpp" +#include "ast_adapted.hpp" +#include "expression.hpp" +#include "common.hpp" +#include "error_handler.hpp" + +namespace client { namespace parser +{ + using x3::uint_; + using x3::char_; + using x3::raw; + using x3::lexeme; + using namespace x3::ascii; + + struct additive_expr_class; + struct multiplicative_expr_class; + struct unary_expr_class; + struct primary_expr_class; + + typedef x3::rule<additive_expr_class, ast::expression> additive_expr_type; + typedef x3::rule<multiplicative_expr_class, ast::expression> multiplicative_expr_type; + typedef x3::rule<unary_expr_class, ast::operand> unary_expr_type; + typedef x3::rule<primary_expr_class, ast::operand> primary_expr_type; + + expression_type const expression = "expression"; + additive_expr_type const additive_expr = "additive_expr"; + multiplicative_expr_type const multiplicative_expr = "multiplicative_expr"; + unary_expr_type unary_expr = "unary_expr"; + primary_expr_type primary_expr = "primary_expr"; + + auto const additive_expr_def = + multiplicative_expr + >> *( (char_('+') > multiplicative_expr) + | (char_('-') > multiplicative_expr) + ) + ; + + auto const multiplicative_expr_def = + unary_expr + >> *( (char_('*') > unary_expr) + | (char_('/') > unary_expr) + ) + ; + + auto const unary_expr_def = + primary_expr + | (char_('-') > primary_expr) + | (char_('+') > primary_expr) + ; + + auto const primary_expr_def = + uint_ + | identifier + | '(' > expression > ')' + ; + + auto const expression_def = additive_expr; + + BOOST_SPIRIT_DEFINE( + expression + , additive_expr + , multiplicative_expr + , unary_expr + , primary_expr + ); + + struct unary_expr_class : x3::annotate_on_success {}; + struct primary_expr_class : x3::annotate_on_success {}; + +}} + +namespace client +{ + parser::expression_type const& expression() + { + return parser::expression; + } +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/main.cpp b/src/boost/libs/spirit/example/x3/calc/calc8/main.cpp new file mode 100644 index 00000000..e87ccbb7 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/main.cpp @@ -0,0 +1,114 @@ +/*============================================================================= + Copyright (c) 2001-2014 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. +// [ JDG May 17, 2014 ] Ported from qi calc7 example. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "ast.hpp" +#include "vm.hpp" +#include "compiler.hpp" +#include "statement.hpp" +#include "error_handler.hpp" +#include "config.hpp" +#include <iostream> + +/////////////////////////////////////////////////////////////////////////////// +// 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'; + } + + using client::parser::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 + + using boost::spirit::x3::with; + using client::parser::error_handler_type; + using client::parser::error_handler_tag; + error_handler_type error_handler(iter, end, std::cerr); // Our error handler + + // Our compiler + client::code_gen::compiler compile(program, error_handler); + + // Our parser + auto const parser = + // we pass our error handler to the parser so we can access + // it later on in our on_error and on_sucess handlers + with<error_handler_tag>(std::ref(error_handler)) + [ + client::statement() + ]; + + using boost::spirit::x3::ascii::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; +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/statement.cpp b/src/boost/libs/spirit/example/x3/calc/calc8/statement.cpp new file mode 100644 index 00000000..ec4bca61 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/statement.cpp @@ -0,0 +1,13 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_def.hpp" +#include "config.hpp" + +namespace client { namespace parser +{ + BOOST_SPIRIT_INSTANTIATE(statement_type, iterator_type, context_type); +}} diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/statement.hpp b/src/boost/libs/spirit/example/x3/calc/calc8/statement.hpp new file mode 100644 index 00000000..7b8973d0 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/statement.hpp @@ -0,0 +1,26 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC8_STATEMENT_HPP) +#define BOOST_SPIRIT_X3_CALC8_STATEMENT_HPP + +#include <boost/spirit/home/x3.hpp> +#include "ast.hpp" + +namespace client +{ + namespace x3 = boost::spirit::x3; + namespace parser + { + struct statement_class; + typedef x3::rule<statement_class, ast::statement_list> statement_type; + BOOST_SPIRIT_DECLARE(statement_type); + } + + parser::statement_type const& statement(); +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/statement_def.hpp b/src/boost/libs/spirit/example/x3/calc/calc8/statement_def.hpp new file mode 100644 index 00000000..8772c76f --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/statement_def.hpp @@ -0,0 +1,84 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC8_STATEMENT_DEF_HPP) +#define BOOST_SPIRIT_X3_CALC8_STATEMENT_DEF_HPP + +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp> +#include "ast.hpp" +#include "ast_adapted.hpp" +#include "statement.hpp" +#include "expression.hpp" +#include "common.hpp" +#include "error_handler.hpp" + +namespace client { namespace parser +{ + using x3::raw; + using x3::lexeme; + using namespace x3::ascii; + + struct statement_list_class; + struct variable_declaration_class; + struct assignment_class; + struct variable_class; + + typedef x3::rule<statement_list_class, ast::statement_list> statement_list_type; + typedef x3::rule<variable_declaration_class, ast::variable_declaration> variable_declaration_type; + typedef x3::rule<assignment_class, ast::assignment> assignment_type; + typedef x3::rule<variable_class, ast::variable> variable_type; + + statement_type const statement = "statement"; + statement_list_type const statement_list = "statement_list"; + variable_declaration_type const variable_declaration = "variable_declaration"; + assignment_type const assignment = "assignment"; + variable_type const variable = "variable"; + + // Import the expression rule + namespace { auto const& expression = client::expression(); } + + auto const statement_list_def = + +(variable_declaration | assignment) + ; + + auto const variable_declaration_def = + lexeme["var" >> !(alnum | '_')] // make sure we have whole words + > assignment + ; + + auto const assignment_def = + variable + > '=' + > expression + > ';' + ; + + auto const variable_def = identifier; + auto const statement_def = statement_list; + + BOOST_SPIRIT_DEFINE( + statement + , statement_list + , variable_declaration + , assignment + , variable + ); + + struct statement_class : error_handler_base, x3::annotate_on_success {}; + struct assignment_class : x3::annotate_on_success {}; + struct variable_class : x3::annotate_on_success {}; +}} + +namespace client +{ + parser::statement_type const& statement() + { + return parser::statement; + } +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/vm.cpp b/src/boost/libs/spirit/example/x3/calc/calc8/vm.cpp new file mode 100644 index 00000000..ff6c8400 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/vm.cpp @@ -0,0 +1,64 @@ +/*============================================================================= + Copyright (c) 2001-2014 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) + { + auto pc = code.begin(); + auto 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; + } + } + } +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc8/vm.hpp b/src/boost/libs/spirit/example/x3/calc/calc8/vm.hpp new file mode 100644 index 00000000..29072977 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc8/vm.hpp @@ -0,0 +1,51 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC8_VM_HPP) +#define BOOST_SPIRIT_X3_CALC8_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 diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/ast.hpp b/src/boost/libs/spirit/example/x3/calc/calc9/ast.hpp new file mode 100644 index 00000000..b3e82c49 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/ast.hpp @@ -0,0 +1,139 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC9_AST_HPP) +#define BOOST_SPIRIT_X3_CALC9_AST_HPP + +#include <boost/spirit/home/x3/support/ast/variant.hpp> +#include <boost/spirit/home/x3/support/ast/position_tagged.hpp> +#include <boost/fusion/include/io.hpp> +#include <boost/optional.hpp> +#include <list> + +namespace client { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // The AST + /////////////////////////////////////////////////////////////////////////// + namespace x3 = boost::spirit::x3; + + struct nil {}; + struct unary; + struct expression; + + struct variable : x3::position_tagged + { + variable(std::string const& name = "") : name(name) {} + std::string name; + }; + + struct operand : + x3::variant< + nil + , unsigned int + , variable + , x3::forward_ast<unary> + , x3::forward_ast<expression> + > + { + using base_type::base_type; + using base_type::operator=; + }; + + enum optoken + { + op_plus, + op_minus, + op_times, + op_divide, + op_positive, + op_negative, + op_not, + op_equal, + op_not_equal, + op_less, + op_less_equal, + op_greater, + op_greater_equal, + op_and, + op_or + }; + + struct unary + { + optoken operator_; + operand operand_; + }; + + struct operation : x3::position_tagged + { + optoken operator_; + operand operand_; + }; + + struct expression : x3::position_tagged + { + operand first; + std::list<operation> rest; + }; + + struct assignment : x3::position_tagged + { + variable lhs; + expression rhs; + }; + + struct variable_declaration + { + assignment assign; + }; + + struct if_statement; + struct while_statement; + struct statement_list; + + struct statement : + x3::variant< + variable_declaration + , assignment + , boost::recursive_wrapper<if_statement> + , boost::recursive_wrapper<while_statement> + , boost::recursive_wrapper<statement_list> + > + { + using base_type::base_type; + using base_type::operator=; + }; + + struct statement_list : std::list<statement> {}; + + struct if_statement + { + expression condition; + statement then; + boost::optional<statement> else_; + }; + + struct while_statement + { + expression condition; + statement body; + }; + + // 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; + } +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/ast_adapted.hpp b/src/boost/libs/spirit/example/x3/calc/calc9/ast_adapted.hpp new file mode 100644 index 00000000..3f8d3cb1 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/ast_adapted.hpp @@ -0,0 +1,41 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC9_AST_ADAPTED_HPP) +#define BOOST_SPIRIT_X3_CALC9_AST_ADAPTED_HPP + +#include "ast.hpp" +#include <boost/fusion/include/adapt_struct.hpp> + +BOOST_FUSION_ADAPT_STRUCT(client::ast::unary, + operator_, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::operation, + operator_, operand_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::expression, + first, rest +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::variable_declaration, + assign +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::assignment, + lhs, rhs +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::if_statement, + condition, then, else_ +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::while_statement, + condition, body +) + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/common.hpp b/src/boost/libs/spirit/example/x3/calc/calc9/common.hpp new file mode 100644 index 00000000..1f1a7011 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/common.hpp @@ -0,0 +1,28 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC9_COMMON_HPP) +#define BOOST_SPIRIT_X3_CALC9_COMMON_HPP + +#include <boost/spirit/home/x3.hpp> + +namespace client { namespace parser +{ + using x3::raw; + using x3::lexeme; + using x3::alpha; + using x3::alnum; + + struct identifier_class; + typedef x3::rule<identifier_class, std::string> identifier_type; + identifier_type const identifier = "identifier"; + + auto const identifier_def = raw[lexeme[(alpha | '_') >> *(alnum | '_')]]; + + BOOST_SPIRIT_DEFINE(identifier); +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/compiler.cpp b/src/boost/libs/spirit/example/x3/calc/calc9/compiler.cpp new file mode 100644 index 00000000..620fa22d --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/compiler.cpp @@ -0,0 +1,376 @@ +/*============================================================================= + Copyright (c) 2001-2014 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/variant/apply_visitor.hpp> +#include <boost/assert.hpp> +#include <iostream> +#include <set> +#include <iostream> + +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 + { + auto 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] = int(n); + } + + void program::print_variables(std::vector<int> const& stack) const + { + for (auto const& p : variables) + { + std::cout << " " << p.first << ": " << stack[p.second] << std::endl; + } + } + + void program::print_assembler() const + { + auto pc = code.begin(); + + std::vector<std::string> locals(variables.size()); + typedef std::pair<std::string, int> pair; + for (pair const& p : variables) + { + locals[p.second] = p.first; + std::cout << "local " + << p.first << ", @" << p.second << std::endl; + } + + std::map<std::size_t, std::string> lines; + std::set<std::size_t> jumps; + + while (pc != code.end()) + { + std::string line; + std::size_t address = pc - code.begin(); + + switch (*pc++) + { + case op_neg: + line += " op_neg"; + break; + + case op_not: + line += " op_not"; + break; + + case op_add: + line += " op_add"; + break; + + case op_sub: + line += " op_sub"; + break; + + case op_mul: + line += " op_mul"; + break; + + case op_div: + line += " op_div"; + break; + + case op_eq: + line += " op_eq"; + break; + + case op_neq: + line += " op_neq"; + break; + + case op_lt: + line += " op_lt"; + break; + + case op_lte: + line += " op_lte"; + break; + + case op_gt: + line += " op_gt"; + break; + + case op_gte: + line += " op_gte"; + break; + + case op_and: + line += " op_and"; + break; + + case op_or: + line += " op_or"; + break; + + case op_load: + line += " op_load "; + line += locals[*pc++]; + break; + + case op_store: + line += " op_store "; + line += locals[*pc++]; + break; + + case op_int: + line += " op_int "; + line += std::to_string(*pc++); + break; + + case op_true: + line += " op_true"; + break; + + case op_false: + line += " op_false"; + break; + + case op_jump: + { + line += " op_jump "; + std::size_t pos = (pc - code.begin()) + *pc++; + if (pos == code.size()) + line += "end"; + else + line += std::to_string(pos); + jumps.insert(pos); + } + break; + + case op_jump_if: + { + line += " op_jump_if "; + std::size_t pos = (pc - code.begin()) + *pc++; + if (pos == code.size()) + line += "end"; + else + line += std::to_string(pos); + jumps.insert(pos); + } + break; + + case op_stk_adj: + line += " op_stk_adj "; + line += std::to_string(*pc++); + break; + } + lines[address] = line; + } + + std::cout << "start:" << std::endl; + for (auto const& l : lines) + { + std::size_t pos = l.first; + if (jumps.find(pos) != jumps.end()) + std::cout << pos << ':' << std::endl; + std::cout << l.second << std::endl; + } + + std::cout << "end:" << std::endl; + } + + bool compiler::operator()(unsigned int x) const + { + program.op(op_int, x); + return true; + } + + bool compiler::operator()(bool x) const + { + program.op(x ? op_true : op_false); + return true; + } + + bool compiler::operator()(ast::variable const& x) const + { + int const* p = program.find_var(x.name); + if (p == 0) + { + error_handler(x, "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 ast::op_plus: program.op(op_add); break; + case ast::op_minus: program.op(op_sub); break; + case ast::op_times: program.op(op_mul); break; + case ast::op_divide: program.op(op_div); break; + + case ast::op_equal: program.op(op_eq); break; + case ast::op_not_equal: program.op(op_neq); break; + case ast::op_less: program.op(op_lt); break; + case ast::op_less_equal: program.op(op_lte); break; + case ast::op_greater: program.op(op_gt); break; + case ast::op_greater_equal: program.op(op_gte); break; + + case ast::op_and: program.op(op_and); break; + case ast::op_or: program.op(op_or); break; + default: BOOST_ASSERT(0); return false; + } + return true; + } + + bool compiler::operator()(ast::unary const& x) const + { + if (!boost::apply_visitor(*this, x.operand_)) + return false; + switch (x.operator_) + { + case ast::op_negative: program.op(op_neg); break; + case ast::op_not: program.op(op_not); break; + case ast::op_positive: 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; + for (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) + { + error_handler(x.lhs, "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) + { + error_handler(x.assign.lhs, "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 const& x) const + { + return boost::apply_visitor(*this, x); + } + + bool compiler::operator()(ast::statement_list const& x) const + { + for (auto const& s : x) + { + if (!(*this)(s)) + return false; + } + return true; + } + + bool compiler::operator()(ast::if_statement const& x) const + { + if (!(*this)(x.condition)) + return false; + program.op(op_jump_if, 0); // we shall fill this (0) in later + std::size_t skip = program.size()-1; // mark its position + if (!(*this)(x.then)) + return false; + program[skip] = int(program.size()-skip); // now we know where to jump to (after the if branch) + + if (x.else_) // We got an alse + { + program[skip] += 2; // adjust for the "else" jump + program.op(op_jump, 0); // we shall fill this (0) in later + std::size_t exit = program.size()-1; // mark its position + if (!(*this)(*x.else_)) + return false; + program[exit] = int(program.size()-exit); // now we know where to jump to (after the else branch) + } + + return true; + } + + bool compiler::operator()(ast::while_statement const& x) const + { + std::size_t loop = program.size(); // mark our position + if (!(*this)(x.condition)) + return false; + program.op(op_jump_if, 0); // we shall fill this (0) in later + std::size_t exit = program.size()-1; // mark its position + if (!(*this)(x.body)) + return false; + program.op(op_jump, + int(loop-1) - int(program.size())); // loop back + program[exit] = int(program.size()-exit); // now we know where to jump to (to exit the loop) + return true; + } + + bool compiler::start(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); + + if (!(*this)(x)) + { + program.clear(); + return false; + } + program[1] = int(program.nvars()); // now store the actual number of variables + return true; + } +}} diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/compiler.hpp b/src/boost/libs/spirit/example/x3/calc/calc9/compiler.hpp new file mode 100644 index 00000000..96b32a70 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/compiler.hpp @@ -0,0 +1,87 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC9_COMPILER_HPP) +#define BOOST_SPIRIT_X3_CALC9_COMPILER_HPP + +#include "ast.hpp" +#include "error_handler.hpp" +#include <vector> +#include <map> + +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 operator[](std::size_t i) const { return code[i]; } + void clear() { code.clear(); variables.clear(); } + std::size_t size() const { return code.size(); } + std::vector<int> const& operator()() const { return code; } + + std::size_t 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; + typedef std::function< + void(x3::position_tagged, std::string const&)> + error_handler_type; + + template <typename ErrorHandler> + compiler( + client::code_gen::program& program + , ErrorHandler const& error_handler) + : program(program) + , error_handler( + [&](x3::position_tagged pos, std::string const& msg) + { error_handler(pos, msg); } + ) + {} + + bool operator()(ast::nil) const { BOOST_ASSERT(0); return false; } + bool operator()(unsigned int x) const; + bool operator()(bool x) const; + bool operator()(ast::variable const& x) const; + bool operator()(ast::operation const& x) const; + bool operator()(ast::unary 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; + bool operator()(ast::statement const& x) const; + bool operator()(ast::if_statement const& x) const; + bool operator()(ast::while_statement const& x) const; + + bool start(ast::statement_list const& x) const; + + client::code_gen::program& program; + error_handler_type error_handler; + }; +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/config.hpp b/src/boost/libs/spirit/example/x3/calc/calc9/config.hpp new file mode 100644 index 00000000..a250bf72 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/config.hpp @@ -0,0 +1,26 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC9_CONFIG_HPP) +#define BOOST_SPIRIT_X3_CALC9_CONFIG_HPP + +#include <boost/spirit/home/x3.hpp> +#include "error_handler.hpp" + +namespace client { namespace parser +{ + typedef std::string::const_iterator iterator_type; + typedef x3::phrase_parse_context<x3::ascii::space_type>::type phrase_context_type; + typedef error_handler<iterator_type> error_handler_type; + + typedef x3::context< + error_handler_tag + , std::reference_wrapper<error_handler_type> + , phrase_context_type> + context_type; +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/error_handler.hpp b/src/boost/libs/spirit/example/x3/calc/calc9/error_handler.hpp new file mode 100644 index 00000000..8d4e8a65 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/error_handler.hpp @@ -0,0 +1,44 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC9_ERROR_HANDLER_HPP) +#define BOOST_SPIRIT_X3_CALC9_ERROR_HANDLER_HPP + +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/utility/error_reporting.hpp> +#include <boost/spirit/home/x3/support/ast/position_tagged.hpp> +#include "expression.hpp" +#include "statement.hpp" + +namespace client { namespace parser +{ + namespace x3 = boost::spirit::x3; + + //////////////////////////////////////////////////////////////////////////// + // Our error handler + //////////////////////////////////////////////////////////////////////////// + template <typename Iterator> + using error_handler = x3::error_handler<Iterator>; + + // tag used to get our error handler from the context + using error_handler_tag = x3::error_handler_tag; + + struct error_handler_base + { + template <typename Iterator, typename Exception, typename Context> + x3::error_handler_result on_error( + Iterator& first, Iterator const& last + , Exception const& x, Context const& context) + { + std::string message = "Error! Expecting: " + x.which() + " here:"; + auto& error_handler = x3::get<error_handler_tag>(context).get(); + error_handler(x.where(), message); + return x3::error_handler_result::fail; + } + }; +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/expression.cpp b/src/boost/libs/spirit/example/x3/calc/calc9/expression.cpp new file mode 100644 index 00000000..e7463cd3 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/expression.cpp @@ -0,0 +1,13 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_def.hpp" +#include "config.hpp" + +namespace client { namespace parser +{ + BOOST_SPIRIT_INSTANTIATE(expression_type, iterator_type, context_type); +}} diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/expression.hpp b/src/boost/libs/spirit/example/x3/calc/calc9/expression.hpp new file mode 100644 index 00000000..f9fa1cc3 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/expression.hpp @@ -0,0 +1,26 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC9_EXPRESSION_HPP) +#define BOOST_SPIRIT_X3_CALC9_EXPRESSION_HPP + +#include <boost/spirit/home/x3.hpp> +#include "ast.hpp" + +namespace client +{ + namespace x3 = boost::spirit::x3; + namespace parser + { + struct expression_class; + typedef x3::rule<expression_class, ast::expression> expression_type; + BOOST_SPIRIT_DECLARE(expression_type); + } + + parser::expression_type const& expression(); +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/expression_def.hpp b/src/boost/libs/spirit/example/x3/calc/calc9/expression_def.hpp new file mode 100644 index 00000000..251fb840 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/expression_def.hpp @@ -0,0 +1,182 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC9_EXPRESSION_DEF_HPP) +#define BOOST_SPIRIT_X3_CALC9_EXPRESSION_DEF_HPP + +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp> +#include "ast.hpp" +#include "ast_adapted.hpp" +#include "expression.hpp" +#include "common.hpp" +#include "error_handler.hpp" + +namespace client { namespace parser +{ + using x3::uint_; + using x3::char_; + using x3::bool_; + using x3::raw; + using x3::lexeme; + using namespace x3::ascii; + + //////////////////////////////////////////////////////////////////////////// + // Tokens + //////////////////////////////////////////////////////////////////////////// + + x3::symbols<ast::optoken> equality_op; + x3::symbols<ast::optoken> relational_op; + x3::symbols<ast::optoken> logical_op; + x3::symbols<ast::optoken> additive_op; + x3::symbols<ast::optoken> multiplicative_op; + x3::symbols<ast::optoken> unary_op; + x3::symbols<> keywords; + + void add_keywords() + { + static bool once = false; + if (once) + return; + once = true; + + logical_op.add + ("&&", ast::op_and) + ("||", ast::op_or) + ; + + equality_op.add + ("==", ast::op_equal) + ("!=", ast::op_not_equal) + ; + + relational_op.add + ("<", ast::op_less) + ("<=", ast::op_less_equal) + (">", ast::op_greater) + (">=", ast::op_greater_equal) + ; + + additive_op.add + ("+", ast::op_plus) + ("-", ast::op_minus) + ; + + multiplicative_op.add + ("*", ast::op_times) + ("/", ast::op_divide) + ; + + unary_op.add + ("+", ast::op_positive) + ("-", ast::op_negative) + ("!", ast::op_not) + ; + + keywords.add + ("var") + ("true") + ("false") + ("if") + ("else") + ("while") + ; + } + + //////////////////////////////////////////////////////////////////////////// + // Main expression grammar + //////////////////////////////////////////////////////////////////////////// + + struct equality_expr_class; + struct relational_expr_class; + struct logical_expr_class; + struct additive_expr_class; + struct multiplicative_expr_class; + struct unary_expr_class; + struct primary_expr_class; + + typedef x3::rule<equality_expr_class, ast::expression> equality_expr_type; + typedef x3::rule<relational_expr_class, ast::expression> relational_expr_type; + typedef x3::rule<logical_expr_class, ast::expression> logical_expr_type; + typedef x3::rule<additive_expr_class, ast::expression> additive_expr_type; + typedef x3::rule<multiplicative_expr_class, ast::expression> multiplicative_expr_type; + typedef x3::rule<unary_expr_class, ast::operand> unary_expr_type; + typedef x3::rule<primary_expr_class, ast::operand> primary_expr_type; + + expression_type const expression = "expression"; + equality_expr_type const equality_expr = "equality_expr"; + relational_expr_type const relational_expr = "relational_expr"; + logical_expr_type const logical_expr = "logical_expr"; + additive_expr_type const additive_expr = "additive_expr"; + multiplicative_expr_type const multiplicative_expr = "multiplicative_expr"; + unary_expr_type const unary_expr = "unary_expr"; + primary_expr_type const primary_expr = "primary_expr"; + + auto const logical_expr_def = + equality_expr + >> *(logical_op > equality_expr) + ; + + auto const equality_expr_def = + relational_expr + >> *(equality_op > relational_expr) + ; + + auto const relational_expr_def = + additive_expr + >> *(relational_op > additive_expr) + ; + + auto const additive_expr_def = + multiplicative_expr + >> *(additive_op > multiplicative_expr) + ; + + auto const multiplicative_expr_def = + unary_expr + >> *(multiplicative_op > unary_expr) + ; + + auto const unary_expr_def = + primary_expr + | (unary_op > primary_expr) + ; + + auto const primary_expr_def = + uint_ + | bool_ + | (!keywords >> identifier) + | '(' > expression > ')' + ; + + auto const expression_def = logical_expr; + + BOOST_SPIRIT_DEFINE( + expression + , logical_expr + , equality_expr + , relational_expr + , additive_expr + , multiplicative_expr + , unary_expr + , primary_expr + ); + + struct unary_expr_class : x3::annotate_on_success {}; + struct primary_expr_class : x3::annotate_on_success {}; + +}} + +namespace client +{ + parser::expression_type const& expression() + { + parser::add_keywords(); + return parser::expression; + } +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/main.cpp b/src/boost/libs/spirit/example/x3/calc/calc9/main.cpp new file mode 100644 index 00000000..154860a3 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/main.cpp @@ -0,0 +1,110 @@ +/*============================================================================= + Copyright (c) 2001-2014 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 boolean expressions and control structures. +// Is it obvious now what we are up to? ;-) +// +// [ JDG April 9, 2007 ] spirit2 +// [ JDG February 18, 2011 ] Pure attributes. No semantic actions. +// [ JDG June 6, 2014 ] Ported from qi calc8 example. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "ast.hpp" +#include "vm.hpp" +#include "compiler.hpp" +#include "statement.hpp" +#include "error_handler.hpp" +#include "config.hpp" +#include <iostream> + +/////////////////////////////////////////////////////////////////////////////// +// 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'; + } + + using client::parser::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 + + using boost::spirit::x3::with; + using client::parser::error_handler_type; + error_handler_type error_handler(iter, end, std::cerr); // Our error handler + + // Our compiler + client::code_gen::compiler compile(program, error_handler); + + // Our parser + auto const parser = + // we pass our error handler to the parser so we can access + // it later on in our on_error and on_sucess handlers + with<client::parser::error_handler_tag>(std::ref(error_handler)) + [ + client::statement() + ]; + + using boost::spirit::x3::ascii::space; + bool success = phrase_parse(iter, end, parser, space, ast); + + std::cout << "-------------------------\n"; + + if (success && iter == end) + { + if (compile.start(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; +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/statement.cpp b/src/boost/libs/spirit/example/x3/calc/calc9/statement.cpp new file mode 100644 index 00000000..ec4bca61 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/statement.cpp @@ -0,0 +1,13 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_def.hpp" +#include "config.hpp" + +namespace client { namespace parser +{ + BOOST_SPIRIT_INSTANTIATE(statement_type, iterator_type, context_type); +}} diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/statement.hpp b/src/boost/libs/spirit/example/x3/calc/calc9/statement.hpp new file mode 100644 index 00000000..d0c3f514 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/statement.hpp @@ -0,0 +1,27 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC9_STATEMENT_HPP) +#define BOOST_SPIRIT_X3_CALC9_STATEMENT_HPP + +#include <boost/spirit/home/x3.hpp> +#include "ast.hpp" + +namespace client +{ + namespace x3 = boost::spirit::x3; + namespace parser + { + struct statement_class; + typedef x3::rule<statement_class, ast::statement_list> statement_type; + typedef statement_type::id statement_id; + BOOST_SPIRIT_DECLARE(statement_type); + } + + parser::statement_type const& statement(); +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/statement_def.hpp b/src/boost/libs/spirit/example/x3/calc/calc9/statement_def.hpp new file mode 100644 index 00000000..84355caa --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/statement_def.hpp @@ -0,0 +1,84 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC9_STATEMENT_DEF_HPP) +#define BOOST_SPIRIT_X3_CALC9_STATEMENT_DEF_HPP + +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp> +#include "ast.hpp" +#include "ast_adapted.hpp" +#include "statement.hpp" +#include "expression.hpp" +#include "common.hpp" +#include "error_handler.hpp" + +namespace client { namespace parser +{ + using x3::raw; + using x3::lexeme; + using namespace x3::ascii; + + struct statement_list_class; + struct variable_declaration_class; + struct assignment_class; + struct variable_class; + + typedef x3::rule<statement_list_class, ast::statement_list> statement_list_type; + typedef x3::rule<variable_declaration_class, ast::variable_declaration> variable_declaration_type; + typedef x3::rule<assignment_class, ast::assignment> assignment_type; + typedef x3::rule<variable_class, ast::variable> variable_type; + + statement_type const statement("statement"); + statement_list_type const statement_list("statement_list"); + variable_declaration_type const variable_declaration("variable_declaration"); + assignment_type const assignment("assignment"); + variable_type const variable("variable"); + + // Import the expression rule + namespace { auto const& expression = client::expression(); } + + auto const statement_list_def = + +(variable_declaration | assignment) + ; + + auto const variable_declaration_def = + lexeme["var" >> !(alnum | '_')] // make sure we have whole words + > assignment + ; + + auto const assignment_def = + variable + > '=' + > expression + > ';' + ; + + auto const variable_def = identifier; + auto const statement_def = statement_list; + + BOOST_SPIRIT_DEFINE( + statement + , statement_list + , variable_declaration + , assignment + , variable + ); + + struct statement_class : error_handler_base, x3::annotate_on_success {}; + struct assignment_class : x3::annotate_on_success {}; + struct variable_class : x3::annotate_on_success {}; +}} + +namespace client +{ + parser::statement_type const& statement() + { + return parser::statement; + } +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/vm.cpp b/src/boost/libs/spirit/example/x3/calc/calc9/vm.cpp new file mode 100644 index 00000000..d3c16713 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/vm.cpp @@ -0,0 +1,156 @@ +/*============================================================================= + Copyright (c) 2001-2014 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" +#include <boost/assert.hpp> + +namespace client +{ + int vmachine::execute( + std::vector<int> const& code + , std::vector<int>::const_iterator pc + , std::vector<int>::iterator frame_ptr + ) + { + std::vector<int>::iterator stack_ptr = frame_ptr; + + while (pc != code.end()) + { + BOOST_ASSERT(pc != code.end()); + + switch (*pc++) + { + case op_neg: + stack_ptr[-1] = -stack_ptr[-1]; + break; + + case op_not: + stack_ptr[-1] = !bool(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_eq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] == stack_ptr[0]); + break; + + case op_neq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] != stack_ptr[0]); + break; + + case op_lt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] < stack_ptr[0]); + break; + + case op_lte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] <= stack_ptr[0]); + break; + + case op_gt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] > stack_ptr[0]); + break; + + case op_gte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] >= stack_ptr[0]); + break; + + case op_and: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) && bool(stack_ptr[0]); + break; + + case op_or: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) || bool(stack_ptr[0]); + break; + + case op_load: + *stack_ptr++ = frame_ptr[*pc++]; + break; + + case op_store: + --stack_ptr; + frame_ptr[*pc++] = stack_ptr[0]; + break; + + case op_int: + *stack_ptr++ = *pc++; + break; + + case op_true: + *stack_ptr++ = true; + break; + + case op_false: + *stack_ptr++ = false; + break; + + case op_jump: + pc += *pc; + break; + + case op_jump_if: + if (!bool(stack_ptr[-1])) + pc += *pc; + else + ++pc; + --stack_ptr; + break; + + case op_stk_adj: + stack_ptr = stack.begin() + *pc++; + break; + + case op_call: + { + int nargs = *pc++; + int jump = *pc++; + + // a function call is a recursive call to execute + int r = execute( + code + , code.begin() + jump + , stack_ptr - nargs + ); + + // cleanup after return from function + stack_ptr[-nargs] = r; // get return value + stack_ptr -= (nargs - 1); // the stack will now contain + // the return value + } + break; + + case op_return: + return stack_ptr[-1]; + } + } + return -1; + } +} diff --git a/src/boost/libs/spirit/example/x3/calc/calc9/vm.hpp b/src/boost/libs/spirit/example/x3/calc/calc9/vm.hpp new file mode 100644 index 00000000..345611cb --- /dev/null +++ b/src/boost/libs/spirit/example/x3/calc/calc9/vm.hpp @@ -0,0 +1,81 @@ +/*============================================================================= + Copyright (c) 2001-2014 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_X3_CALC9_VM_HPP) +#define BOOST_SPIRIT_X3_CALC9_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_not, // boolean negate the top stack entry + op_eq, // compare the top two stack entries for == + op_neq, // compare the top two stack entries for != + op_lt, // compare the top two stack entries for < + op_lte, // compare the top two stack entries for <= + op_gt, // compare the top two stack entries for > + op_gte, // compare the top two stack entries for >= + + op_and, // logical and top two stack entries + op_or, // logical or top two stack entries + + op_load, // load a variable + op_store, // store a variable + + op_int, // push constant integer into the stack + op_true, // push constant 0 into the stack + op_false, // push constant 1 into the stack + + op_jump_if, // jump to a relative position in the code if top stack + // evaluates to false + op_jump, // jump to a relative position in the code + + op_stk_adj, // adjust the stack (for args and locals) + op_call, // function call + op_return // return from function + }; + + class vmachine + { + public: + + vmachine(unsigned stackSize = 4096) + : stack(stackSize) + { + } + + int execute( + std::vector<int> const& code // the program code + , std::vector<int>::const_iterator pc // program counter + , std::vector<int>::iterator frame_ptr // start of arguments and locals + ); + + int execute(std::vector<int> const& code) + { + return execute(code, code.begin(), stack.begin()); + }; + + std::vector<int> const& get_stack() const { return stack; }; + + private: + + std::vector<int> stack; + }; +} + +#endif + diff --git a/src/boost/libs/spirit/example/x3/complex_number.cpp b/src/boost/libs/spirit/example/x3/complex_number.cpp new file mode 100644 index 00000000..3d63ab61 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/complex_number.cpp @@ -0,0 +1,98 @@ +/*============================================================================= + Copyright (c) 2002-2015 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 parser. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG May 9, 2007 ] spirit2 +// [ JDG May 12, 2015 ] spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> + +#include <iostream> +#include <string> +#include <complex> + +/////////////////////////////////////////////////////////////////////////////// +// Our complex number parser/compiler +/////////////////////////////////////////////////////////////////////////////// +namespace client +{ + template <typename Iterator> + bool parse_complex(Iterator first, Iterator last, std::complex<double>& c) + { + using boost::spirit::x3::double_; + using boost::spirit::x3::_attr; + using boost::spirit::x3::phrase_parse; + using boost::spirit::x3::ascii::space; + + double rN = 0.0; + double iN = 0.0; + auto fr = [&](auto& ctx){ rN = _attr(ctx); }; + auto fi = [&](auto& ctx){ iN = _attr(ctx); }; + + bool r = phrase_parse(first, last, + + // Begin grammar + ( + '(' >> double_[fr] + >> -(',' >> double_[fi]) >> ')' + | double_[fr] + ), + // End grammar + + space); + + if (!r || first != last) // fail if we did not get a full match + return false; + c = std::complex<double>(rN, iN); + return r; + } +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA complex number micro parser 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::cout << "Parsing succeeded\n"; + std::cout << "got: " << c << std::endl; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/src/boost/libs/spirit/example/x3/employee.cpp b/src/boost/libs/spirit/example/x3/employee.cpp new file mode 100644 index 00000000..62571027 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/employee.cpp @@ -0,0 +1,134 @@ +/*============================================================================= + Copyright (c) 2002-2015 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 parser for arbitrary tuples. This example presents a parser +// for an employee structure. +// +// [ JDG May 9, 2007 ] +// [ JDG May 13, 2015 ] spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/fusion/include/io.hpp> + +#include <iostream> +#include <string> + +namespace client { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // Our employee struct + /////////////////////////////////////////////////////////////////////////// + struct employee + { + int age; + std::string forename; + std::string surname; + double salary; + }; + + using boost::fusion::operator<<; +}} + +// We need to tell fusion about our employee struct +// to make it a first-class fusion citizen. This has to +// be in global scope. + +BOOST_FUSION_ADAPT_STRUCT(client::ast::employee, + age, forename, surname, salary +) + +namespace client +{ + /////////////////////////////////////////////////////////////////////////////// + // Our employee parser + /////////////////////////////////////////////////////////////////////////////// + namespace parser + { + namespace x3 = boost::spirit::x3; + namespace ascii = boost::spirit::x3::ascii; + + using x3::int_; + using x3::lit; + using x3::double_; + using x3::lexeme; + using ascii::char_; + + x3::rule<class employee, ast::employee> const employee = "employee"; + + auto const quoted_string = lexeme['"' >> +(char_ - '"') >> '"']; + + auto const employee_def = + lit("employee") + >> '{' + >> int_ >> ',' + >> quoted_string >> ',' + >> quoted_string >> ',' + >> double_ + >> '}' + ; + + BOOST_SPIRIT_DEFINE(employee); + } +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tAn employee parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout + << "Give me an employee of the form :" + << "employee{age, \"forename\", \"surname\", salary } \n"; + std::cout << "Type [q or Q] to quit\n\n"; + + using boost::spirit::x3::ascii::space; + typedef std::string::const_iterator iterator_type; + using client::parser::employee; + + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + client::ast::employee emp; + iterator_type iter = str.begin(); + iterator_type const end = str.end(); + bool r = phrase_parse(iter, end, employee, space, emp); + + if (r && iter == end) + { + std::cout << boost::fusion::tuple_open('['); + std::cout << boost::fusion::tuple_close(']'); + std::cout << boost::fusion::tuple_delimiter(", "); + + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "got: " << emp << std::endl; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/src/boost/libs/spirit/example/x3/error_handling.cpp b/src/boost/libs/spirit/example/x3/error_handling.cpp new file mode 100644 index 00000000..3b890a5e --- /dev/null +++ b/src/boost/libs/spirit/example/x3/error_handling.cpp @@ -0,0 +1,277 @@ +/*============================================================================= + Copyright (c) 2002-2018 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) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Based on the employee parser (see employee.cpp), this example shows how +// to implement error handling. This example also shows how to "inject" client +// data, using the "with" directive, that the handlers can access. +// +// [ JDG May 9, 2007 ] +// [ JDG May 13, 2015 ] spirit X3 +// [ JDG Feb 19, 2018 ] Error handling for spirit X3 +// +// I would like to thank Rainbowverse, llc (https://primeorbial.com/) +// for sponsoring this work and donating it to the community. +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/ast/position_tagged.hpp> +#include <boost/spirit/home/x3/support/utility/error_reporting.hpp> +#include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/fusion/include/io.hpp> + +#include <iostream> +#include <string> + +namespace client { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // Our AST (employee and person structs) + /////////////////////////////////////////////////////////////////////////// + namespace x3 = boost::spirit::x3; + + struct person : x3::position_tagged + { + person( + std::string const& first_name = "" + , std::string const& last_name = "" + ) + : first_name(first_name) + , last_name(last_name) + {} + + std::string first_name, last_name; + }; + + struct employee : x3::position_tagged + { + int age; + person who; + double salary; + }; + + using boost::fusion::operator<<; +}} + +// We need to tell fusion about our employee struct +// to make it a first-class fusion citizen. This has to +// be in global scope. + +BOOST_FUSION_ADAPT_STRUCT(client::ast::person, + first_name, last_name +) + +BOOST_FUSION_ADAPT_STRUCT(client::ast::employee, + age, who, salary +) + +namespace client +{ + namespace parser + { + namespace x3 = boost::spirit::x3; + namespace ascii = boost::spirit::x3::ascii; + + /////////////////////////////////////////////////////////////////////// + // Our error handler + /////////////////////////////////////////////////////////////////////// + struct error_handler + { + template <typename Iterator, typename Exception, typename Context> + x3::error_handler_result on_error( + Iterator& first, Iterator const& last + , Exception const& x, Context const& context) + { + auto& error_handler = x3::get<x3::error_handler_tag>(context).get(); + std::string message = "Error! Expecting: " + x.which() + " here:"; + error_handler(x.where(), message); + return x3::error_handler_result::fail; + } + }; + + /////////////////////////////////////////////////////////////////////// + // Our employee parser + /////////////////////////////////////////////////////////////////////// + + using x3::int_; + using x3::double_; + using x3::lexeme; + using ascii::char_; + + struct quoted_string_class; + struct person_class; + struct employee_class; + + x3::rule<quoted_string_class, std::string> const quoted_string = "quoted_string"; + x3::rule<person_class, ast::person> const person = "person"; + x3::rule<employee_class, ast::employee> const employee = "employee"; + + auto const quoted_string_def = lexeme['"' >> +(char_ - '"') >> '"']; + auto const person_def = quoted_string > ',' > quoted_string; + + auto const employee_def = + '{' + > int_ > ',' + > person > ',' + > double_ + > '}' + ; + + auto const employees = employee >> *(',' >> employee); + + BOOST_SPIRIT_DEFINE(quoted_string, person, employee); + + struct quoted_string_class {}; + struct person_class : x3::annotate_on_success {}; + struct employee_class : error_handler, x3::annotate_on_success {}; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Our main parse entry point +/////////////////////////////////////////////////////////////////////////////// + +void parse(std::string const& input) +{ + using boost::spirit::x3::ascii::space; + typedef std::string::const_iterator iterator_type; + + std::vector<client::ast::employee> ast; + iterator_type iter = input.begin(); + iterator_type const end = input.end(); + + using boost::spirit::x3::with; + using boost::spirit::x3::error_handler_tag; + using error_handler_type = boost::spirit::x3::error_handler<iterator_type>; + + // Our error handler + error_handler_type error_handler(iter, end, std::cerr); + + // Our parser + using client::parser::employees; + auto const parser = + // we pass our error handler to the parser so we can access + // it later in our on_error and on_sucess handlers + with<error_handler_tag>(std::ref(error_handler)) + [ + employees + ]; + + bool r = phrase_parse(iter, end, parser, space, ast); + + if (r && iter == end) + { + std::cout << boost::fusion::tuple_open('['); + std::cout << boost::fusion::tuple_close(']'); + std::cout << boost::fusion::tuple_delimiter(", "); + + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + + for (auto const& emp : ast) + { + std::cout << "got: " << emp << std::endl; + } + std::cout << "\n-------------------------\n"; + + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + ast.clear(); + } +} + +// Good sample: + +std::string good_input = R"( +{ + 23, + "Amanda", + "Stefanski", + 1000.99 +}, +{ + 35, + "Angie", + "Chilcote", + 2000.99 +}, +{ + 43, + "Dannie", + "Dillinger", + 3000.99 +}, +{ + 22, + "Dorene", + "Dole", + 2500.99 +}, +{ + 38, + "Rossana", + "Rafferty", + 5000.99 +} +)"; + +// Input sample with error: + +std::string bad_input = R"( +{ + 23, + "Amanda", + "Stefanski", + 1000.99 +}, +{ + 35, + "Angie", + "Chilcote", + 2000.99 +}, +{ + 43, + 'I am not a person!' <--- this should be a person + 3000.99 +}, +{ + 22, + "Dorene", + "Dole", + 2500.99 +}, +{ + 38, + "Rossana", + "Rafferty", + 5000.99 +} +)"; + +int +main() +{ + // Good input + parse(good_input); + + // Bad input + std::cout << "Now we have some errors" << std::endl; + parse(bad_input); + return 0; +} diff --git a/src/boost/libs/spirit/example/x3/minimal/ast.hpp b/src/boost/libs/spirit/example/x3/minimal/ast.hpp new file mode 100644 index 00000000..b66bb072 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/minimal/ast.hpp @@ -0,0 +1,31 @@ +/*============================================================================= + Copyright (c) 2002-2018 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_X3_MINIMAL_AST_HPP) +#define BOOST_SPIRIT_X3_MINIMAL_AST_HPP + +#include <boost/fusion/include/io.hpp> + +#include <iostream> +#include <string> + +namespace client { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // Our employee AST struct + /////////////////////////////////////////////////////////////////////////// + struct employee + { + int age; + std::string forename; + std::string surname; + double salary; + }; + + using boost::fusion::operator<<; +}} + +#endif
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/x3/minimal/ast_adapted.hpp b/src/boost/libs/spirit/example/x3/minimal/ast_adapted.hpp new file mode 100644 index 00000000..9d6c6fe9 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/minimal/ast_adapted.hpp @@ -0,0 +1,21 @@ +/*============================================================================= + Copyright (c) 2002-2018 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_X3_MINIMAL_AST_ADAPTED_HPP) +#define BOOST_SPIRIT_X3_MINIMAL_AST_ADAPTED_HPP + +#include <boost/fusion/include/adapt_struct.hpp> +#include "ast.hpp" + +// We need to tell fusion about our employee struct +// to make it a first-class fusion citizen. This has to +// be in global scope. + +BOOST_FUSION_ADAPT_STRUCT(client::ast::employee, + age, forename, surname, salary +) + +#endif diff --git a/src/boost/libs/spirit/example/x3/minimal/config.hpp b/src/boost/libs/spirit/example/x3/minimal/config.hpp new file mode 100644 index 00000000..3787346a --- /dev/null +++ b/src/boost/libs/spirit/example/x3/minimal/config.hpp @@ -0,0 +1,20 @@ +/*============================================================================= + Copyright (c) 2001-2018 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_X3_MINIMAL_CONFIG_HPP) +#define BOOST_SPIRIT_X3_MINIMAL_CONFIG_HPP + +#include <boost/spirit/home/x3.hpp> + +namespace client { namespace parser +{ + namespace x3 = boost::spirit::x3; + + using iterator_type = std::string::const_iterator; + using context_type = x3::phrase_parse_context<x3::ascii::space_type>::type; +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/minimal/employee.cpp b/src/boost/libs/spirit/example/x3/minimal/employee.cpp new file mode 100644 index 00000000..8b2fa6c1 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/minimal/employee.cpp @@ -0,0 +1,13 @@ +/*============================================================================= + Copyright (c) 2001-2018 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 "employee_def.hpp" +#include "config.hpp" + +namespace client { namespace parser +{ + BOOST_SPIRIT_INSTANTIATE(employee_type, iterator_type, context_type); +}} diff --git a/src/boost/libs/spirit/example/x3/minimal/employee.hpp b/src/boost/libs/spirit/example/x3/minimal/employee.hpp new file mode 100644 index 00000000..a4475ec2 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/minimal/employee.hpp @@ -0,0 +1,30 @@ +/*============================================================================= + Copyright (c) 2002-2018 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_X3_MINIMAL_EMPLOYEE_HPP) +#define BOOST_SPIRIT_X3_MINIMAL_EMPLOYEE_HPP + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> + +#include "ast.hpp" + +namespace client +{ + /////////////////////////////////////////////////////////////////////////////// + // Our employee parser declaration + /////////////////////////////////////////////////////////////////////////////// + namespace parser + { + namespace x3 = boost::spirit::x3; + using employee_type = x3::rule<class employee, ast::employee>; + BOOST_SPIRIT_DECLARE(employee_type); + } + + parser::employee_type employee(); +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/minimal/employee_def.hpp b/src/boost/libs/spirit/example/x3/minimal/employee_def.hpp new file mode 100644 index 00000000..227b2f7f --- /dev/null +++ b/src/boost/libs/spirit/example/x3/minimal/employee_def.hpp @@ -0,0 +1,56 @@ +/*============================================================================= + Copyright (c) 2002-2018 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_X3_MINIMAL_EMPLOYEE_DEF_HPP) +#define BOOST_SPIRIT_X3_MINIMAL_EMPLOYEE_DEF_HPP + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> + +#include "ast.hpp" +#include "ast_adapted.hpp" +#include "employee.hpp" + +namespace client +{ + /////////////////////////////////////////////////////////////////////////////// + // Our employee parser definition + /////////////////////////////////////////////////////////////////////////////// + namespace parser + { + namespace x3 = boost::spirit::x3; + namespace ascii = boost::spirit::x3::ascii; + + using x3::int_; + using x3::lit; + using x3::double_; + using x3::lexeme; + using ascii::char_; + + x3::rule<class employee, ast::employee> const employee = "employee"; + + auto const quoted_string = lexeme['"' >> +(char_ - '"') >> '"']; + + auto const employee_def = + lit("employee") + >> '{' + >> int_ >> ',' + >> quoted_string >> ',' + >> quoted_string >> ',' + >> double_ + >> '}' + ; + + BOOST_SPIRIT_DEFINE(employee); + } + + parser::employee_type employee() + { + return parser::employee; + } +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/minimal/main.cpp b/src/boost/libs/spirit/example/x3/minimal/main.cpp new file mode 100644 index 00000000..aeb3bc51 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/minimal/main.cpp @@ -0,0 +1,79 @@ +/*============================================================================= + Copyright (c) 2002-2018 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 is the same employee parser (see employee.cpp) but structured to +// allow separate compilation of the actual parser in its own definition +// file (employee_def.hpp) and cpp file (employee.cpp). This main cpp file +// sees only the header file (employee.hpp). This is a good example on how +// parsers are structured in a C++ application. +// +// [ JDG May 9, 2007 ] +// [ JDG May 13, 2015 ] spirit X3 +// [ JDG Feb 20, 2018 ] Minimal "best practice" example +// +// I would like to thank Rainbowverse, llc (https://primeorbial.com/) +// for sponsoring this work and donating it to the community. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "ast.hpp" +#include "ast_adapted.hpp" +#include "employee.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tAn employee parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout + << "Give me an employee of the form :" + << "employee{age, \"forename\", \"surname\", salary } \n"; + std::cout << "Type [q or Q] to quit\n\n"; + + using boost::spirit::x3::ascii::space; + using iterator_type = std::string::const_iterator; + using client::employee; + + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + client::ast::employee emp; + iterator_type iter = str.begin(); + iterator_type const end = str.end(); + bool r = phrase_parse(iter, end, employee(), space, emp); + + if (r && iter == end) + { + std::cout << boost::fusion::tuple_open('['); + std::cout << boost::fusion::tuple_close(']'); + std::cout << boost::fusion::tuple_delimiter(", "); + + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "got: " << emp << std::endl; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/src/boost/libs/spirit/example/x3/num_list/num_list1.cpp b/src/boost/libs/spirit/example/x3/num_list/num_list1.cpp new file mode 100644 index 00000000..34371e67 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/num_list/num_list1.cpp @@ -0,0 +1,87 @@ +/*============================================================================= + Copyright (c) 2002-2015 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 demontrates a parser for a comma separated list of numbers. +// No semantic actions. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// [ JDG May 12, 2015 ] spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> + +#include <iostream> +#include <string> +#include <vector> + +namespace client +{ + namespace x3 = boost::spirit::x3; + namespace ascii = boost::spirit::x3::ascii; + + /////////////////////////////////////////////////////////////////////////// + // Our number list parser + /////////////////////////////////////////////////////////////////////////// + template <typename Iterator> + bool parse_numbers(Iterator first, Iterator last) + { + using x3::double_; + using x3::phrase_parse; + using ascii::space; + + bool r = phrase_parse( + first, // Start Iterator + last, // End Iterator + double_ >> *(',' >> double_), // The Parser + space // The Skip-Parser + ); + if (first != last) // fail if we did not get a full match + return false; + return r; + } +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser 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; + + if (client::parse_numbers(str.begin(), str.end())) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/src/boost/libs/spirit/example/x3/num_list/num_list2.cpp b/src/boost/libs/spirit/example/x3/num_list/num_list2.cpp new file mode 100644 index 00000000..67a75053 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/num_list/num_list2.cpp @@ -0,0 +1,101 @@ +/*============================================================================= + Copyright (c) 2002-2015 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 demontrates a parser for a comma separated list of numbers. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// [ JDG May 12, 2015 ] spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> + +#include <iostream> +#include <string> +#include <vector> + +namespace client +{ + namespace x3 = boost::spirit::x3; + namespace ascii = boost::spirit::x3::ascii; + + /////////////////////////////////////////////////////////////////////////// + // Our number list compiler + /////////////////////////////////////////////////////////////////////////// + template <typename Iterator> + bool parse_numbers(Iterator first, Iterator last, std::vector<double>& v) + { + using x3::double_; + using x3::phrase_parse; + using x3::_attr; + using ascii::space; + + auto push_back = [&](auto& ctx){ v.push_back(_attr(ctx)); }; + + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_[push_back] >> *(',' >> double_[push_back]) + ) + , + // End grammar + + space); + + if (first != last) // fail if we did not get a full match + return false; + return r; + } +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers will be inserted in a vector 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; + if (client::parse_numbers(str.begin(), str.end(), v)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + for (std::vector<double>::size_type i = 0; i < v.size(); ++i) + std::cout << i << ": " << v[i] << std::endl; + + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/src/boost/libs/spirit/example/x3/num_list/num_list3.cpp b/src/boost/libs/spirit/example/x3/num_list/num_list3.cpp new file mode 100644 index 00000000..53a275bb --- /dev/null +++ b/src/boost/libs/spirit/example/x3/num_list/num_list3.cpp @@ -0,0 +1,101 @@ +/*============================================================================= + Copyright (c) 2002-2015 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 demontrates a parser for a comma separated list of numbers. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// [ JDG May 12, 2015 ] spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> + +#include <iostream> +#include <string> +#include <vector> + +namespace client +{ + namespace x3 = boost::spirit::x3; + namespace ascii = boost::spirit::x3::ascii; + + /////////////////////////////////////////////////////////////////////////// + // Our number list compiler + /////////////////////////////////////////////////////////////////////////// + template <typename Iterator> + bool parse_numbers(Iterator first, Iterator last, std::vector<double>& v) + { + using x3::double_; + using x3::phrase_parse; + using x3::_attr; + using ascii::space; + + auto push_back = [&](auto& ctx){ v.push_back(_attr(ctx)); }; + + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_[push_back] % ',' + ) + , + // End grammar + + space); + + if (first != last) // fail if we did not get a full match + return false; + return r; + } +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers will be inserted in a vector 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; + if (client::parse_numbers(str.begin(), str.end(), v)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + for (std::vector<double>::size_type i = 0; i < v.size(); ++i) + std::cout << i << ": " << v[i] << std::endl; + + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/src/boost/libs/spirit/example/x3/num_list/num_list4.cpp b/src/boost/libs/spirit/example/x3/num_list/num_list4.cpp new file mode 100644 index 00000000..1c88b284 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/num_list/num_list4.cpp @@ -0,0 +1,103 @@ +/*============================================================================= + Copyright (c) 2002-2015 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 demontrates a parser for a comma separated list of numbers. +// This time, the numbers are automatically collected into the attribute by +// the parser itself using the full power of attribute grammars. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// [ JDG May 12, 2015 ] spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> + +#include <iostream> +#include <string> +#include <vector> + +namespace client +{ + namespace x3 = boost::spirit::x3; + namespace ascii = boost::spirit::x3::ascii; + + /////////////////////////////////////////////////////////////////////////// + // Our number list compiler + /////////////////////////////////////////////////////////////////////////// + //[tutorial_numlist4 + template <typename Iterator> + bool parse_numbers(Iterator first, Iterator last, std::vector<double>& v) + { + using x3::double_; + using x3::phrase_parse; + using x3::_attr; + using ascii::space; + + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_ % ',' + ) + , + // End grammar + + space, v); + + if (first != last) // fail if we did not get a full match + return false; + return r; + } + //] +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers will be inserted in a vector 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; + if (client::parse_numbers(str.begin(), str.end(), v)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + for (std::vector<double>::size_type i = 0; i < v.size(); ++i) + std::cout << i << ": " << v[i] << std::endl; + + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_examples/a.rexpr b/src/boost/libs/spirit/example/x3/rexpr/rexpr_examples/a.rexpr new file mode 100644 index 00000000..880f5bac --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_examples/a.rexpr @@ -0,0 +1,8 @@ +{ + "color" = "blue" + "size" = "29 cm." + "position" = { + "x" = "123" + "y" = "456" + } +}
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/Jamfile b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/Jamfile new file mode 100644 index 00000000..5723b1ed --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/Jamfile @@ -0,0 +1,20 @@ +# +# Copyright (C) 2015 Michael Caisse, ciere.com +# +# 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-x3-example-rexpr + : requirements + <c++-template-depth>512 + <use>/boost//headers + <define>BOOST_SPIRIT_X3_NO_FILESYSTEM + <include>. + ; + +lib rexpr + : [ glob src/*.cpp ] + ; + +build-project test ; diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/ast.hpp b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/ast.hpp new file mode 100644 index 00000000..c7635b12 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/ast.hpp @@ -0,0 +1,42 @@ +/*============================================================================= + Copyright (c) 2001-2015 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_X3_REPR_AST_HPP) +#define BOOST_SPIRIT_X3_REPR_AST_HPP + +#include <boost/spirit/home/x3/support/ast/position_tagged.hpp> +#include <boost/spirit/home/x3/support/ast/variant.hpp> + +#include <map> + +namespace rexpr { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // The AST + /////////////////////////////////////////////////////////////////////////// + namespace x3 = boost::spirit::x3; + + struct rexpr; + + struct rexpr_value : x3::variant< + std::string + , x3::forward_ast<rexpr> + > + { + using base_type::base_type; + using base_type::operator=; + }; + + typedef std::map<std::string, rexpr_value> rexpr_map; + typedef std::pair<std::string, rexpr_value> rexpr_key_value; + + struct rexpr : x3::position_tagged + { + rexpr_map entries; + }; +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/ast_adapted.hpp b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/ast_adapted.hpp new file mode 100644 index 00000000..d325cbbd --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/ast_adapted.hpp @@ -0,0 +1,22 @@ +/*============================================================================= + Copyright (c) 2001-2015 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_X3_REPR_AST_ADAPTED_HPP) +#define BOOST_SPIRIT_X3_REPR_AST_ADAPTED_HPP + +#include "ast.hpp" + +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/fusion/include/std_pair.hpp> + +// We need to tell fusion about our rexpr and rexpr_key_value +// to make them a first-class fusion citizens + +BOOST_FUSION_ADAPT_STRUCT(rexpr::ast::rexpr, + entries +) + +#endif diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/config.hpp b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/config.hpp new file mode 100644 index 00000000..12a25def --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/config.hpp @@ -0,0 +1,35 @@ +/*============================================================================= + Copyright (c) 2001-2015 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_X3_REPR_CONFIG_HPP) +#define BOOST_SPIRIT_X3_REPR_CONFIG_HPP + +#include "error_handler.hpp" + +#include <boost/spirit/home/x3.hpp> + +namespace rexpr { namespace parser +{ + // Our Iterator Type + typedef std::string::const_iterator iterator_type; + + // The Phrase Parse Context + typedef + x3::phrase_parse_context<x3::ascii::space_type>::type + phrase_context_type; + + // Our Error Handler + typedef error_handler<iterator_type> error_handler_type; + + // Combined Error Handler and Phrase Parse Context + typedef x3::context< + error_handler_tag + , std::reference_wrapper<error_handler_type> + , phrase_context_type> + context_type; +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/error_handler.hpp b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/error_handler.hpp new file mode 100644 index 00000000..a173d492 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/error_handler.hpp @@ -0,0 +1,72 @@ +/*============================================================================= + Copyright (c) 2001-2015 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_X3_REPR_ERROR_HANDLER_HPP) +#define BOOST_SPIRIT_X3_REPR_ERROR_HANDLER_HPP + +#include "rexpr.hpp" + +#include <boost/spirit/home/x3/support/ast/position_tagged.hpp> +#include <boost/spirit/home/x3/support/utility/error_reporting.hpp> + +#include <map> + +namespace rexpr { namespace parser +{ + namespace x3 = boost::spirit::x3; + + //////////////////////////////////////////////////////////////////////////// + // Our error handler + //////////////////////////////////////////////////////////////////////////// + // X3 Error Handler Utility + template <typename Iterator> + using error_handler = x3::error_handler<Iterator>; + + // tag used to get our error handler from the context + using error_handler_tag = x3::error_handler_tag; + + struct error_handler_base + { + error_handler_base(); + + template <typename Iterator, typename Exception, typename Context> + x3::error_handler_result on_error( + Iterator& first, Iterator const& last + , Exception const& x, Context const& context); + + std::map<std::string, std::string> id_map; + }; + + //////////////////////////////////////////////////////////////////////////// + // Implementation + //////////////////////////////////////////////////////////////////////////// + + inline error_handler_base::error_handler_base() + { + id_map["rexpr"] = "RExpression"; + id_map["rexpr_value"] = "Value"; + id_map["rexpr_key_value"] = "Key value pair"; + } + + template <typename Iterator, typename Exception, typename Context> + inline x3::error_handler_result + error_handler_base::on_error( + Iterator& first, Iterator const& last + , Exception const& x, Context const& context) + { + std::string which = x.which(); + auto iter = id_map.find(which); + if (iter != id_map.end()) + which = iter->second; + + std::string message = "Error! Expecting: " + which + " here:"; + auto& error_handler = x3::get<error_handler_tag>(context).get(); + error_handler(x.where(), message); + return x3::error_handler_result::fail; + } +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/printer.hpp b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/printer.hpp new file mode 100644 index 00000000..a1c307c6 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/printer.hpp @@ -0,0 +1,57 @@ +/*============================================================================= + Copyright (c) 2001-2015 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_X3_REPR_PRINTER_HPP) +#define BOOST_SPIRIT_X3_REPR_PRINTER_HPP + +#include "ast.hpp" + +#include <ostream> + +namespace rexpr { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // Print out the rexpr tree + /////////////////////////////////////////////////////////////////////////// + int const tabsize = 4; + + struct rexpr_printer + { + typedef void result_type; + + rexpr_printer(std::ostream& out, int indent = 0) + : out(out), indent(indent) {} + + void operator()(rexpr const& ast) const + { + out << '{' << std::endl; + for (auto const& entry : ast.entries) + { + tab(indent+tabsize); + out << '"' << entry.first << "\" = "; + boost::apply_visitor(rexpr_printer(out, indent+tabsize), entry.second); + } + tab(indent); + out << '}' << std::endl; + } + + void operator()(std::string const& text) const + { + out << '"' << text << '"' << std::endl; + } + + void tab(int spaces) const + { + for (int i = 0; i < spaces; ++i) + out << ' '; + } + + std::ostream& out; + int indent; + }; +}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/rexpr.hpp b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/rexpr.hpp new file mode 100644 index 00000000..f3979f9d --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/rexpr.hpp @@ -0,0 +1,33 @@ +/*============================================================================= + Copyright (c) 2001-2015 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_X3_REXPR_HPP) +#define BOOST_SPIRIT_X3_REXPR_HPP + +#include "ast.hpp" + +#include <boost/spirit/home/x3.hpp> + +namespace rexpr +{ + namespace x3 = boost::spirit::x3; + + /////////////////////////////////////////////////////////////////////////// + // rexpr public interface + /////////////////////////////////////////////////////////////////////////// + namespace parser + { + struct rexpr_class; + typedef + x3::rule<rexpr_class, ast::rexpr> + rexpr_type; + BOOST_SPIRIT_DECLARE(rexpr_type); + } + + parser::rexpr_type const& rexpr(); +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/rexpr_def.hpp b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/rexpr_def.hpp new file mode 100644 index 00000000..3875031a --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/rexpr/rexpr_def.hpp @@ -0,0 +1,94 @@ +/*============================================================================= + Copyright (c) 2001-2015 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_X3_REPR_REXPR_DEF_HPP) +#define BOOST_SPIRIT_X3_REPR_REXPR_DEF_HPP + +#include "ast.hpp" +#include "ast_adapted.hpp" +#include "error_handler.hpp" +#include "rexpr.hpp" + +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp> + +namespace rexpr { namespace parser +{ + namespace x3 = boost::spirit::x3; + namespace ascii = boost::spirit::x3::ascii; + + using x3::lit; + using x3::lexeme; + + using ascii::char_; + using ascii::string; + + /////////////////////////////////////////////////////////////////////////// + // Rule IDs + /////////////////////////////////////////////////////////////////////////// + + struct rexpr_value_class; + struct rexpr_key_value_class; + struct rexpr_inner_class; + + /////////////////////////////////////////////////////////////////////////// + // Rules + /////////////////////////////////////////////////////////////////////////// + + x3::rule<rexpr_value_class, ast::rexpr_value> const + rexpr_value = "rexpr_value"; + + x3::rule<rexpr_key_value_class, ast::rexpr_key_value> const + rexpr_key_value = "rexpr_key_value"; + + x3::rule<rexpr_inner_class, ast::rexpr> const + rexpr_inner = "rexpr"; + + rexpr_type const rexpr = "rexpr"; + + /////////////////////////////////////////////////////////////////////////// + // Grammar + /////////////////////////////////////////////////////////////////////////// + + auto const quoted_string = + lexeme['"' >> *(char_ - '"') >> '"']; + + auto const rexpr_value_def = + quoted_string | rexpr_inner; + + auto const rexpr_key_value_def = + quoted_string > '=' > rexpr_value; + + auto const rexpr_inner_def = + '{' > *rexpr_key_value > '}'; + + auto const rexpr_def = rexpr_inner_def; + + BOOST_SPIRIT_DEFINE(rexpr_value, rexpr, rexpr_inner, rexpr_key_value); + + /////////////////////////////////////////////////////////////////////////// + // Annotation and Error handling + /////////////////////////////////////////////////////////////////////////// + + // We want these to be annotated with the iterator position. + struct rexpr_value_class : x3::annotate_on_success {}; + struct rexpr_key_value_class : x3::annotate_on_success {}; + struct rexpr_inner_class : x3::annotate_on_success {}; + + // We want error-handling only for the start (outermost) rexpr + // rexpr is the same as rexpr_inner but without error-handling (see error_handler.hpp) + struct rexpr_class : x3::annotate_on_success, error_handler_base {}; +}} + +namespace rexpr +{ + parser::rexpr_type const& rexpr() + { + return parser::rexpr; + } +} + +#endif diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/src/rexpr.cpp b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/src/rexpr.cpp new file mode 100644 index 00000000..14b84c60 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/src/rexpr.cpp @@ -0,0 +1,14 @@ +/*============================================================================= + Copyright (c) 2001-2015 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 "../rexpr/rexpr_def.hpp" +#include "../rexpr/config.hpp" + +namespace rexpr { namespace parser +{ + BOOST_SPIRIT_INSTANTIATE( + rexpr_type, iterator_type, context_type); +}} diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/Jamfile b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/Jamfile new file mode 100644 index 00000000..4952e281 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/Jamfile @@ -0,0 +1,23 @@ +# +# Copyright (C) 2015 Michael Caisse, ciere.com +# +# 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) +# + +import testing ; + +project + : requirements + <source>..//rexpr + <source>/boost//regex + <source>/boost//filesystem + <c++-template-depth>300 + ; + +path-constant TEST_FILES : test_files ; + +unit-test parse_rexpr_test + : parse_rexpr_test.cpp + : <testing.arg>"$(TEST_FILES)" + ; diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/parse_rexpr_test.cpp b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/parse_rexpr_test.cpp new file mode 100644 index 00000000..bdbacf2d --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/parse_rexpr_test.cpp @@ -0,0 +1,86 @@ +/*============================================================================= + Copyright (c) 2001-2015 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 <iterator> +#include <algorithm> +#include <sstream> + +#include "../rexpr/ast.hpp" +#include "../rexpr/rexpr.hpp" +#include "../rexpr/error_handler.hpp" +#include "../rexpr/config.hpp" +#include "../rexpr/printer.hpp" + +#include "testing.hpp" + +namespace fs = boost::filesystem; +namespace testing = boost::spirit::x3::testing; + +auto parse = [](std::string const& source, fs::path input_path)-> std::string +{ + std::stringstream out; + + using rexpr::parser::iterator_type; + iterator_type iter(source.begin()); + iterator_type const end(source.end()); + + // Our AST + rexpr::ast::rexpr ast; + + // Our error handler + using boost::spirit::x3::with; + using rexpr::parser::error_handler_type; + using rexpr::parser::error_handler_tag; + error_handler_type error_handler(iter, end, out, input_path.string()); // Our error handler + + // Our parser + auto const parser = + // we pass our error handler to the parser so we can access + // it later on in our on_error and on_sucess handlers + with<error_handler_tag>(std::ref(error_handler)) + [ + rexpr::rexpr() + ]; + + // Go forth and parse! + using boost::spirit::x3::ascii::space; + bool success = phrase_parse(iter, end, parser, space, ast); + + if (success) + { + if (iter != end) + error_handler(iter, "Error! Expecting end of input here: "); + else + rexpr::ast::rexpr_printer{out}(ast); + } + + return out.str(); +}; + +int num_files_tested = 0; +auto compare = [](fs::path input_path, fs::path expect_path) +{ + testing::compare(input_path, expect_path, parse); + ++num_files_tested; +}; + +int main(int argc, char* argv[]) +{ + if (argc < 2) + { + std::cout << "usage: " << fs::path(argv[0]).filename() << " path/to/test/files" << std::endl; + return -1; + } + + std::cout << "===================================================================================================" << std::endl; + std::cout << "Testing: " << fs::absolute(fs::path(argv[1])) << std::endl; + int r = testing::for_each_file(fs::path(argv[1]), compare); + if (r == 0) + std::cout << num_files_tested << " files tested." << std::endl; + std::cout << "===================================================================================================" << std::endl; + return r; +} diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/a.expect b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/a.expect new file mode 100644 index 00000000..635c145b --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/a.expect @@ -0,0 +1,8 @@ +{ + "color" = "blue" + "position" = { + "x" = "123" + "y" = "456" + } + "size" = "29 cm." +} diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/a.input b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/a.input new file mode 100644 index 00000000..880f5bac --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/a.input @@ -0,0 +1,8 @@ +{ + "color" = "blue" + "size" = "29 cm." + "position" = { + "x" = "123" + "y" = "456" + } +}
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/b.expect b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/b.expect new file mode 100644 index 00000000..3f485184 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/b.expect @@ -0,0 +1,4 @@ +In file <%.*?b.input%>, line 4: +Error! Expecting: Value here: + "position" = $ +_________________^_
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/b.input b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/b.input new file mode 100644 index 00000000..6f0743b1 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/b.input @@ -0,0 +1,8 @@ +{ + "color" = "blue" + "size" = "29 cm." + "position" = $ + "x" = "123" + "y" = "456" + } +}
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/c.expect b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/c.expect new file mode 100644 index 00000000..bc7efcd0 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/c.expect @@ -0,0 +1,4 @@ +In file <%.*?c.input%>, line 5: +Error! Expecting: '}' here: + banana +________^_
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/c.input b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/c.input new file mode 100644 index 00000000..6eaa1259 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/c.input @@ -0,0 +1,8 @@ +{ + "color" = "blue" + "size" = "29 cm." + "position" = { + banana + "y" = "456" + } +}
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/d.expect b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/d.expect new file mode 100644 index 00000000..5b376035 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/d.expect @@ -0,0 +1,4 @@ +In file <%.*?d.input%>, line 5: +Error! Expecting: '=' here: + "x" : "123" +____________^_ diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/d.input b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/d.input new file mode 100644 index 00000000..c8be6438 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/d.input @@ -0,0 +1,8 @@ +{ + "color" = "blue" + "size" = "29 cm." + "position" = { + "x" : "123" + "y" = "456" + } +}
\ No newline at end of file diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/e.expect b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/e.expect new file mode 100644 index 00000000..4fed2140 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/e.expect @@ -0,0 +1,5 @@ +In file <%.*?e.input%>, line 8: +Error! Expecting end of input here: +}; +_^_ + diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/e.input b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/e.input new file mode 100644 index 00000000..1bd447f1 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/test_files/e.input @@ -0,0 +1,8 @@ +{ + "color" = "blue" + "size" = "29 cm." + "position" = { + "x" = "123" + "y" = "456" + } +}; diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/testing.hpp b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/testing.hpp new file mode 100644 index 00000000..eb99f692 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_full/test/testing.hpp @@ -0,0 +1,269 @@ +/*============================================================================= + Copyright (c) 2001-2015 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_X3_TEST_UTILITIES) +#define BOOST_SPIRIT_X3_TEST_UTILITIES + +#include <boost/regex.hpp> +#include <boost/filesystem.hpp> +#include <boost/filesystem/fstream.hpp> + +namespace boost { namespace spirit { namespace x3 { namespace testing +{ + namespace fs = boost::filesystem; + + //////////////////////////////////////////////////////////////////////////// + // compare + // + // Compares the contents of in with the template tem. The template + // may include embedded regular expressions marked up within re_prefix + // and re_suffix tags. For example, given the default RE markup, this + // template <%[0-9]+%> will match any integer in in. The function + // will return the first non-matching position. The flag full_match + // indicates a full match. It is possible for returned pos to be + // at the end of in (in.end()) while still returning full_match == + // false. In that case, we have a partial match. + //////////////////////////////////////////////////////////////////////////// + + template <typename Iterator> + struct compare_result + { + compare_result( + Iterator pos + , bool full_match + ) : pos(pos), full_match(full_match) {} + + Iterator pos; + bool full_match; + }; + + template <typename Range> + compare_result<typename Range::const_iterator> + compare( + Range const& in + , Range const& tem + , char const* re_prefix = "<%" + , char const* re_suffix = "%>" + ); + + //////////////////////////////////////////////////////////////////////////// + // compare + // + // 1) Call f, given the contents of input_path loaded in a string. + // The result of calling f is the output string. + // 2) Compare the result of calling f with expected template + // file (expect_path) using the low-level compare utility + // abive + //////////////////////////////////////////////////////////////////////////// + template <typename F> + bool compare( + fs::path input_path, fs::path expect_path + , F f + , char const* re_prefix = "<%" + , char const* re_suffix = "%>" + ); + + //////////////////////////////////////////////////////////////////////////// + // for_each_file + // + // For each *.input and *.expect file in a given directory, + // call the function f, passing in the *.input and *.expect paths. + //////////////////////////////////////////////////////////////////////////// + template <typename F> + int for_each_file(fs::path p, F f); + + //////////////////////////////////////////////////////////////////////////// + // load_file + // + // Load file into a string. + //////////////////////////////////////////////////////////////////////////// + std::string load(fs::path p); + + //////////////////////////////////////////////////////////////////////////// + // Implementation + //////////////////////////////////////////////////////////////////////////// + + template <typename Iterator> + inline bool is_regex( + Iterator& first + , Iterator last + , std::string& re + , char const* re_prefix + , char const* re_suffix + ) + { + boost::regex e(re_prefix + std::string("(.*?)") + re_suffix); + boost::match_results<Iterator> what; + if (boost::regex_search( + first, last, what, e + , boost::match_default | boost::match_continuous)) + { + re = what[1].str(); + first = what[0].second; + return true; + } + return false; + } + + template <typename Range> + inline compare_result<typename Range::const_iterator> + compare( + Range const& in + , Range const& tem + , char const* re_prefix + , char const* re_suffix + ) + { + typedef typename Range::const_iterator iter_t; + typedef compare_result<iter_t> compare_result_t; + + iter_t in_first = in.begin(); + iter_t in_last = in.end(); + iter_t tem_first = tem.begin(); + iter_t tem_last = tem.end(); + std::string re; + + while (in_first != in_last && tem_first != tem_last) + { + if (is_regex(tem_first, tem_last, re, re_prefix, re_suffix)) + { + boost::match_results<iter_t> what; + boost::regex e(re); + if (!boost::regex_search( + in_first, in_last, what, e + , boost::match_default | boost::match_continuous)) + { + // RE mismatch: exit now. + return compare_result_t(in_first, false); + } + else + { + // RE match: gobble the matching string. + in_first = what[0].second; + } + } + else + { + // Char by char comparison. Exit if we have a mismatch. + if (*in_first++ != *tem_first++) + return compare_result_t(in_first, false); + } + } + + // Ignore trailing spaces in template + bool has_trailing_nonspaces = false; + while (tem_first != tem_last) + { + if (!std::isspace(*tem_first++)) + { + has_trailing_nonspaces = true; + break; + } + } + while (in_first != in_last) + { + if (!std::isspace(*in_first++)) + { + has_trailing_nonspaces = true; + break; + } + } + // return a full match only if the template is fully matched and if there + // are no more characters to match in the source + return compare_result_t(in_first, !has_trailing_nonspaces); + } + + template <typename F> + inline int for_each_file(fs::path p, F f) + { + try + { + if (fs::exists(p) && fs::is_directory(p)) + { + for (auto i = fs::directory_iterator(p); i != fs::directory_iterator(); ++i) + { + auto ext = fs::extension(i->path()); + if (ext == ".input") + { + auto input_path = i->path(); + auto expect_path = input_path; + expect_path.replace_extension(".expect"); + f(input_path, expect_path); + } + } + } + else + { + std::cerr << "Directory: " << fs::absolute(p) << " does not exist." << std::endl; + return 1; + } + } + + catch (const fs::filesystem_error& ex) + { + std::cerr << ex.what() << '\n'; + return 1; + } + return 0; + } + + inline std::string load(fs::path p) + { + boost::filesystem::ifstream file(p); + if (!file) + return ""; + std::string contents((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); + return contents; + } + + template <typename F> + inline bool compare( + fs::path input_path, fs::path expect_path + , F f + , char const* re_prefix + , char const* re_suffix + ) + { + std::string output = f(load(input_path), input_path); + std::string expected = load(expect_path); + + auto result = compare(output, expected, re_prefix, re_suffix); + if (!result.full_match) + { + std::cout << "=============================================" << std::endl; + std::cout << "==== Mismatch Found:" << std::endl; + int line = 1; + int col = 1; + for (auto i = output.begin(); i != result.pos; ++i) + { + if (*i == '\n') + { + line++; + col = 0; + } + ++col; + } + + std::cerr + << "==== File: " << expect_path + << ", Line: " << line + << ", Column: " << col + << std::endl; + std::cerr << "=============================================" << std::endl; + + // Print output + std::cerr << output; + std::cerr << "=============================================" << std::endl; + std::cerr << "==== End" << std::endl; + std::cerr << "=============================================" << std::endl; + return false; + } + return true; + } + +}}}} + +#endif diff --git a/src/boost/libs/spirit/example/x3/rexpr/rexpr_min/rexpr.cpp b/src/boost/libs/spirit/example/x3/rexpr/rexpr_min/rexpr.cpp new file mode 100644 index 00000000..f3a3556d --- /dev/null +++ b/src/boost/libs/spirit/example/x3/rexpr/rexpr_min/rexpr.cpp @@ -0,0 +1,215 @@ +/*============================================================================= + Copyright (c) 2001-2015 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 simple parser for X3 intended as a minimal starting point. +// 'rexpr' is a parser for a language resembling a minimal subset +// of json, but limited to a dictionary (composed of key=value pairs) +// where the value can itself be a string or a recursive dictionary. +// +// Example: +// +// { +// "color" = "blue" +// "size" = "29 cm." +// "position" = { +// "x" = "123" +// "y" = "456" +// } +// } +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> +#include <boost/spirit/home/x3/support/ast/variant.hpp> +#include <boost/fusion/include/adapt_struct.hpp> +#include <boost/fusion/include/std_pair.hpp> +#include <boost/fusion/include/io.hpp> + +#include <iostream> +#include <fstream> +#include <string> +#include <map> + +/////////////////////////////////////////////////////////////////////////////// +// Our AST +/////////////////////////////////////////////////////////////////////////////// +namespace client { namespace ast +{ + namespace fusion = boost::fusion; + namespace x3 = boost::spirit::x3; + + struct rexpr; + + struct rexpr_value : x3::variant< + std::string + , x3::forward_ast<rexpr> + > + { + using base_type::base_type; + using base_type::operator=; + }; + + typedef std::map<std::string, rexpr_value> rexpr_map; + typedef std::pair<std::string, rexpr_value> rexpr_key_value; + + struct rexpr + { + rexpr_map entries; + }; +}} + +// We need to tell fusion about our rexpr struct +// to make it a first-class fusion citizen +BOOST_FUSION_ADAPT_STRUCT(client::ast::rexpr, + entries +) + +/////////////////////////////////////////////////////////////////////////////// +// AST processing +/////////////////////////////////////////////////////////////////////////////// +namespace client { namespace ast +{ + /////////////////////////////////////////////////////////////////////////// + // Print out the rexpr tree + /////////////////////////////////////////////////////////////////////////// + int const tabsize = 4; + + struct rexpr_printer + { + typedef void result_type; + + rexpr_printer(int indent = 0) + : indent(indent) {} + + void operator()(rexpr const& ast) const + { + std::cout << '{' << std::endl; + for (auto const& entry : ast.entries) + { + tab(indent+tabsize); + std::cout << '"' << entry.first << "\" = "; + boost::apply_visitor(rexpr_printer(indent+tabsize), entry.second); + } + tab(indent); + std::cout << '}' << std::endl; + } + + void operator()(std::string const& text) const + { + std::cout << '"' << text << '"' << std::endl; + } + + void tab(int spaces) const + { + for (int i = 0; i < spaces; ++i) + std::cout << ' '; + } + + int indent; + }; +}} + +/////////////////////////////////////////////////////////////////////////////// +// Our rexpr grammar +/////////////////////////////////////////////////////////////////////////////// +namespace client { namespace parser +{ + namespace x3 = boost::spirit::x3; + namespace ascii = boost::spirit::x3::ascii; + + using x3::lit; + using x3::lexeme; + + using ascii::char_; + using ascii::string; + + x3::rule<class rexpr_value, ast::rexpr_value> + rexpr_value = "rexpr_value"; + + x3::rule<class rexpr, ast::rexpr> + rexpr = "rexpr"; + + x3::rule<class rexpr_key_value, ast::rexpr_key_value> + rexpr_key_value = "rexpr_key_value"; + + auto const quoted_string = + lexeme['"' >> *(char_ - '"') >> '"']; + + auto const rexpr_value_def = + quoted_string | rexpr; + + auto const rexpr_key_value_def = + quoted_string >> '=' >> rexpr_value; + + auto const rexpr_def = + '{' >> *rexpr_key_value >> '}'; + + BOOST_SPIRIT_DEFINE(rexpr_value, rexpr, rexpr_key_value); +}} + +/////////////////////////////////////////////////////////////////////////////// +// 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)); + + using client::parser::rexpr; // Our grammar + client::ast::rexpr ast; // Our tree + + using boost::spirit::x3::ascii::space; + std::string::const_iterator iter = storage.begin(); + std::string::const_iterator end = storage.end(); + bool r = phrase_parse(iter, end, rexpr, space, ast); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + client::ast::rexpr_printer printer; + printer(ast); + return 0; + } + else + { + std::string::const_iterator some = iter+30; + std::string context(iter, (some>end)?end:some); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << context << "...\"\n"; + std::cout << "-------------------------\n"; + return 1; + } +} diff --git a/src/boost/libs/spirit/example/x3/roman.cpp b/src/boost/libs/spirit/example/x3/roman.cpp new file mode 100644 index 00000000..7423c19c --- /dev/null +++ b/src/boost/libs/spirit/example/x3/roman.cpp @@ -0,0 +1,179 @@ +/*============================================================================= + Copyright (c) 2001-2015 Joel de Guzman + Copyright (c) 2015 Ahmed Charles + + 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 Roman Numerals Parser (demonstrating the symbol table). This is +// discussed in the "Symbols" chapter in the Spirit User's Guide. +// +// [ JDG August 22, 2002 ] spirit1 +// [ JDG March 13, 2007 ] spirit2 +// [ JDG May 13, 2015 ] spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> + +#include <iostream> +#include <string> + +namespace client +{ + namespace x3 = boost::spirit::x3; + namespace ascii = boost::spirit::x3::ascii; + + /////////////////////////////////////////////////////////////////////////////// + // Parse roman hundreds (100..900) numerals using the symbol table. + // Notice that the data associated with each slot is the parser's attribute + // (which is passed to attached semantic actions). + /////////////////////////////////////////////////////////////////////////////// + struct hundreds_ : x3::symbols<unsigned> + { + hundreds_() + { + add + ("C" , 100) + ("CC" , 200) + ("CCC" , 300) + ("CD" , 400) + ("D" , 500) + ("DC" , 600) + ("DCC" , 700) + ("DCCC" , 800) + ("CM" , 900) + ; + } + + } hundreds; + + /////////////////////////////////////////////////////////////////////////////// + // Parse roman tens (10..90) numerals using the symbol table. + /////////////////////////////////////////////////////////////////////////////// + struct tens_ : x3::symbols<unsigned> + { + tens_() + { + add + ("X" , 10) + ("XX" , 20) + ("XXX" , 30) + ("XL" , 40) + ("L" , 50) + ("LX" , 60) + ("LXX" , 70) + ("LXXX" , 80) + ("XC" , 90) + ; + } + + } tens; + + /////////////////////////////////////////////////////////////////////////////// + // Parse roman ones (1..9) numerals using the symbol table. + /////////////////////////////////////////////////////////////////////////////// + struct ones_ : x3::symbols<unsigned> + { + ones_() + { + add + ("I" , 1) + ("II" , 2) + ("III" , 3) + ("IV" , 4) + ("V" , 5) + ("VI" , 6) + ("VII" , 7) + ("VIII" , 8) + ("IX" , 9) + ; + } + + } ones; + + /////////////////////////////////////////////////////////////////////////////// + // roman (numerals) grammar + // + // Note the use of the || operator. The expression + // a || b reads match a or b and in sequence. Try + // defining the roman numerals grammar in YACC or + // PCCTS. Spirit rules! :-) + /////////////////////////////////////////////////////////////////////////////// + namespace parser + { + using x3::eps; + using x3::lit; + using x3::_val; + using x3::_attr; + using ascii::char_; + + auto set_zero = [](auto& ctx){ _val(ctx) = 0; }; + auto add1000 = [](auto& ctx){ _val(ctx) += 1000; }; + auto add = [](auto& ctx){ _val(ctx) += _attr(ctx); }; + + x3::rule<class roman, unsigned> const roman = "roman"; + + auto const roman_def = + eps [set_zero] + >> + ( + -(+lit('M') [add1000]) + >> -hundreds [add] + >> -tens [add] + >> -ones [add] + ) + ; + + BOOST_SPIRIT_DEFINE(roman); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tRoman Numerals Parser\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type a Roman Numeral ...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + using client::parser::roman; // Our parser + + std::string str; + unsigned result; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + iterator_type iter = str.begin(); + iterator_type const end = str.end(); + bool r = parse(iter, end, roman, 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; +} diff --git a/src/boost/libs/spirit/example/x3/sum.cpp b/src/boost/libs/spirit/example/x3/sum.cpp new file mode 100644 index 00000000..81315fb2 --- /dev/null +++ b/src/boost/libs/spirit/example/x3/sum.cpp @@ -0,0 +1,98 @@ +/*============================================================================= + Copyright (c) 2002-2015 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 parser for summing a comma-separated list of numbers using phoenix. +// +// [ JDG June 28, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// [ JDG May 12, 2015 ] spirit X3 +// +/////////////////////////////////////////////////////////////////////////////// + +#include <boost/config/warning_disable.hpp> +#include <boost/spirit/home/x3.hpp> +#include <iostream> +#include <string> + +namespace client +{ + namespace x3 = boost::spirit::x3; + namespace ascii = boost::spirit::x3::ascii; + + using x3::double_; + using ascii::space; + using x3::_attr; + + /////////////////////////////////////////////////////////////////////////// + // Our adder + /////////////////////////////////////////////////////////////////////////// + + template <typename Iterator> + bool adder(Iterator first, Iterator last, double& n) + { + auto assign = [&](auto& ctx){ n = _attr(ctx); }; + auto add = [&](auto& ctx){ n += _attr(ctx); }; + + bool r = x3::phrase_parse(first, last, + + // Begin grammar + ( + double_[assign] >> *(',' >> double_[add]) + ) + , + // End grammar + + space); + + if (first != last) // fail if we did not get a full match + return false; + return r; + } +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA parser for summing a list of numbers...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers are added using Phoenix.\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; + + double n; + if (client::adder(str.begin(), str.end(), n)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + std::cout << "sum = " << n; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} |