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