diff --git a/libwccl/CMakeLists.txt b/libwccl/CMakeLists.txt
index 398687b30d54b1d6f83d93eb91965c32245be8e4..3027aeee0356e9bfac135891afd256ce5b307159 100644
--- a/libwccl/CMakeLists.txt
+++ b/libwccl/CMakeLists.txt
@@ -16,6 +16,7 @@ set(LIBS ${LIBS} ${Boost_LIBRARIES})
 SET(libwccl_STAT_SRC
 	exception.cpp
 	main.cpp
+	ops/logicalpredicate.cpp
 	ops/predicate.cpp
 	sentencecontext.cpp
 	values/bool.cpp
diff --git a/libwccl/ops/logicalpredicate.cpp b/libwccl/ops/logicalpredicate.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..750f1445eb32e084d7a2d206d12858ac7be0b705
--- /dev/null
+++ b/libwccl/ops/logicalpredicate.cpp
@@ -0,0 +1,35 @@
+#include <libwccl/ops/logicalpredicate.h>
+
+namespace Wccl {
+
+std::string LogicalPredicate::to_string(const Corpus2::Tagset& tagset) const
+{
+	std::string s(operator_name(tagset));
+	s.append("(");
+	BoolFunctionPtrVector::const_iterator it = expressions_->begin();
+	while(it != expressions_->end()) {
+		s.append((*it)->to_string(tagset));
+		if(++it != expressions_->end()) {
+			s.append(", ");
+		}
+	}
+	s.append(")");
+	return s;
+}
+
+std::string LogicalPredicate::to_raw_string() const
+{
+	std::string s(raw_operator_name());
+	s.append("(");
+	BoolFunctionPtrVector::const_iterator it = expressions_->begin();
+	while(it != expressions_->end()) {
+		s.append((*it)->to_raw_string());
+		if(++it != expressions_->end()) {
+			s.append(", ");
+		}
+	}
+	s.append(")");
+	return s;
+}
+
+} /* end ns Wccl */
diff --git a/libwccl/ops/logicalpredicate.h b/libwccl/ops/logicalpredicate.h
new file mode 100644
index 0000000000000000000000000000000000000000..60427e32f816066326b3fcdfe8de550fcfc0650b
--- /dev/null
+++ b/libwccl/ops/logicalpredicate.h
@@ -0,0 +1,48 @@
+#ifndef LOGICALPREDICATE_H
+#define LOGICALPREDICATE_H
+
+#include <vector>
+#include <boost/shared_array.hpp>
+#include <boost/assert.hpp>
+#include <libcorpus2/tagset.h>
+
+#include <libwccl/ops/predicate.h>
+
+namespace Wccl {
+/**
+ * Abstract base class for predicates which are logical functions
+ * (over any number of arguments)
+ */
+class LogicalPredicate : public Predicate {
+public:
+	typedef boost::shared_ptr<Function<Bool> > BoolFunctionPtr;
+	typedef std::vector<BoolFunctionPtr> BoolFunctionPtrVector;
+
+	LogicalPredicate(const boost::shared_ptr<BoolFunctionPtrVector>& expressions)
+		: expressions_(expressions)
+	{
+		BOOST_ASSERT(expressions_);
+		BOOST_ASSERT(expressions_->size() > 0);
+	}
+
+	/**
+	 * String representation of the logical predicate, realised by default
+	 * as "operator_name(expr1_string, ..., exprn_string)"
+	 */
+	virtual std::string to_string(const Corpus2::Tagset& tagset) const;
+
+	/**
+	 * String representation of the logical predicate, realised by default
+	 * as "raw_operator_name(raw_expr1_string, ..., raw_exprn_string)"
+	 * (this version doesn't require tagset, but may be incomplete and/or
+	 * contain internal info)
+	 */
+	virtual std::string to_raw_string() const;
+
+protected:
+	const boost::shared_ptr<BoolFunctionPtrVector> expressions_;
+};
+
+} /* end ns Wccl */
+
+#endif // LOGICALPREDICATE_H