diff --git a/libwccl/ops/functions/bool/iterations/leftlook.cpp b/libwccl/ops/functions/bool/iterations/leftlook.cpp
index 57cd4a8dd7d42e5d059b2bc2b73773d90625757d..d1a80282735c953fa4b714e79914fa7e2b589546 100644
--- a/libwccl/ops/functions/bool/iterations/leftlook.cpp
+++ b/libwccl/ops/functions/bool/iterations/leftlook.cpp
@@ -8,7 +8,7 @@ bool LeftLook::iterate(
 	Wccl::Position &p,
 	const Wccl::FunExecContext &context) const
 {
-	for(int i = left; i <= right; ++i) {
+	for(int i = right; i >= left; --i) {
 		p.set_value(i);
 		if(evaluating_expr_->apply(context)->get_value()) {
 			return true;
diff --git a/libwccl/ops/functions/bool/iterations/leftlook.h b/libwccl/ops/functions/bool/iterations/leftlook.h
index 5c07a39c3c828e5d85c14810830ca375536e56cd..92e42dab29581b899b855639df708443495f9593 100644
--- a/libwccl/ops/functions/bool/iterations/leftlook.h
+++ b/libwccl/ops/functions/bool/iterations/leftlook.h
@@ -6,8 +6,9 @@
 namespace Wccl {
 
 /**
- * Iterative operator "llook", which looks for a first
- * position from left that makes evaluating expression return true.
+ * Iterative operator "llook", which looks for the rightmost position
+ * in a range that makes the evaluating expression return true, i.e.
+ * it "looks to the left".
  */
 class LeftLook : public Iteration
 {
@@ -30,12 +31,12 @@ public:
 
 protected:
 	/**
-	 * @returns True if, when scanning from left,
-	 * a position within range is found that makes
+	 * @returns True if, when scanning right-to-left,
+	 * a position within the range is found that makes
 	 * the evaluating function return true. False
 	 * otherwise.
 	 * @note Upon success, the iteration variable is
-	 * set to the first position in range from left
+	 * set to the rightmost position in range
 	 * that has made evaluation function return true
 	 * (that is the position that made the stop condition
 	 * true in this case).
diff --git a/libwccl/ops/functions/bool/iterations/rightlook.cpp b/libwccl/ops/functions/bool/iterations/rightlook.cpp
index 36bae48af8cdb939d6dcf8f48dc414a2b0805d30..70b4cdae1dc7fcbe03e4154d19df1cfbd0c29009 100644
--- a/libwccl/ops/functions/bool/iterations/rightlook.cpp
+++ b/libwccl/ops/functions/bool/iterations/rightlook.cpp
@@ -8,7 +8,7 @@ bool RightLook::iterate(
 	Wccl::Position &p,
 	const Wccl::FunExecContext &context) const
 {
-	for(int i = right; i >= left; --i) {
+	for(int i = left; i <= right; ++i) {
 		p.set_value(i);
 		if(evaluating_expr_->apply(context)->get_value()) {
 			return true;
diff --git a/libwccl/ops/functions/bool/iterations/rightlook.h b/libwccl/ops/functions/bool/iterations/rightlook.h
index b39251affb8f3defb9c9b2d81c60c3dcf17518bc..dcc5e5fa722ef007c0a14e3d583d165f40f32a85 100644
--- a/libwccl/ops/functions/bool/iterations/rightlook.h
+++ b/libwccl/ops/functions/bool/iterations/rightlook.h
@@ -6,8 +6,9 @@
 namespace Wccl {
 
 /**
- * Iterative operator "rlook", which looks for a first
- * position from right that makes evaluating expression return true.
+ * Iterative operator "rlook", which looks for the leftmost position
+ * in a range that makes the evaluating expression return true, i.e.
+ * it "looks to the right".
  */
 class RightLook : public Iteration
 {
@@ -30,12 +31,12 @@ public:
 
 protected:
 	/**
-	 * @returns True if, when scanning from right,
-	 * a position within range is found that makes
+	 * @returns True if, when scanning left-to-right,
+	 * a position within the range is found that makes
 	 * the evaluating function return true. False
 	 * otherwise.
 	 * @note Upon success, the iteration variable is
-	 * set to the first position in range from right
+	 * set to the leftmost position in range
 	 * that has made evaluation function return true
 	 * (that is the position that made the stop condition
 	 * true in this case).
diff --git a/libwccl/ops/functions/bool/predicates/pointagreement.h b/libwccl/ops/functions/bool/predicates/pointagreement.h
index b9b58e910e707f0d821b571f14e3114174769765..cefa1f7c25058cf0381be5421528a9aed9163c25 100644
--- a/libwccl/ops/functions/bool/predicates/pointagreement.h
+++ b/libwccl/ops/functions/bool/predicates/pointagreement.h
@@ -19,10 +19,12 @@ public:
 	PointAgreement(
 		const PosFunctionPtr& pos1_expr,
 		const PosFunctionPtr& pos2_expr,
-		const TSetFunctionPtr& attribs_expr)
+		const TSetFunctionPtr& attribs_expr,
+		const Corpus2::Tagset& tagset)
 		: pos1_expr_(pos1_expr),
 		  pos2_expr_(pos2_expr),
-		  attribs_expr_(attribs_expr)
+		  attribs_expr_(attribs_expr),
+		  tagset_(tagset)
 	{
 		BOOST_ASSERT(pos1_expr_);
 		BOOST_ASSERT(pos2_expr_);
@@ -45,6 +47,7 @@ protected:
 	const PosFunctionPtr pos1_expr_;
 	const PosFunctionPtr pos2_expr_;
 	const TSetFunctionPtr attribs_expr_;
+	const Corpus2::Tagset& tagset_;
 
 	/**
 	 * Gets two positions from arguments. If either of them
diff --git a/libwccl/ops/functions/bool/predicates/strongagreement.h b/libwccl/ops/functions/bool/predicates/strongagreement.h
index c59ee02a08dcfa47736cf938aff6920063cbe804..83c9dd630e1645936a6aebf05af67e12ce6c1478 100644
--- a/libwccl/ops/functions/bool/predicates/strongagreement.h
+++ b/libwccl/ops/functions/bool/predicates/strongagreement.h
@@ -18,10 +18,12 @@ public:
 	StrongAgreement(
 		const PosFunctionPtr& left_pos_expr,
 		const PosFunctionPtr& right_pos_expr,
-		const TSetFunctionPtr& attribs_expr)
+		const TSetFunctionPtr& attribs_expr,
+		const Corpus2::Tagset& tagset)
 		: left_pos_expr_(left_pos_expr),
 		  right_pos_expr_(right_pos_expr),
-		  attribs_expr_(attribs_expr)
+		  attribs_expr_(attribs_expr),
+		  tagset_(tagset)
 	{
 		BOOST_ASSERT(left_pos_expr_);
 		BOOST_ASSERT(right_pos_expr_);
@@ -45,6 +47,7 @@ protected:
 	const PosFunctionPtr left_pos_expr_;
 	const PosFunctionPtr right_pos_expr_;
 	const TSetFunctionPtr attribs_expr_;
+	const Corpus2::Tagset& tagset_;
 
 	/**
 	 * Gets start and end positions for range from arguments.
diff --git a/libwccl/ops/functions/bool/predicates/weakagreement.h b/libwccl/ops/functions/bool/predicates/weakagreement.h
index f3f58838001b3032875a685a48414c98b510c305..e4a47ab7ab14791210709439cd6c1c9aee150dbb 100644
--- a/libwccl/ops/functions/bool/predicates/weakagreement.h
+++ b/libwccl/ops/functions/bool/predicates/weakagreement.h
@@ -18,10 +18,12 @@ public:
 	WeakAgreement(
 		const PosFunctionPtr& left_pos_expr,
 		const PosFunctionPtr& right_pos_expr,
-		const TSetFunctionPtr& attribs_expr)
+		const TSetFunctionPtr& attribs_expr,
+		const Corpus2::Tagset& tagset)
 		: left_pos_expr_(left_pos_expr),
 		  right_pos_expr_(right_pos_expr),
-		  attribs_expr_(attribs_expr)
+		  attribs_expr_(attribs_expr),
+		  tagset_(tagset)
 	{
 		BOOST_ASSERT(left_pos_expr_);
 		BOOST_ASSERT(right_pos_expr_);
@@ -45,6 +47,7 @@ protected:
 	const PosFunctionPtr left_pos_expr_;
 	const PosFunctionPtr right_pos_expr_;
 	const TSetFunctionPtr attribs_expr_;
+	const Corpus2::Tagset& tagset_;
 
 	/**
 	 * Gets start and end positions for range from arguments.
diff --git a/libwccl/ops/functions/tset/agrfilter.h b/libwccl/ops/functions/tset/agrfilter.h
index f86f3fda842938e718fbd5fc25b597ce6a8c9942..d41d4c63980b29d0c676071e8b206745ffb351e2 100644
--- a/libwccl/ops/functions/tset/agrfilter.h
+++ b/libwccl/ops/functions/tset/agrfilter.h
@@ -22,11 +22,13 @@ public:
 			const PosFunctionPtr& left_pos_expr,
 			const PosFunctionPtr& right_pos_expr,
 			const TSetFunctionPtr& attribs_expr,
-			const TSetFunctionPtr& mask_expr)
+			const TSetFunctionPtr& mask_expr,
+			const Corpus2::Tagset& tagset)
 		: left_pos_expr_(left_pos_expr),
 		  right_pos_expr_(right_pos_expr),
 		  attribs_expr_(attribs_expr),
-		  mask_expr_(mask_expr)
+		  mask_expr_(mask_expr),
+		  tagset_(tagset)
 	{
 		BOOST_ASSERT(left_pos_expr_);
 		BOOST_ASSERT(right_pos_expr_);
@@ -54,7 +56,7 @@ protected:
 	const TSetFunctionPtr attribs_expr_;
 	const TSetFunctionPtr mask_expr_;
 
-
+	const Corpus2::Tagset& tagset_;
 
 	/**
 	 * Gets range specified via Position expressions. Trims the range
diff --git a/libwccl/parser/grammar.g b/libwccl/parser/grammar.g
index 49c60a52d61bc14bf9ba91604a5a22993fbdc2bf..bb69381aaf068285e21ec11b8075da40e91e2db8 100644
--- a/libwccl/parser/grammar.g
+++ b/libwccl/parser/grammar.g
@@ -1098,9 +1098,9 @@ bool_iteration
 		RPAREN {
 			ret.reset(new AtLeast(lpos, rpos, *pacc, expr, min_match));
 		}
-	| "llook" LPAREN
-			lpos = position_operator [tagset, vars] COMMA 
+	| "llook" LPAREN //note inverted rpos/lpos order
 			rpos = position_operator [tagset, vars] COMMA
+			lpos = position_operator [tagset, vars] COMMA
 			pacc = position_variable_acc [vars]     COMMA
 			expr = bool_operator     [tagset, vars] 
 		RPAREN {
diff --git a/libwccl/values/value.h b/libwccl/values/value.h
index efd92cb7f0cc5d726e8396f0f28f5b66f71b9836..f4048c04e298b78d9d2e80794af0bacc291a2de3 100644
--- a/libwccl/values/value.h
+++ b/libwccl/values/value.h
@@ -6,7 +6,10 @@
 #define WCCL_VALUE_PREAMBLE \
 static const char* type_name; \
 const char* get_type_name() const { return type_name; } \
-static std::string var_repr(const std::string &var_name);
+static std::string var_repr(const std::string &var_name); \
+std::string make_var_repr(const std::string &var_name) const { \
+	return var_repr(var_name); \
+}
 
 
 namespace Wccl {
@@ -35,6 +38,8 @@ public:
 
 	virtual ~Value() {}
 
+	virtual std::string make_var_repr(const std::string&) const = 0;
+
 	/**
 	 * String representation function, in general, a tagset is required,
 	 * but some classes might not need that, so by default just forward
diff --git a/tests/data/example.ccl b/tests/data/example.ccl
index bdabf498699f011abd580574685a2e2e47fcaa0c..3cbd81c9de6d0a878510c558a04eb8a416f11bbf 100644
--- a/tests/data/example.ccl
+++ b/tests/data/example.ccl
@@ -4,6 +4,7 @@ After the --- line comes the first test -- the operator string, followed by an e
 If the first operator line is "position=XXX", position is parsed as an int and that is used as the current position in the sentence for this and any following tests. Default position is 0.
 Tests are separated from one another by a --- line.
 This test loads no sentence, but can change the position anyway. Tagset is kipi by default.
+Variable state can be checked by a name=value lines after the expected output.
 tagset=ikipi
 ---
 equal(["aaa"], "aaa")
@@ -13,3 +14,10 @@ True
 lower(["A"])
 
 ["a"]
+---
+setvar($b:A, setvar($t:B, subst))
+
+True
+A=True
+B={subst}
+
diff --git a/tests/data/iterations.ccl b/tests/data/iterations.ccl
index 60b5052c329cc2bd79e4c686729ce0c0f652da65..f50cd485c013ffd404e6627447eb7ee50440dd3a 100644
--- a/tests/data/iterations.ccl
+++ b/tests/data/iterations.ccl
@@ -1,6 +1,90 @@
 tagset=kipi
+sentence=t01.xml
 ---
 rlook(begin, end, $Test, True)
 
 True
 ---
+llook(0, end, $V, False)
+
+False
+V=nowhere
+---
+position=0
+rlook(0,end,$P,
+	inter(class[$P], {subst})
+)
+
+True
+P=3
+---
+position=3
+rlook(0,end,$P,
+	inter(class[$P], {subst})
+)
+
+True
+P=0
+---
+position=4
+rlook(0,end,$P,
+	inter(class[$P], {subst})
+)
+
+True
+P=8
+---
+position=19
+rlook(0,end,$P,inter(class[$P], {subst}))
+
+False
+P=nowhere
+---
+position=26
+llook(0,begin,$NomAcc, equal(cas[$NomAcc],{nom,acc}))
+
+True
+NomAcc=-1
+---
+position=12
+llook(0,begin,$NomAcc, equal(cas[$NomAcc],{nom,acc}))
+
+True
+NomAcc=-4
+---
+position=2
+llook(0,begin,$NomAcc, equal(cas[$NomAcc],{nom,acc}))
+
+False
+NomAcc=nowhere
+---
+position=0
+only(10,12,$Loc, in(loc, cas[$Loc]))
+
+True
+Loc=12
+---
+position=0
+only(10,13,$Loc, in(loc, cas[$Loc]))
+
+False
+Loc=nowhere
+---
+position=0
+atleast(9,13,$Loc, in(loc, cas[$Loc]), 3)
+
+True
+Loc=12
+---
+position=0
+atleast(9,13,$Loc, in(loc, cas[$Loc]), 4)
+
+False
+Loc=nowhere
+---
+position=-1
+llook(0,begin,$NomAcc, equal(cas[$NomAcc],{nom,acc}))
+
+False
+NomAcc=nowhere
+---
diff --git a/tests/datadriven.cpp b/tests/datadriven.cpp
index 5b62d31a324a2038455140271402a69a8490bde3..c39054982f43b3c8a9eb5c9e4a448eeda4ff3da8 100644
--- a/tests/datadriven.cpp
+++ b/tests/datadriven.cpp
@@ -98,7 +98,24 @@ void test_one_item_actual(const compare_test& c)
 			expected_output = "";
 			operator_string = "";
 			++line_no;
-			std::getline(ifs_in, line);
+			while (ifs_in.good() && line != "---" && line != "") {
+				std::getline(ifs_in, line);
+				std::vector<std::string> fields;
+				boost::algorithm::split(fields, line, boost::is_any_of(separators));
+				if (fields.size() == 2) {
+					boost::shared_ptr<Wccl::Value> v;
+					v = fu.variables()->get<Wccl::Value>(fields[0]);
+					if (!v) {
+						BOOST_ERROR("Invalid variable name in test: "
+							<< fields[0] << " on line " << line_no);
+					} else if (v->to_string(tagset) != fields[1]) {
+						BOOST_ERROR("Variable " << fields[0]
+							<< " value mismatch on line "
+							<< line_no << "\n: expected " << fields[1]
+							<< " got " << v->to_string(tagset));
+					}
+				}
+			}
 			BOOST_REQUIRE(line == "---" || line == "");
 		} else {
 			if (operator_string.empty() && line.substr(0, 9) == "position=") {
diff --git a/wcclparser/main.cpp b/wcclparser/main.cpp
index 2ef8976a2e02cb7e22454de13d61b6888cc3791c..4df9c59eb70490eaab65c868df74a43be780caa8 100644
--- a/wcclparser/main.cpp
+++ b/wcclparser/main.cpp
@@ -90,8 +90,17 @@ void libedit_read_loop(boost::function<bool (const std::string&)>& line_cb)
 }
 #endif
 
+void dumpvariables(const Wccl::Variables& vars, const Corpus2::Tagset& tagset)
+{
+	typedef std::pair<std::string, boost::shared_ptr<Wccl::Value> > v_t;
+	foreach (const v_t& v, vars.get_all<Wccl::Value>()) {
+		std::cerr << v.second->make_var_repr(v.first) << "="
+			<< v.second->to_string(tagset) << "\n";
+	}
+}
+
 bool process_line(const std::string& line, Wccl::Parser& parser,
-	Wccl::SentenceContext& sc, bool all_positions)
+	Wccl::SentenceContext& sc, bool all_positions, bool dump_variables)
 {
 	if (line.empty() || line == "exit" || line == "quit") {
 		return true;
@@ -123,6 +132,9 @@ bool process_line(const std::string& line, Wccl::Parser& parser,
 						<< "Parsed expression: "
 						<< retVal->to_string(parser.tagset())
 						<< std::endl;
+					if (dump_variables) {
+						dumpvariables(*cx.variables(), parser.tagset());
+					}
 				} else {
 					std::cerr << "Problem while parsing -- "
 						<< "retVal is NULL!" << std::endl;
@@ -156,6 +168,7 @@ int main(int argc, char** argv)
 	std::string query_load = "";
 	std::string position = "0";
 	bool quiet = false;
+	bool dump_variables = false;
 	using boost::program_options::value;
 
 	boost::program_options::options_description desc("Allowed options");
@@ -172,6 +185,8 @@ int main(int argc, char** argv)
 			 "Query to run (disables interactive mode)\n")
 			("quiet,q", value(&quiet)->zero_tokens(),
 			 "Suppress messages\n")
+			("variables,V", value(&dump_variables)->zero_tokens(),
+			 "Dump variables after running each query\n")
 			("help,h", "Show help")
 			;
 	boost::program_options::variables_map vm;
@@ -214,14 +229,14 @@ int main(int argc, char** argv)
 		sc.set_position(pos);
 		Wccl::Parser parser(tagset);
 		if (!query.empty()) {
-			process_line(query, parser, sc, position == "all");
+			process_line(query, parser, sc, position == "all", dump_variables);
 			return 0;
 		} else if (!query_load.empty()) {
 			std::ifstream qf(query_load.c_str());
 			if (qf.good()) {
 				std::stringstream ss;
 				ss << qf.rdbuf();
-				process_line(ss.str(), parser, sc, position == "all");
+				process_line(ss.str(), parser, sc, position == "all", dump_variables);
 				return 0;
 			} else {
 				throw Wccl::FileNotFound(sentence_load, "", "Query file");
@@ -229,7 +244,7 @@ int main(int argc, char** argv)
 		}
 		boost::function<bool (const std::string&)> f;
 		f = boost::bind(&process_line, _1, boost::ref(parser), boost::ref(sc),
-			position == "all");
+			position == "all", dump_variables);
 #ifdef HAVE_LIBEDIT
 		libedit_read_loop(f);
 #else