/* Copyright (c) 2011, Ronald Landheer-Cieslak <rlc at vlinder dot ca>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Vlinder Software nor the name of Ronald
* Landheer-Cieslak names of its contributors may be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL RONALD LANDHEER-CIESLAK BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
*/
#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 <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/erase.hpp>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using namespace std;
using namespace boost;
enum Operator {
plus__,
minus__,
multiply__,
divide__,
operator_count__
};
enum ListItemType {
int__,
double__,
string__,
expression__,
list_item_type_count__
};
struct Expression;
typedef variant< int, double, string, Expression > ListItem;
typedef vector< ListItem > List;
struct Expression
{
Operator operator_;
List list_;
};
BOOST_FUSION_ADAPT_STRUCT(
Expression,
(Operator, operator_)
(List, list_)
)
template < ListItemType target_list_item_type__ >
struct get_cast_target_type;
template <>
struct get_cast_target_type< int__ >
{
typedef int type;
};
template <>
struct get_cast_target_type< double__ >
{
typedef double type;
};
template <>
struct get_cast_target_type< string__ >
{
typedef string type;
};
template < ListItemType target_list_item_type__ >
/*unspecified*/typename get_cast_target_type< target_list_item_type__ >::type cast(const ListItem & list_item);
template < >
int cast< int__ >(const ListItem & list_item)
{
assert(list_item.which() == int__);
return get< int >(list_item);
}
template < >
double cast< double__ >(const ListItem & list_item)
{
assert((list_item.which() == int__) || (list_item.which() == double__));
if (list_item.which() == double__)
{
return get< double >(list_item);
}
else
{
return static_cast< double >(get< int >(list_item));
}
}
template < >
string cast< string__ >(const ListItem & list_item)
{
assert(list_item.which() != expression__);
if (list_item.which() == string__)
{
return get< string >(list_item);
}
else if (list_item.which() == int__)
{
return lexical_cast< string >(get< int >(list_item));
}
else
{
assert(list_item.which() == double__);
return lexical_cast< string >(get< double >(list_item));
}
}
template < Operator operator__, ListItemType return_type__ >
struct Operator_;
template <>
struct Operator_< plus__, int__ >
{
static ListItem apply(ListItem lhs, ListItem rhs)
{
assert(lhs.which() == int__);
assert(rhs.which() == int__);
get< int >(lhs) += get< int >(rhs);
return lhs;
}
};
template <>
struct Operator_< plus__, double__ >
{
static ListItem apply(ListItem lhs, ListItem rhs)
{
get< double >(lhs) += cast< double__ >(rhs);
return lhs;
}
};
template <>
struct Operator_< plus__, string__ >
{
static ListItem apply(ListItem lhs, ListItem rhs)
{
string r = cast< string__ >(rhs);
string &l = get< string >(lhs);
l.insert(l.end(), r.begin(), r.end());
return lhs;
}
};
template <>
struct Operator_< minus__, int__ >
{
static ListItem apply(ListItem lhs, ListItem rhs)
{
assert(lhs.which() == int__);
assert(rhs.which() == int__);
get< int >(lhs) -= get< int >(rhs);
return lhs;
}
};
template <>
struct Operator_< minus__, double__ >
{
static ListItem apply(ListItem lhs, ListItem rhs)
{
get< double >(lhs) -= get< double >(rhs);
return lhs;
}
};
template <>
struct Operator_< minus__, string__ >
{
static ListItem apply(ListItem lhs, ListItem rhs)
{
using boost::algorithm::erase_first;
string r = cast< string__ >(rhs);
string &l = get< string >(lhs);
erase_first(l, r);
return lhs;
}
};
template <>
struct Operator_< divide__, int__ >
{
static ListItem apply(ListItem lhs, ListItem rhs)
{
assert(lhs.which() == int__);
assert(rhs.which() == int__);
get< int >(lhs) /= get< int >(rhs);
return lhs;
}
};
template <>
struct Operator_< divide__, double__ >
{
static ListItem apply(ListItem lhs, ListItem rhs)
{
get< double >(lhs) /= cast< double__ >(rhs);
return lhs;
}
};
template <>
struct Operator_< multiply__, int__ >
{
static ListItem apply(ListItem lhs, ListItem rhs)
{
assert(lhs.which() == int__);
assert(rhs.which() == int__);
get< int >(lhs) *= get< int >(rhs);
return lhs;
}
};
template <>
struct Operator_< multiply__, double__ >
{
static ListItem apply(ListItem lhs, ListItem rhs)
{
get< double >(lhs) *= cast< double__ >(rhs);
return lhs;
}
};
struct Accumulator
{
virtual ~Accumulator()
{}
virtual Accumulator* create(ListItem &result) const = 0;
const Accumulator& operator()(const ListItem &list_item) const
{
call_(list_item);
return *this;
}
ListItem operator*() const
{
return *result_;
}
protected :
virtual void call_(const ListItem &list_item) const = 0;
Accumulator()
: result_(0)
, first_(true)
{ /* no-op */ }
Accumulator(ListItem &result)
: result_(&result)
, first_(true)
{ /* no-op */ }
ListItem *result_;
mutable bool first_;
};
template < Operator operator_type__, ListItemType return_type__ >
struct Accumulator_ : Accumulator
{
private :
Accumulator_()
{ /* no-op */ }
Accumulator_(ListItem &result)
: Accumulator(result)
{ /* no-op */ }
Accumulator_* create(ListItem &result) const
{
return new Accumulator_(result);
}
protected :
/*virtual */void call_(const ListItem &list_item) const/* = 0*/
{
if (first_)
{
switch (result_->which())
{
case int__ :
*result_ = cast< int__ >(list_item);
break;
case double__ :
*result_ = cast< double__ >(list_item);
break;
case string__ :
*result_ = cast< string__ >(list_item);
break;
}
first_ = false;
}
else
{
*result_ = Operator_< operator_type__, return_type__ >::apply(*result_, list_item);
}
}
friend boost::shared_ptr< Accumulator > getAccumulator(ListItem &result, Operator operator_type, ListItemType return_type);
};
boost::shared_ptr< Accumulator > getAccumulator(ListItem &result, Operator operator_type, ListItemType return_type)
{
static Accumulator_< plus__, int__ > pi_accumulator__;
static Accumulator_< plus__, double__ > pd_accumulator__;
static Accumulator_< plus__, string__ > ps_accumulator__;
static Accumulator_< minus__, int__ > mi_accumulator__;
static Accumulator_< minus__, double__ > md_accumulator__;
static Accumulator_< minus__, string__ > ms_accumulator__;
static Accumulator_< multiply__, int__ > ui_accumulator__;
static Accumulator_< multiply__, double__ > ud_accumulator__;
static Accumulator_< divide__, int__ > di_accumulator__;
static Accumulator_< divide__, double__ > dd_accumulator__;
static Accumulator* accumulators__[operator_count__][list_item_type_count__] = {
{ &pi_accumulator__, &pd_accumulator__, &ps_accumulator__, 0 },
{ &mi_accumulator__, &md_accumulator__, &ms_accumulator__, 0 },
{ &ui_accumulator__, &ud_accumulator__, 0, 0 },
{ &di_accumulator__, &dd_accumulator__, 0, 0 }
};
if (accumulators__[operator_type][return_type] != 0)
{
return boost::shared_ptr< Accumulator >(accumulators__[operator_type][return_type]->create(result));
}
else
{
if (operator_type == multiply__)
{
throw logic_error("Don't know how to multiply a string");
}
else
{
throw logic_error("Don't know how to divide a string");
}
}
}
void ping()
{
cout << "ping" << endl;
}
template < typename Iterator >
struct Grammar : qi::grammar< Iterator, Expression(), ascii::space_type >
{
Grammar()
: Grammar::base_type(expression_)
{
using qi::_val;
using qi::_1;
using phoenix::push_back;
using qi::lexeme;
expression_ = '(' >> operator_ >> list_ >> ')'
;
operator_.add
("+", plus__)
("-", minus__)
("*", multiply__)
("/", divide__)
;
list_ = +list_item_
;
list_item_ = expression_
| qi::int_
| qi::double_
| string_
;
string_ = lexeme['"' >> *(unescape_char_ | ("\\x" >> qi::hex) | (qi::char_ - qi::char_('"'))) >> '"']
;
unescape_char_.add
("\\a", '\a')("\\b", '\b')("\\f", '\f')("\\n", '\n')
("\\r", '\r')("\\t", '\t')("\\v", '\v')("\\\\", '\\')
("\\\'", '\'')("\\\"", '\"');
}
qi::rule< Iterator, Expression(), ascii::space_type > expression_;
qi::rule< Iterator, List(), ascii::space_type > list_;
qi::rule< Iterator, ListItem(), ascii::space_type > list_item_;
qi::rule< Iterator, string(), ascii::space_type > string_;
qi::symbols< char const, char const > unescape_char_;
qi::symbols< char const, Operator > operator_;
};
ListItem evaluate(Expression &expression)
{
ListItemType result_type(int__);
ListItem retval;
// first pass: evaluate any sub-expressions
for (List::iterator iter(expression.list_.begin()); iter != expression.list_.end(); ++iter)
{
if (iter->which() == expression__)
{
*iter = evaluate(get< Expression >(*iter));
}
switch (iter->which())
{
case int__ :
// this is the default type - it doesn't change anything
break;
case double__ :
if (result_type == int__)
{
result_type = double__;
}
else
{ /* either already a double, or it's a string */ }
break;
case string__ :
result_type = string__;
break;
default :
throw logic_error("unexpected operand type");
}
}
switch (result_type)
{
case int__ :
// nothing to do in this case: this is the default for the variant, and it will be zero-initialized
break;
case double__ :
retval = 0.0;
break;
case string__ :
retval = string();
break;
default :
throw logic_error("Unexpected result type");
}
boost::shared_ptr< Accumulator > accumulator = getAccumulator(retval, expression.operator_, result_type);
for (List::const_iterator iter(expression.list_.begin()); iter != expression.list_.end(); ++iter)
{
(*accumulator)(*iter);
}
return retval;
}
int main()
{
using boost::spirit::ascii::space;
Expression expression;
Grammar< string::const_iterator > grammar;
string test("(+ 1 (+ 1 1.2))");
string::const_iterator iter = test.begin();
string::const_iterator end = test.end();
if (qi::phrase_parse(iter, end, grammar, space, expression))
{
assert(expression.operator_ == plus__);
ListItem result(evaluate(expression));
assert(result.which() == double__);
assert(get< double >(result) == 3.2);
}
else
{
assert(!"parse failed");
}
test = "(* 1 2)";
iter = test.begin();
end = test.end();
expression.list_.clear();
if (qi::phrase_parse(iter, end, grammar, space, expression))
{
assert(expression.operator_ == multiply__);
ListItem result(evaluate(expression));
assert(result.which() == int__);
assert(get< int >(result) == 2);
}
else
{
assert(!"parse failed");
}
test = "(+ \"Hello, \" \"world!\")";
iter = test.begin();
end = test.end();
expression.list_.clear();
if (qi::phrase_parse(iter, end, grammar, space, expression))
{
assert(expression.operator_ == plus__);
ListItem result(evaluate(expression));
assert(result.which() == string__);
assert(get< string >(result) == "Hello, world!");
}
else
{
assert(!"parse failed");
}
test = "(+ \"Goodbye\" (- \"Hello, world!\" \"Hello\"))";
iter = test.begin();
end = test.end();
expression.list_.clear();
if (qi::phrase_parse(iter, end, grammar, space, expression))
{
assert(expression.operator_ == plus__);
ListItem result(evaluate(expression));
assert(result.which() == string__);
assert(get< string >(result) == "Goodbye, world!");
}
else
{
assert(!"parse failed");
}
}