Here is a quick and grammar grammar for the input shown.
UPDATE
Now the call and implementation of predicate functions ( GreaterImpl and EqualsImpl ) is EqualsImpl .
I tried to be smart, allowing you to compare mixed arithmetic types (but not, for example, Greater(bool,string) ). If you compare incompatible types, you will get a std::runtime_error exception that provides type feedback to the caller.
Live on coliru
#include <deque> #include <boost/spirit/include/qi.hpp> #include <boost/fusion/adapted/struct.hpp> namespace qi = boost::spirit::qi; namespace Ast { using Value = boost::variant<int, double, bool, std::string>; using BinaryPred = std::function<bool(Value, Value)>; using TernaryPred = std::function<bool(Value, Value, Value)>; using Pred = boost::variant<BinaryPred, TernaryPred>; using Values = std::vector<Value>; struct Invocation { Pred pred; Values args; }; using Invocations = std::vector<Invocation>; } BOOST_FUSION_ADAPT_STRUCT(Ast::Invocation, pred, args) namespace Predicates { using Ast::Value; struct Greater : boost::static_visitor<bool> { bool operator()(Value const& a, Value const& b) const { return boost::apply_visitor(*this, a, b); } template <typename T> bool operator()(T const& a, T const& b) const { return std::greater<T>{}(a, b); } template <typename T, typename U> typename std::enable_if<std::is_arithmetic<T>() && std::is_arithmetic<U>(), bool>::type operator()(T const& a, U const& b) const { return a > b; } template <typename T, typename U> typename std::enable_if<not (std::is_arithmetic<T>() && std::is_arithmetic<U>()), bool>::type operator()(T const&, U const&) const { throw std::runtime_error("Type Mismatch"); } }; struct Equals : boost::static_visitor<bool> { bool operator()(Value const& a, Value const& b) const { return boost::apply_visitor(*this, a, b); } template <typename T> bool operator()(T const& a, T const& b) const { return std::equal_to<T>{}(a, b); } template <typename T, typename U, typename enable = typename std::enable_if<std::is_arithmetic<T>() && std::is_arithmetic<U>()>::type > bool operator()(T const& a, U const& b) const { return a == b; } template <typename T, typename U> typename std::enable_if<not (std::is_arithmetic<T>() && std::is_arithmetic<U>()), bool>::type operator()(T const&, U const&) const { throw std::runtime_error("Type Mismatch"); } }; struct Between { bool operator()(Value const& v, Value const& lower, Value const& upper) const { return Greater{}(v,lower) && Greater{}(upper,v); } }; } static inline bool evaluate(Ast::Invocation const& i) { struct Invoker { using result_type = bool; Ast::Values const& args; result_type operator()(Ast::BinaryPred const& p) const { if (args.size() != 2) throw std::runtime_error("Arity Mismatch"); return p(args.at(0), args.at(1)); } result_type operator()(Ast::TernaryPred const& p) const { if (args.size() != 3) throw std::runtime_error("Arity Mismatch"); return p(args.at(0), args.at(1), args.at(2)); } }; return boost::apply_visitor(Invoker{i.args}, i.pred); } template <typename It> struct Grammar : qi::grammar<It, Ast::Invocations()> { Grammar() : Grammar::base_type(start) { using namespace qi; start = skip(blank) [ invocation % eol ]; invocation = pred >> -("," >> args); args = arg % ","; arg = my_double_ | qi::int_ | qi::bool_ | lexeme['"' > *~char_('"') > '"']; } private: struct pred_t : qi::symbols<char, Ast::Pred> { pred_t() { this->add ("Greater", Predicates::Greater{}) ("Equals", Predicates::Equals{}) ("Between", Predicates::Between{}) ; } } const pred; qi::rule<It, Ast::Invocations()> start; qi::rule<It, Ast::Invocation(), qi::blank_type> invocation; qi::rule<It, Ast::Values(), qi::blank_type> args; qi::rule<It, Ast::Value(), qi::blank_type> arg; qi::real_parser<double, qi::strict_real_policies<double> > my_double_; }; #include <sstream> int main() { using It = boost::spirit::istream_iterator; std::deque<std::string> testcases { // one multiline case: "Between, 20, 10, 30\n" "Between, NaN, NaN, NaN\n" "Between, \"q\", \"a\", \"z\"" }; // many single line cases for easy test reporting for (std::string op : {"Greater","Equals"}) for (auto rhs : { "42", "0.0", "true", "\"hello\"" }) for (auto lhs : { "41", "-0.0", "false", "\"bye\"" }) { testcases.push_front(op + ", " + lhs + ", " + rhs); } for (auto testcase : testcases) { std::cout << "--- Testcase '" << testcase << "' -> "; std::istringstream iss(testcase); It f(iss >> std::noskipws), l; Ast::Invocations parsed; if (qi::parse(f, l, Grammar<It>(), parsed)) { for (auto& invocation : parsed) { try { std::cout << std::boolalpha << evaluate(invocation) << "; "; } catch(std::exception const& e) { std::cout << e.what() << "; "; } } std::cout << "\n"; } else { std::cout << "Parse failed\n"; } if (f != l) std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n"; } }
Print
--- Testcase 'Equals, "bye", "hello"' -> false; --- Testcase 'Equals, false, "hello"' -> Type Mismatch; --- Testcase 'Equals, -0.0, "hello"' -> Type Mismatch; --- Testcase 'Equals, 41, "hello"' -> Type Mismatch; --- Testcase 'Equals, "bye", true' -> Type Mismatch; --- Testcase 'Equals, false, true' -> false; --- Testcase 'Equals, -0.0, true' -> false; --- Testcase 'Equals, 41, true' -> false; --- Testcase 'Equals, "bye", 0.0' -> Type Mismatch; --- Testcase 'Equals, false, 0.0' -> true; --- Testcase 'Equals, -0.0, 0.0' -> true; --- Testcase 'Equals, 41, 0.0' -> false; --- Testcase 'Equals, "bye", 42' -> Type Mismatch; --- Testcase 'Equals, false, 42' -> false; --- Testcase 'Equals, -0.0, 42' -> false; --- Testcase 'Equals, 41, 42' -> false; --- Testcase 'Greater, "bye", "hello"' -> false; --- Testcase 'Greater, false, "hello"' -> Type Mismatch; --- Testcase 'Greater, -0.0, "hello"' -> Type Mismatch; --- Testcase 'Greater, 41, "hello"' -> Type Mismatch; --- Testcase 'Greater, "bye", true' -> Type Mismatch; --- Testcase 'Greater, false, true' -> false; --- Testcase 'Greater, -0.0, true' -> false; --- Testcase 'Greater, 41, true' -> true; --- Testcase 'Greater, "bye", 0.0' -> Type Mismatch; --- Testcase 'Greater, false, 0.0' -> false; --- Testcase 'Greater, -0.0, 0.0' -> false; --- Testcase 'Greater, 41, 0.0' -> true; --- Testcase 'Greater, "bye", 42' -> Type Mismatch; --- Testcase 'Greater, false, 42' -> false; --- Testcase 'Greater, -0.0, 42' -> false; --- Testcase 'Greater, 41, 42' -> false; --- Testcase 'Between, 20, 10, 30 Between, NaN, NaN, NaN Between, "q", "a", "z"' -> true; false; true;