diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c60988e12317074aee65e72688cc2648b1c61815..226d711e2cbedb4ca44518d695becec2b61e8299 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ # driver source files -SET(sources imagine-planner.cpp types.cpp queries.cpp) +SET(sources imagine-planner.cpp types.cpp queries.cpp parsing.cpp) # application header files -SET(headers imagine-planner.h types.h queries.h) +SET(headers imagine-planner.h types.h queries.h parsing.h) # Boost FIND_PACKAGE(Boost COMPONENTS system filesystem unit_test_framework REQUIRED) # Swi-pl diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt index 27851e08db4d615d18d3b700109c0dfca5f87210..dd6dd89c4c842afdb3c2396ee643052fd1d23683 100644 --- a/src/examples/CMakeLists.txt +++ b/src/examples/CMakeLists.txt @@ -17,3 +17,10 @@ TARGET_LINK_LIBRARIES(queries_test ${Boost_SYSTEM_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) +ADD_EXECUTABLE(parsing_test parsing_test.cpp) +TARGET_LINK_LIBRARIES(parsing_test + imagine-planner + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + diff --git a/src/examples/parsing_test.cpp b/src/examples/parsing_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6d7422c4c8a3aa0dcf4eafece2891ae6dc355192 --- /dev/null +++ b/src/examples/parsing_test.cpp @@ -0,0 +1,142 @@ +#define BOOST_TEST_MODULE Parsing Test +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> + +#include "parsing.h" +#include <iostream> +using namespace imagine_planner; + +#if 0 // Set to 1 if __PRETTY_FUNCTION__ macro is not available +#define __PRETTY_FUNCTION__ __func__ +#endif + +#if 1 +#define INFO(x)\ + std::cout << "\e[1m\e[32m\e[4m" << __PRETTY_FUNCTION__ << " [" << __LINE__ << "]" << "\e[24m: " << x << "\e[0m" << std::endl; +#else +#define INFO(x) +#endif + +std::ostream& operator<<(std::ostream& os, const TokenV& tokens) +{ + bool first = true; + os << '['; + for (const Token& token : tokens) + { + if (not first) os << ", "; + os << token.first << "(" << token.second << ")"; + first = false; + } + os << ']'; + return os; +} + +BOOST_AUTO_TEST_CASE(spaces) +{ + std::string str(" Hello ( world) "); + std::string str_clean = remove_spaces(str); + INFO("str_clean: " << str_clean); + BOOST_CHECK_EQUAL(str_clean, "Hello(world)"); +} + +BOOST_AUTO_TEST_CASE(test_parse_predicate) +{ + std::string str("at(lettuce,east), cost(2.5), r(), s("); + TokenV tokens = decompose(str); + INFO(tokens); + std::size_t cursor = 0; + Predicate* predicate = parse_predicate(tokens, cursor); + BOOST_REQUIRE(predicate); + BOOST_CHECK_EQUAL(*predicate, Predicate("at", "lettuce", "east")); + BOOST_CHECK_EQUAL(cursor, 6); + delete predicate; + cursor = 7; + predicate = parse_predicate(tokens, cursor); + BOOST_REQUIRE(predicate); + BOOST_CHECK_EQUAL(*predicate, Predicate("cost", 2.5)); + BOOST_CHECK_EQUAL(cursor, 11); + delete predicate; + cursor = 12; + predicate = parse_predicate(tokens, cursor); + BOOST_REQUIRE(predicate); + BOOST_CHECK_EQUAL(*predicate, Predicate("r")); + BOOST_CHECK_EQUAL(cursor, 15); + delete predicate; + cursor = 16; + predicate = parse_predicate(tokens, cursor); + BOOST_REQUIRE(not predicate); +} + +BOOST_AUTO_TEST_CASE(test_parse_simple_expressions) +{ + std::string str1("at(X, Y)"); + std::string str2("\\+at(X, Y)"); + std::string str3("(at(X, Y))"); + std::string str4("\\+(at(X, Y))"); + QuerySpecification* q1 = parse_expression(str1); + if (q1) + { + INFO("q1: " << *q1); + delete q1; + } + else INFO("q1: none"); + QuerySpecification* q2 = parse_expression(str2); + if (q2) + { + INFO("q2: " << *q2); + delete q2; + } + else INFO("q2: none"); + QuerySpecification* q3 = parse_expression(str3); + if (q3) + { + INFO("q3: " << *q3); + delete q3; + } + else INFO("q3: none"); + QuerySpecification* q4 = parse_expression(str4); + if (q4) + { + INFO("q4: " << *q4); + delete q4; + } + else INFO("q4: none"); +} + +BOOST_AUTO_TEST_CASE(test_parse_complex_expressions) +{ + std::string str1("at(X, Y), at(Z, Y)"); + std::string str2("at(X, Y), at(Z, Y), \\+at(boat, Y)"); + std::string str3("fixes(X, Y), \\+(precedes(_1, X), occluded_by(X, _2))"); + std::string str4("\\+(\\+q(X), \\+q(Y), \\+r())"); + QuerySpecification* q1 = parse_expression(str1); + if (q1) + { + INFO("q1: " << *q1); + delete q1; + } + else INFO("q1: none"); + QuerySpecification* q2 = parse_expression(str2); + if (q2) + { + INFO("q2: " << *q2); + delete q2; + } + else INFO("q2: none"); + QuerySpecification* q3 = parse_expression(str3); + if (q3) + { + INFO("q3: " << *q3); + delete q3; + } + else INFO("q3: none"); + QuerySpecification* q4 = parse_expression(str4); + if (q4) + { + INFO("q4: " << *q4); + delete q4; + } + else INFO("q4: none"); +} + + diff --git a/src/examples/queries_test.cpp b/src/examples/queries_test.cpp index ca9aa7c50df273ef1a9273e1f762aedb9ddb16bd..e86e3c675a2dc180f206eb7df1dfe11f930a8e16 100644 --- a/src/examples/queries_test.cpp +++ b/src/examples/queries_test.cpp @@ -1,4 +1,4 @@ -#define BOOST_TEST_MODULE Types Test +#define BOOST_TEST_MODULE Queries Test #define BOOST_TEST_DYN_LINK #include <boost/test/unit_test.hpp> @@ -24,6 +24,7 @@ BOOST_AUTO_TEST_CASE(simple_query_test_sat) INFO(state); SimpleQuery q1(state, Predicate("p", "a", "X")); SimpleQuery q2(state, Predicate("q", "w")); + INFO("q1: " << q1 << ", " << q2); BOOST_REQUIRE(q1.next_solution()); BOOST_CHECK_EQUAL(q1.get_solution().to_str(), "{\n X -> b\n}"); BOOST_REQUIRE(q1.next_solution()); @@ -41,6 +42,7 @@ BOOST_AUTO_TEST_CASE(simple_query_test_unsat) INFO(state); SimpleQuery q1(state, Predicate("p", "c", "X")); SimpleQuery q2(state, Predicate("p", "a", "d")); + INFO("q1: " << q1 << ", " << q2); BOOST_REQUIRE(not q1.next_solution()); BOOST_REQUIRE(not q2.next_solution()); } @@ -48,16 +50,19 @@ BOOST_AUTO_TEST_CASE(simple_query_test_unsat) BOOST_AUTO_TEST_CASE(and_query_test_sat) { PlanState state({Predicate("p","a","b"), Predicate("p","a","c"), - Predicate("p", "b", "d"), Predicate("p","d","a"), Predicate("q","a"), - Predicate("q", "d")}); + Predicate("p", "b", "d"), Predicate("p","d","a"), Predicate("q","b")}); INFO("state: " << state); - SimpleQuery q1(state, Predicate("p", "X", "Y")); - SimpleQuery q2(state, Predicate("q", "X")); - //NotQuery not1(q2); - AndQuery and1(q1, q2); - while (and1.next_solution()) - { - INFO(and1.get_solution()); - } + SimpleQuery* q1 = new SimpleQuery(state, Predicate("p", "X", "Y")); + SimpleQuery* q2 = new SimpleQuery(state, Predicate("q", "X")); + NotQuery* not1 = new NotQuery(q2); + AndQuery* and1 = new AndQuery(q1, not1); + INFO("and1: " << *and1); + BOOST_REQUIRE(and1->next_solution()); + BOOST_CHECK_EQUAL(and1->get_solution().to_str(), "{\n X -> a,\n Y -> b\n}"); + BOOST_REQUIRE(and1->next_solution()); + BOOST_CHECK_EQUAL(and1->get_solution().to_str(), "{\n X -> a,\n Y -> c\n}"); + BOOST_REQUIRE(and1->next_solution()); + BOOST_CHECK_EQUAL(and1->get_solution().to_str(), "{\n X -> d,\n Y -> a\n}"); + BOOST_REQUIRE(not and1->next_solution()); } diff --git a/src/parsing.cpp b/src/parsing.cpp new file mode 100644 index 0000000000000000000000000000000000000000..49677a1bf2694abeb28eb451d862d963e4b0f54c --- /dev/null +++ b/src/parsing.cpp @@ -0,0 +1,249 @@ +#include "parsing.h" +#include <cctype> +#include <sstream> +#include <iostream> + +namespace imagine_planner +{ + +bool is_number(const std::string& str) +{ + try + { + std::size_t idx = 0; + std::stod(str, &idx); + return idx == str.length(); // no more chars after the number + } + catch (...) + { + return false; + } +} + +std::string remove_spaces(const std::string& in) +{ + std::ostringstream oss; + for (char c : in) + { + if (not std::isspace(c)) oss << c; + } + return oss.str(); +} + +TokenV decompose(const std::string& in) +{ + TokenV tokens; + std::string in_clean = remove_spaces(in); + std::size_t cursor = 0; + while (cursor < in_clean.length()) + { + if (in_clean.compare(cursor, 2, "\\+") == 0) + { + tokens.push_back({"NOT", ""}); + cursor += 2; + } + else if (in_clean[cursor] == '(') + { + tokens.push_back({"PAR_OPEN", ""}); + ++cursor; + } + else if (in_clean[cursor] == ')') + { + tokens.push_back({"PAR_CLOSE", ""}); + ++cursor; + } + else if (in_clean[cursor] == ',') + { + tokens.push_back({"COMMA", ""}); + ++cursor; + } + else + { + std::size_t end = in_clean.find_first_of(",()\\", cursor); + if (end == std::string::npos) end = in_clean.length(); + std::size_t len = end - cursor; + std::string substr = in_clean.substr(cursor, len); + std::string type = is_number(substr)? "NUMBER" : "SYMBOL"; + tokens.push_back({type, substr}); + cursor = end; + } + } + tokens.push_back({"EOF", ""}); + return tokens; +} + +Predicate* parse_predicate(const TokenV& tokens, std::size_t& cursor) +{ + enum State {EXPECTING_NAME, EXPECTING_PAR_OPEN, EXPECTING_ARG_OR_END, + EXPECTING_COMMA_OR_END, EXPECTING_ARG, KO, OK}; + State state = EXPECTING_NAME; + Predicate* predicate = nullptr; + std::string pred_name; + TermV arguments; + std::size_t idx = cursor; + while (state != OK and state != KO) + { + const Token& token = tokens[idx]; + switch (state) + { + case EXPECTING_NAME: + if (token.first == "SYMBOL") + { + pred_name = token.second; + state = EXPECTING_PAR_OPEN; + } + else state = KO; + break; + case EXPECTING_PAR_OPEN: + if (token.first == "PAR_OPEN") state = EXPECTING_ARG_OR_END; + else state = KO; + break; + case EXPECTING_ARG_OR_END: + if (token.first == "PAR_CLOSE") state = OK; + else if (token.first == "SYMBOL") + { + arguments.push_back(TermWrapper(token.second)); + state = EXPECTING_COMMA_OR_END; + } + else if (token.first == "NUMBER") + { + arguments.push_back(TermWrapper(std::stod(token.second))); + state = EXPECTING_COMMA_OR_END; + } + else state = KO; + break; + case EXPECTING_COMMA_OR_END: + if (token.first == "COMMA") state = EXPECTING_ARG; + else if (token.first == "PAR_CLOSE") state = OK; + else state = KO; + break; + case EXPECTING_ARG: + if (token.first == "SYMBOL") + { + arguments.push_back(TermWrapper(token.second)); + state = EXPECTING_COMMA_OR_END; + } + else if (token.first == "NUMBER") + { + arguments.push_back(TermWrapper(std::stod(token.second))); + state = EXPECTING_COMMA_OR_END; + } + else state = KO; + break; + default: + /* do nothing */ + break; + } + ++idx; + } + if (state == OK) + { + cursor = idx; + predicate = new Predicate(pred_name, arguments); + } + return predicate; +} + +QuerySpecification* parse_expression(const TokenV& tokens, std::size_t& cursor) +{ + enum State {EXPECTING_PRED_OR_PAR, EXPECTING_PAR_EXPR, + EXPECTING_PAR_CLOSE, EXPECTING_COMMA_OR_END, EXPECTING_END_EXPR, OK, KO}; + State state = EXPECTING_PRED_OR_PAR; + bool negated = false; + QuerySpecification* query = nullptr; + std::size_t idx = cursor; + while (state != OK and state != KO) + { + const Token& token = tokens[idx]; + std::cout << token.first << '(' << token.second << ')' << std::endl; + switch (state) + { + case EXPECTING_PRED_OR_PAR: + std::cout << "EXPECTING_PRED_OR_PAR" << std::endl; + if (token.first == "NOT") + { + negated = not negated; + ++idx; + /* Stay in the same state */ + } + else if (Predicate* predicate = parse_predicate(tokens, idx)) + { + query = new SimpleQuerySpecification(*predicate); + query = negated? new NotQuerySpecification(query) : query; + state = EXPECTING_COMMA_OR_END; + delete predicate; + } + else if (token.first == "PAR_OPEN") + { + state = EXPECTING_PAR_EXPR; + ++idx; + } + else state = KO; + break; + case EXPECTING_PAR_EXPR: + std::cout << "EXPECTING_PAR_EXPR" << std::endl; + if ((query = parse_expression(tokens, idx))) + { + query = negated? new NotQuerySpecification(query) : query; + state = EXPECTING_PAR_CLOSE; + } + else state = KO; + break; + case EXPECTING_PAR_CLOSE: + std::cout << "EXPECTING_PAR_CLOSE" << std::endl; + if (token.first == "PAR_CLOSE") + { + state = EXPECTING_COMMA_OR_END; + ++idx; + } + else state = KO; + break; + case EXPECTING_COMMA_OR_END: + std::cout << "EXPECTING_COMMA_OR_END" << std::endl; + if (token.first == "COMMA") + { + state = EXPECTING_END_EXPR; + ++idx; + } + else state = OK; + break; + case EXPECTING_END_EXPR: + std::cout << "EXPECTING_END_EXPR" << std::endl; + if (QuerySpecification* query_snd = parse_expression(tokens, idx)) + { + query = new AndQuerySpecification(query, query_snd); + state = OK; + } + break; + default: + /* do nothing */ + break; + } + } + if (state == OK) + { + std::cout << "OK" << std::endl; + cursor = idx; + } + else + { + std::cout << "KO" << std::endl; + if (query) + { + delete query; + query = nullptr; + } + } + return query; +} + +QuerySpecification* parse_expression(const std::string& str) +{ + TokenV tokens = decompose(str); + std::size_t cursor = 0; + return parse_expression(tokens, cursor); +} + +} + + diff --git a/src/parsing.h b/src/parsing.h new file mode 100644 index 0000000000000000000000000000000000000000..c89d6f2f4fbcfeac0a18afb7bafb6693824e4e39 --- /dev/null +++ b/src/parsing.h @@ -0,0 +1,26 @@ +#ifndef _LIBIMAGINE_PLANNER_PARSING_H_ +#define _LIBIMAGINE_PLANNER_PARSING_H_ + +#include "queries.h" + +namespace imagine_planner +{ + +typedef std::pair<std::string, std::string> Token; +typedef std::vector<Token> TokenV; + +bool is_number(const std::string& str); + +std::string remove_spaces(const std::string& in); + +TokenV decompose(const std::string& in); + +Predicate* parse_predicate(const TokenV& tokens, std::size_t& cursor); + +QuerySpecification* parse_expression(const TokenV& tokens, std::size_t& cursor); + +QuerySpecification* parse_expression(const std::string& str); + +} /* end namespace imagine_planner */ + +#endif diff --git a/src/queries.cpp b/src/queries.cpp index 9ec9d7bde3121b2d5668c4c7d27c40149ca56766..3566caf868ffff48cab415dce7db43807abde301 100644 --- a/src/queries.cpp +++ b/src/queries.cpp @@ -7,12 +7,12 @@ namespace imagine_planner bool SimpleQuery::unify(const Predicate& goal) { - if (goal.arity() != target_.arity()) return false; - if (goal.get_name() != target_.get_name()) return false; + if (goal.arity() != pattern_.arity()) return false; + if (goal.get_name() != pattern_.get_name()) return false; sigma_ = sigma0_; for (int idx = 0; idx < goal.arity(); ++idx) { - const TermWrapper& term1 = target_.get_arguments()[idx]; + const TermWrapper& term1 = pattern_.get_arguments()[idx]; const TermWrapper& term2 = goal.get_arguments()[idx]; if (term1.is_ground()) { @@ -30,19 +30,24 @@ bool SimpleQuery::unify(const Predicate& goal) return true; } -SimpleQuery::SimpleQuery(const PlanState& state, const Predicate& target, +SimpleQuery::SimpleQuery(const PlanState& state, const Predicate& pattern, const Substitution& sigma0) : - state_(state), target_(target), sigma0_(sigma0) + state_(state), pattern_(pattern), sigma0_(sigma0) { cursor_ = state_.begin(); } +std::string SimpleQuery::to_str() const +{ + return pattern_.to_str(); +} + bool SimpleQuery::next_solution() { - if (cursor_ != state_.end() and target_.is_ground()) + if (cursor_ != state_.end() and pattern_.is_ground()) { cursor_ = state_.end(); - return state_.has(target_); + return state_.has(pattern_); } while (cursor_ != state_.end()) @@ -65,37 +70,52 @@ void SimpleQuery::reset(const Substitution& sigma0) // NotQuery methods -NotQuery::NotQuery(Query& q) : query_(q), done_(false) {} +NotQuery::NotQuery(Query* q) : query_(q), done_(false) {} + +std::string NotQuery::to_str() const +{ + return std::string("\\+") + query_->to_str(); +} bool NotQuery::next_solution() { if (done_) return false; done_ = true; - return not query_.next_solution(); + return not query_->next_solution(); } void NotQuery::reset(const Substitution& sigma0) { - query_.reset(sigma0); + query_->reset(sigma0); done_ = false; } +NotQuery::~NotQuery() +{ + delete query_; +} + // AndQuery methods -AndQuery::AndQuery(Query& q1, Query& q2) : +AndQuery::AndQuery(Query* q1, Query* q2) : q1_(q1), q2_(q2), first_fixed_(false) {} +std::string AndQuery::to_str() const +{ + return std::string(1, '(') + q1_->to_str() + ',' + q2_->to_str() + ')'; +} + bool AndQuery::next_solution() { - if (first_fixed_ and q2_.next_solution()) + if (first_fixed_ and q2_->next_solution()) { return true; } first_fixed_ = false; - while (q1_.next_solution()) + while (q1_->next_solution()) { - q2_.reset(q1_.get_solution()); - if (q2_.next_solution()) + q2_->reset(q1_->get_solution()); + if (q2_->next_solution()) { first_fixed_ = true; return true; @@ -104,5 +124,71 @@ bool AndQuery::next_solution() return false; } +AndQuery::~AndQuery() +{ + delete q1_; + delete q2_; +} + +// SimpleQuerySpecification methods + +std::string SimpleQuerySpecification::to_str() const +{ + return pattern_.to_str(); +} + +SimpleQuerySpecification::SimpleQuerySpecification(const Predicate& pattern) + : pattern_(pattern) {} + +Query* SimpleQuerySpecification::operator()(const PlanState& state, + const Substitution& sigma0) const +{ + return new SimpleQuery(state, pattern_, sigma0); +} + + +// NotQuerySpecification methods + +std::string NotQuerySpecification::to_str() const +{ + return std::string("\\+") + query_->to_str(); +} + +NotQuerySpecification::~NotQuerySpecification() +{ + delete query_; +} + +NotQuerySpecification::NotQuerySpecification(QuerySpecification* query) + : query_(query) {} + +Query* NotQuerySpecification::operator()(const PlanState& state, + const Substitution& sigma0) const +{ + return new NotQuery((*query_)(state, sigma0)); +} + +// AndQuerySpecification methods + +std::string AndQuerySpecification::to_str() const +{ + return std::string(1,'(') + q1_->to_str() + ',' + q2_->to_str() + ')'; +} + +AndQuerySpecification::~AndQuerySpecification() +{ + delete q1_; + delete q2_; +} + +AndQuerySpecification::AndQuerySpecification(QuerySpecification* q1, + QuerySpecification* q2) : q1_(q1), q2_(q2) {} + +Query* AndQuerySpecification::operator()(const PlanState& state, + const Substitution& sigma0) const +{ + return new AndQuery((*q1_)(state, sigma0), (*q2_)(state, sigma0)); +} + } diff --git a/src/queries.h b/src/queries.h index 3efe6e4f5f660b41333cda521410133fbec859d5..86f6976174299c996e60387d9e8909777bfb5b69 100644 --- a/src/queries.h +++ b/src/queries.h @@ -6,10 +6,10 @@ namespace imagine_planner { -class Query +class Query : public Stringifiable { public: - virtual ~Query() {}; + virtual ~Query() {} virtual bool next_solution() =0; @@ -25,16 +25,18 @@ class SimpleQuery : public Query { private: const PlanState& state_; - Predicate target_; + Predicate pattern_; Substitution sigma0_, sigma_; PlanState::CIter cursor_; bool unify(const Predicate& goal); public: - SimpleQuery(const PlanState& state, const Predicate& target, + SimpleQuery(const PlanState& state, const Predicate& pattern, const Substitution& sigma0=Substitution()); + virtual std::string to_str() const override; + virtual bool next_solution() override; virtual const Substitution& get_solution() const override { return sigma_; } @@ -48,54 +50,117 @@ class SimpleQuery : public Query class NotQuery : public Query { private: - Query& query_; + Query* query_; bool done_; public: - NotQuery(Query& q); + NotQuery(Query* q); + + virtual std::string to_str() const override; virtual bool next_solution() override; virtual const Substitution& get_solution() const override { - return query_.get_sigma0(); + return query_->get_sigma0(); } virtual const Substitution& get_sigma0() const override { - return query_.get_sigma0(); + return query_->get_sigma0(); } virtual void reset(const Substitution& sigma0) override; + + virtual ~NotQuery() override; }; class AndQuery : public Query { private: - Query& q1_; - Query& q2_; + Query* q1_; + Query* q2_; bool first_fixed_; public: - AndQuery(Query& q1, Query& q2); + AndQuery(Query* q1, Query* q2); + + virtual std::string to_str() const override; virtual bool next_solution() override; virtual const Substitution& get_solution() const override { - return q2_.get_solution(); + return q2_->get_solution(); } virtual const Substitution& get_sigma0() const override { - return q1_.get_sigma0(); + return q1_->get_sigma0(); } virtual void reset(const Substitution& sigma0) override { - q1_.reset(sigma0); + q1_->reset(sigma0); } + virtual ~AndQuery() override; + +}; + +class QuerySpecification : public Stringifiable +{ + public: + virtual Query* operator()(const PlanState& state, + const Substitution& sigma0=Substitution()) const=0; + + virtual ~QuerySpecification() {} +}; + +class SimpleQuerySpecification : public QuerySpecification +{ + private: + Predicate pattern_; + + public: + virtual std::string to_str() const override; + + SimpleQuerySpecification(const Predicate& pattern); + + virtual Query* operator()(const PlanState& state, + const Substitution& sigma0=Substitution()) const override; +}; + +class NotQuerySpecification : public QuerySpecification +{ + private: + QuerySpecification* query_; + + public: + virtual std::string to_str() const override; + + NotQuerySpecification(QuerySpecification* query); + + virtual Query* operator()(const PlanState& state, + const Substitution& sigma0=Substitution()) const override; + + virtual ~NotQuerySpecification() override; +}; + +class AndQuerySpecification : public QuerySpecification +{ + private: + QuerySpecification* q1_; + QuerySpecification* q2_; + public: + virtual std::string to_str() const override; + + AndQuerySpecification(QuerySpecification* q1, QuerySpecification* q2); + + virtual Query* operator()(const PlanState& state, + const Substitution& sigma0=Substitution()) const override; + + virtual ~AndQuerySpecification() override; }; } /* end namespace imagine-planner */