Skip to content

Instantly share code, notes, and snippets.

@hsk
Last active August 29, 2015 14:04
Show Gist options
  • Save hsk/1af9ac2c3820841491c6 to your computer and use it in GitHub Desktop.
Save hsk/1af9ac2c3820841491c6 to your computer and use it in GitHub Desktop.
#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/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <iostream>
#include <vector>
#include <string>
#define rw boost::recursive_wrapper
#define visit(v) boost::apply_visitor(*this, v)
#define match(t) public boost::static_visitor<t>
#define case(a) operator()(a) const
struct E1;
struct E2;
typedef boost::variant<int, rw<E1>, rw<E2> > E;
struct E2 {
char op;E a;E b;
E2(char op, E const& a, E const& b) : op(op), a(a), b(b) {}
};
struct E1 {
char op; E e;
E1() : op('n'),e(0) {}
E1(char op, E const& a) : op(op), e(a) {}
template <typename T> E1(T const& e): op('m'),e(e) {}
E1& operator+=(E1 const& rhs) {
e = E2(rhs.op, e, rhs.e);
return *this;
}
};
struct EOp {
char op;
EOp(char op):op(op){}
template <typename T> struct result {
typedef T type;
};
E1 operator()(E1 const& e) const {
return E1(op, e);
}
};
typedef boost::phoenix::function<EOp> eop;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using qi::_val;
using qi::_1;
using qi::int_;
template <typename Iterator>
struct parser : qi::grammar<Iterator, E1(), ascii::space_type> {
qi::rule<Iterator, E1(), ascii::space_type> expr, term, fact;
parser() : parser::base_type(expr) {
expr = term [_val = _1] >> *(
'+' >> term [_val += eop('+')(_1)] |
'-' >> term [_val += eop('-')(_1)]
);
term = fact [_val = _1] >> *(
'*' >> fact [_val += eop('*')(_1)] |
'/' >> fact [_val += eop('/')(_1)]
);
fact = int_ [_val = _1]
| '(' >> expr [_val = _1] >> ')'
| '-' >> fact [_val = eop('-')(_1)];
}
};
struct cnv : match(E) {
E case(int n) { return n; }
E case(E1 const& e) {
switch(e.op) {
case 'm': return visit(e.e);
default: return E1(e.op,visit(e.e));
}
}
E case(E2 const& e) {
return E2(e.op,visit(e.a),visit(e.b));
}
};
E parse(std::string const& str) {
using boost::spirit::ascii::space;
typedef std::string::const_iterator iterator_type;
E1 e;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
bool r = phrase_parse(iter, end, parser<iterator_type>(), space, e) && iter == end;
if(!r)e.op='e';
return cnv()(e);
}
struct print : match(void) {
void case(int n) {
std::cout << n;
}
void case(E1 const& e) {
std::cout << e.op << "(";
visit(e.e);
std::cout << ')';
}
void case(E2 const& e) {
std::cout << e.op << "(";
visit(e.a);
std::cout << ", ";
visit(e.b);
std::cout << ')';
}
};
struct eval : match(int) {
int case(int n) { return n; }
int case(E1 const& e) {
switch(e.op) {
case '-': return -visit(e.e);
default: return visit(e.e);
}
}
int case(E2 const& e) {
switch(e.op) {
case '+': return visit(e.a) + visit(e.b);
case '-': return visit(e.a) - visit(e.b);
case '*': return visit(e.a) * visit(e.b);
case '/': return visit(e.a) / visit(e.b);
default: return 0;
}
}
};
int main(int argv, char** args) {
std::cout << args[1] << std::endl;
E e = parse(args[1]);
print()(e); std::cout << std::endl;
std::cout << eval()(e) << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment