diff --git a/src/examples/parsing_test.cpp b/src/examples/parsing_test.cpp index 04b3c87aaea5d761825c114e63bb56f3f217a688..e7b3ccdb6e7c6169cf416054ea7aaf0f07095f0e 100644 --- a/src/examples/parsing_test.cpp +++ b/src/examples/parsing_test.cpp @@ -90,6 +90,8 @@ BOOST_AUTO_TEST_CASE(test_parse_complex_expressions) std::string str2("at(X, Y), at(Z, Y), !=(boat, X)"); std::string str3("fixes(X, Y), \\+(precedes(_1, X), occluded_by(X, _2))"); std::string str4("\\+(\\+q(X), \\+q(Y), \\+r())"); + std::string str5("(\\+q(X), q(Y)); \\+r()"); + std::string str6("\\+(\\+q(X); \\+q(Y)), \\+r()"); Query::Ptr q1 = parse_query(str1); BOOST_REQUIRE(q1); BOOST_CHECK_EQUAL(q1->to_str(), "at(X,Y),at(Z,Y)"); @@ -102,6 +104,12 @@ BOOST_AUTO_TEST_CASE(test_parse_complex_expressions) Query::Ptr q4 = parse_query(str4); BOOST_REQUIRE(q4); BOOST_CHECK_EQUAL(q4->to_str(), "\\+(\\+q(X),\\+q(Y),\\+r())"); + Query::Ptr q5 = parse_query(str5); + BOOST_REQUIRE(q5); + BOOST_CHECK_EQUAL(q5->to_str(), "(\\+q(X),q(Y));\\+r()"); + Query::Ptr q6 = parse_query(str6); + BOOST_REQUIRE(q6); + BOOST_CHECK_EQUAL(q6->to_str(), "\\+(\\+q(X);\\+q(Y)),\\+r()"); } BOOST_AUTO_TEST_CASE(test_parse_effects) diff --git a/src/examples/queries_test.cpp b/src/examples/queries_test.cpp index 1922ed251af65411c52df346217a158cfe3bcd50..b1f2be77464ca792b0990ad81720f2e847b726b5 100644 --- a/src/examples/queries_test.cpp +++ b/src/examples/queries_test.cpp @@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE(simple_query_test_sat) { auto state = PlanState::create({Predicate("p","a","b"), Predicate("p","a","c"), Predicate("p","d","a"), Predicate("q","w"), Predicate("q", "s")}); - INFO(state); + INFO(*state); Query::Ptr q1 = SimpleQuery(Predicate("p", "a", "X")).bind(state); Query::Ptr q2 = SimpleQuery(Predicate("q", "w")).bind(state); INFO("q1: " << *q1 << ", q2: " << *q2); @@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(simple_query_test_unsat) { auto state = PlanState::create({Predicate("p","a","b"), Predicate("p","a","c"), Predicate("p","d","a"), Predicate("q","w"), Predicate("q", "s")}); - INFO(state); + INFO(*state); Query::Ptr q1 = SimpleQuery(Predicate("p", "c", "X")).bind(state); Query::Ptr q2 = SimpleQuery(Predicate("p", "a", "d")).bind(state); INFO("q1: " << *q1 << ", q2: " << *q2); @@ -87,7 +87,7 @@ BOOST_AUTO_TEST_CASE(not_query_test) { auto state = PlanState::create({Predicate("p","a","b"), Predicate("p","a","c"), Predicate("p","d","a"), Predicate("q","w"), Predicate("q", "s")}); - INFO(state); + INFO(*state); Query::Ptr q1 = std::make_shared<SimpleQuery>(Predicate("p", "a", "X")); Query::Ptr q2 = std::make_shared<SimpleQuery>(Predicate("p", "Z", "d")); Query::Ptr q1neg = NotQuery(q1).bind(state); @@ -101,7 +101,7 @@ BOOST_AUTO_TEST_CASE(and_query_test_sat) { auto state = PlanState::create({Predicate("p","a","b"), Predicate("p","a","c"), Predicate("p", "b", "d"), Predicate("p","d","a"), Predicate("q","b")}); - INFO("state: " << state); + INFO("state: " << *state); Query::Ptr q1 = std::make_shared<SimpleQuery>(Predicate("p", "X", "Y")); Query::Ptr q2 = std::make_shared<SimpleQuery>(Predicate("q", "X")); Query::Ptr not1 = std::make_shared<NotQuery>(q2); @@ -116,3 +116,36 @@ BOOST_AUTO_TEST_CASE(and_query_test_sat) BOOST_REQUIRE(not and1->next_solution()); } +BOOST_AUTO_TEST_CASE(or_query_test_sat) +{ + auto state = PlanState::create({Predicate("p","a","b"), Predicate("p","a","c"), + Predicate("p", "b", "d"), Predicate("p","d","a"), Predicate("q","b")}); + INFO("state: " << *state); + Query::Ptr q1 = std::make_shared<SimpleQuery>(Predicate("p", "X", "X")); + Query::Ptr q2 = std::make_shared<SimpleQuery>(Predicate("q", "X")); + Query::Ptr q3 = std::make_shared<SimpleQuery>(Predicate("p", "X", "_")); + Query::Ptr or1 = OrQuery(q1, q2).bind(state); + Query::Ptr or2 = OrQuery(q1, q3).bind(state); + INFO("or1: " << *or1); + INFO("or2: " << *or2); + BOOST_REQUIRE(or1->next_solution()); + BOOST_CHECK_EQUAL(or1->get_solution().to_str(), "{}"); + BOOST_REQUIRE(not or1->next_solution()); + BOOST_REQUIRE(or2->next_solution()); + BOOST_CHECK_EQUAL(or2->get_solution().to_str(), "{}"); + BOOST_REQUIRE(not or2->next_solution()); +} + +BOOST_AUTO_TEST_CASE(or_query_test_unsat) +{ + auto state = PlanState::create({Predicate("p","a","b"), Predicate("p","a","c"), + Predicate("p", "b", "d"), Predicate("p","d","a"), Predicate("q","b")}); + INFO("state: " << *state); + Query::Ptr q1 = std::make_shared<SimpleQuery>(Predicate("p", "X", "X")); + Query::Ptr q2 = std::make_shared<SimpleQuery>(Predicate("q", "b")); + Query::Ptr not1 = std::make_shared<NotQuery>(q2); + Query::Ptr or1 = OrQuery(q1, not1).bind(state); + INFO("or1: " << *or1); + BOOST_REQUIRE(not or1->next_solution()); +} + diff --git a/src/parsing.cpp b/src/parsing.cpp index c8fdb1c5a23bed590bead1e4a2739c168f6cb181..d00a3f4ae8e650f729fa140fd0fcd5aa3060697c 100644 --- a/src/parsing.cpp +++ b/src/parsing.cpp @@ -62,6 +62,11 @@ TokenV decompose(const std::string& in) tokens.push_back({"COMMA", ""}); ++cursor; } + else if (in_clean[cursor] == ';') + { + tokens.push_back({"SEMICOLON", ""}); + ++cursor; + } else { std::size_t end = in_clean.find_first_of(",()\\", cursor); @@ -153,7 +158,8 @@ Predicate::Ptr parse_predicate(const TokenV& tokens, std::size_t& cursor) Query::Ptr parse_query(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}; + EXPECTING_PAR_CLOSE, EXPECTING_PUNCT_OR_END, EXPECTING_AND_2ND, + EXPECTING_OR_2ND, OK, KO}; State state = EXPECTING_PRED_OR_PAR; bool negated = false; Query::Ptr query = nullptr; @@ -181,7 +187,7 @@ Query::Ptr parse_query(const TokenV& tokens, std::size_t& cursor) query = std::make_shared<SimpleQuery>(*predicate); } if (negated) query = std::make_shared<NotQuery>(query); - state = EXPECTING_COMMA_OR_END; + state = EXPECTING_PUNCT_OR_END; } else if (token.first == "PAR_OPEN") { @@ -201,26 +207,37 @@ Query::Ptr parse_query(const TokenV& tokens, std::size_t& cursor) case EXPECTING_PAR_CLOSE: if (token.first == "PAR_CLOSE") { - state = EXPECTING_COMMA_OR_END; + state = EXPECTING_PUNCT_OR_END; ++idx; } else state = KO; break; - case EXPECTING_COMMA_OR_END: + case EXPECTING_PUNCT_OR_END: if (token.first == "COMMA") { - state = EXPECTING_END_EXPR; + state = EXPECTING_AND_2ND; + ++idx; + } + else if (token.first == "SEMICOLON") + { + state = EXPECTING_OR_2ND; ++idx; } else state = OK; break; - case EXPECTING_END_EXPR: + case EXPECTING_AND_2ND: if (Query::Ptr query_snd = parse_query(tokens, idx)) { query = std::make_shared<AndQuery>(query, query_snd); state = OK; } break; + case EXPECTING_OR_2ND: + if (Query::Ptr query_snd = parse_query(tokens, idx)) + { + query = std::make_shared<OrQuery>(query, query_snd); + state = OK; + } default: /* do nothing */ break; diff --git a/src/queries.cpp b/src/queries.cpp index 58bbed3ea490004b24eec21fe3c52202f991903e..1bae8afe02037723a20996f25a4c950243289478 100644 --- a/src/queries.cpp +++ b/src/queries.cpp @@ -122,7 +122,8 @@ bool SimpleQuery::unify(const Predicate& goal) std::string NotQuery::to_str() const { - if (std::dynamic_pointer_cast<const AndQuery>(neg_)) + if (std::dynamic_pointer_cast<const AndQuery>(neg_) or + std::dynamic_pointer_cast<const OrQuery>(neg_)) { return std::string("\\+(") + neg_->to_str() + ')'; } @@ -140,7 +141,7 @@ bool NotQuery::next_solution() Query::Ptr NotQuery::bind(const PlanState::Ptr& state) const { return std::make_shared<NotQuery>(neg_->bind(state)); -} +} void NotQuery::reset(const Substitution& sigma0) { @@ -150,11 +151,26 @@ void NotQuery::reset(const Substitution& sigma0) } -//// AndQuery methods +// AndQuery methods std::string AndQuery::to_str() const { - return q1_->to_str() + ',' + q2_->to_str(); + std::string fst_part = q1_->to_str(), snd_part = q2_->to_str(); + if (not (std::dynamic_pointer_cast<const AndQuery>(q1_) or + std::dynamic_pointer_cast<const ComparisonQuery>(q1_) or + std::dynamic_pointer_cast<const SimpleQuery>(q1_) or + std::dynamic_pointer_cast<const NotQuery>(q1_))) + { + fst_part = '(' + fst_part + ')'; + } + if (not (std::dynamic_pointer_cast<const AndQuery>(q2_) or + std::dynamic_pointer_cast<const ComparisonQuery>(q2_) or + std::dynamic_pointer_cast<const SimpleQuery>(q2_) or + std::dynamic_pointer_cast<const NotQuery>(q2_))) + { + snd_part = '(' + snd_part + ')'; + } + return fst_part + ',' + snd_part; } bool AndQuery::next_solution() @@ -186,6 +202,51 @@ Query::Ptr AndQuery::bind(const PlanState::Ptr& state) const void AndQuery::reset(const Substitution& sigma0) { q1_->reset(sigma0); + first_fixed_ = false; +} + +// OrQuery methods + +std::string OrQuery::to_str() const +{ + std::string fst_part = q1_->to_str(), snd_part = q2_->to_str(); + if (not (std::dynamic_pointer_cast<const OrQuery>(q1_) or + std::dynamic_pointer_cast<const ComparisonQuery>(q1_) or + std::dynamic_pointer_cast<const SimpleQuery>(q1_) or + std::dynamic_pointer_cast<const NotQuery>(q1_))) + { + fst_part = '(' + fst_part + ')'; + } + if (not (std::dynamic_pointer_cast<const OrQuery>(q2_) or + std::dynamic_pointer_cast<const ComparisonQuery>(q2_) or + std::dynamic_pointer_cast<const SimpleQuery>(q2_) or + std::dynamic_pointer_cast<const NotQuery>(q2_))) + { + snd_part = '(' + snd_part + ')'; + } + return fst_part + ';' + snd_part; +} + +bool OrQuery::next_solution() +{ + if (done_) return false; + done_ = true; + if (q1_->next_solution()) return true; + q2_->reset(sigma_); // reset q2_ lazily + return q2_->next_solution(); +} + +Query::Ptr OrQuery::bind(const PlanState::Ptr& state) const +{ + return std::make_shared<OrQuery>(q1_->bind(state), q2_->bind(state)); +} + +void OrQuery::reset(const Substitution& sigma0) +{ + q1_->reset(sigma0); + // no need to reset q2_ now, we can wait until the first query fails. + sigma_ = sigma0; + done_ = false; } } diff --git a/src/queries.h b/src/queries.h index c117c6af585dc123fde252d1b2826c3682dc973f..d81e133dd880368ef5a0edddf17ed21ec49e3a02 100644 --- a/src/queries.h +++ b/src/queries.h @@ -11,6 +11,7 @@ class ComparisonQuery; class SimpleQuery; class NotQuery; class AndQuery; +class OrQuery; class Query : public Stringifiable @@ -18,6 +19,7 @@ class Query : public Stringifiable public: typedef std::shared_ptr<Query> Ptr; + typedef std::shared_ptr<const Query> ConstPtr; virtual bool next_solution() =0; @@ -42,6 +44,7 @@ class ComparisonQuery : public Query public: typedef std::shared_ptr<ComparisonQuery> Ptr; + typedef std::shared_ptr<const ComparisonQuery> ConstPtr; ComparisonQuery(const Predicate& cmp); @@ -65,6 +68,7 @@ class SimpleQuery : public Query public: typedef std::shared_ptr<SimpleQuery> Ptr; + typedef std::shared_ptr<const SimpleQuery> ConstPtr; SimpleQuery(const Predicate& pattern) : pattern_(pattern) {} @@ -86,11 +90,13 @@ class SimpleQuery : public Query bool unify(const Predicate& goal); }; + class NotQuery : public Query { public: typedef std::shared_ptr<NotQuery> Ptr; + typedef std::shared_ptr<const NotQuery> ConstPtr; NotQuery(const Query::Ptr& neg) : neg_(neg), done_(false) {} @@ -108,11 +114,13 @@ class NotQuery : public Query bool done_; }; + class AndQuery : public Query { public: - typedef std::shared_ptr<NotQuery> Ptr; + typedef std::shared_ptr<AndQuery> Ptr; + typedef std::shared_ptr<const AndQuery> ConstPtr; AndQuery(const Query::Ptr& q1, const Query::Ptr& q2) : q1_(q1), q2_(q2), first_fixed_(false) {} @@ -131,6 +139,33 @@ class AndQuery : public Query bool first_fixed_; }; + +class OrQuery : public Query +{ + public: + + typedef std::shared_ptr<OrQuery> Ptr; + typedef std::shared_ptr<const OrQuery> ConstPtr; + + OrQuery(const Query::Ptr& q1, const Query::Ptr& q2) : + q1_(q1), q2_(q2), done_(false) {} + + virtual std::string to_str() const override; + + virtual bool next_solution() override; + + Query::Ptr bind(const PlanState::Ptr& state) const override; + + virtual void reset(const Substitution& sigma0) override; + + private: + + Query::Ptr q1_, q2_; + bool done_; + +}; + + } /* end namespace imagine-planner */ #endif