direct declaration relations
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java b/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
index 11f15bc..d2628c8 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
@@ -17,6 +17,7 @@
 import org.antlr.v4.runtime.Parser;
 import org.antlr.v4.runtime.ParserRuleContext;
 import org.antlr.v4.runtime.tree.ParseTree;
+import org.slf4j.LoggerFactory;
 
 import de.ids_mannheim.korap.query.annis.AqlLexer;
 import de.ids_mannheim.korap.query.annis.AqlParser;
@@ -28,6 +29,8 @@
  *
  */
 public class AqlTree extends Antlr4AbstractSyntaxTree {
+    private org.slf4j.Logger log = LoggerFactory
+            .getLogger(AqlTree.class);
 	/**
 	 * Top-level map representing the whole request.
 	 */
@@ -64,6 +67,11 @@
 	 * Marks the currently active token in order to know where to add flags (might already have been taken away from token stack).
 	 */
 	LinkedHashMap<String,Object> curToken = new LinkedHashMap<String,Object>();
+	/**
+	 * Keeps track of operands lists that are to be serialised in an inverted
+	 * order (e.g. the IN() operator) compared to their AST representation. 
+	 */
+	private LinkedList<ArrayList<Object>> invertedOperandsLists = new LinkedList<ArrayList<Object>>();
 
 	private LinkedList<ArrayList<ArrayList<Object>>> distributedOperandsLists = new LinkedList<ArrayList<ArrayList<Object>>>();
 	
@@ -78,6 +86,9 @@
 	 * nodes here and exclude the operands from being written into the query map individually.   
 	 */
 	private LinkedList<String> operandOnlyNodeRefs = new LinkedList<String>();
+	private List<String> mirroredPositionFrames = Arrays.asList(new String[]{"startswith", "endswith", "overlaps", "contains"});
+//	private List<String> mirroredPositionFrames = Arrays.asList(new String[]{});
+	
 	public static boolean verbose = false;
 	
 	/**
@@ -139,10 +150,11 @@
 		} else {
 			throw new NullPointerException("Parser has not been instantiated!"); 
 		}
-		
+		log.info("Processing Annis query.");
 		System.out.println("Processing Annis QL");
 		if (verbose) System.out.println(tree.toStringTree(parser));
 		processNode(tree);
+		log.info(requestMap.toString());
 	}
 	
 	@SuppressWarnings("unchecked")
@@ -179,11 +191,11 @@
 		}
 		
 		if (nodeCat.equals("andTopExpr")) {
-			// Before processing any child expr node, check if it has one or more "n_ary_linguistic_term" nodes.
+			// Before processing any child expr node, check if it has one or more "*ary_linguistic_term" nodes.
 			// Those nodes may use references to earlier established operand nodes.
 			// Those operand nodes are not to be included into the query map individually but
 			// naturally as operands of the relations/groups introduced by the 
-			// n_ary_linguistic_term node. For that purpose, this section mines all used references
+			// *node. For that purpose, this section mines all used references
 			// and stores them in a list for later reference.
 			for (ParseTree exprNode : getChildrenWithCat(node,"expr")) {
 				List<ParseTree> lingTermNodes = new ArrayList<ParseTree>();
@@ -192,7 +204,13 @@
 				// Traverse refOrNode nodes under *ary_linguistic_term nodes and extract references
 				for (ParseTree lingTermNode : lingTermNodes) {
 					for (ParseTree refOrNode : getChildrenWithCat(lingTermNode, "refOrNode")) {
-						operandOnlyNodeRefs.add(refOrNode.getChild(0).toStringTree(parser).substring(1));
+						String refOrNodeString = refOrNode.getChild(0).toStringTree(parser);
+						if (refOrNodeString.startsWith("#")) {
+							operandOnlyNodeRefs.add(refOrNode.getChild(0).toStringTree(parser).substring(1));
+						} else {
+							
+						}
+						
 					}
 				}
 			}
@@ -244,14 +262,39 @@
 				group.put("relation", relationGroup);
 			} else if (groupType.equals("sequence")) {
 				putAllButGroupType(group, operatorTree);
+			} else if (groupType.equals("position")) {
+				String frame = (String) operatorTree.get("frame");
+				if (!mirroredPositionFrames.contains(frame.substring(6))) { //remove leading "frame:"
+					putAllButGroupType(group, operatorTree);
+				} else {
+					group = makeGroup("or");
+					LinkedHashMap<String, Object> group1 = makeGroup(groupType);
+					LinkedHashMap<String, Object> group2 = makeGroup(groupType);
+					putAllButGroupType(group1, operatorTree);
+					putAllButGroupType(group2, operatorTree);
+					ArrayList<Object> groupOperands = (ArrayList<Object>) group.get("operands");
+					groupOperands.add(group1);
+					groupOperands.add(group2);
+					ArrayList<ArrayList<Object>> distOperandsList = new ArrayList<ArrayList<Object>>();
+					distOperandsList.add((ArrayList<Object>) group1.get("operands"));
+					distOperandsList.add((ArrayList<Object>) group2.get("operands"));
+					invertedOperandsLists.push((ArrayList<Object>) group2.get("operands"));
+					distributedOperandsLists.push(distOperandsList);
+				}
 			}
-			
+			// insert referenced nodes into operands list
 			List<Object> operands = (List<Object>) group.get("operands");
 			for (ParseTree refOrNode : getChildrenWithCat(node, "refOrNode")) {
-				String ref = refOrNode.getChild(0).toStringTree(parser).substring(1);
-				operands.add(variableReferences.get(ref));
+				if (!getNodeCat(refOrNode.getChild(0)).equals("variableExpr")) {
+					String ref = refOrNode.getChild(0).toStringTree(parser).substring(1);
+					operands.add(variableReferences.get(ref));
+				} else {
+					
+				}
 			}
 			putIntoSuperObject(group);
+			objectStack.push(group);
+			stackedObjects++;
 		}
 		
 		if (nodeCat.equals("variableExpr")) {
@@ -295,7 +338,10 @@
 			}
 			
 			if (object != null) {
-				if (! operandOnlyNodeRefs.contains(variableCounter.toString())) putIntoSuperObject(object);
+				if (! operandOnlyNodeRefs.contains(variableCounter.toString())) {
+					putIntoSuperObject(object);
+					System.out.println(object.toString()+" "+objectStack.toString());
+				}
 				variableReferences.put(variableCounter.toString(), object);
 				variableCounter++;
 			}
@@ -382,7 +428,28 @@
 			relation.put("inOrder", true);
 		}
 		else if (operator.equals("spanrelation")) {
-			
+			relation = makeGroup(null);
+			relation.put("groupType", "position");
+			String reltype = operatorNode.getChild(0).toStringTree(parser);
+			String frame = null;
+			switch (reltype) {
+				case "_=_":
+					frame = "matches"; break;
+				case "_l_":
+					frame = "startswith"; break;
+				case "_r_":
+					frame = "endswith"; break;
+				case "_i_":
+					frame = "contains"; break;
+				case "_o_":
+					frame = "overlaps"; break;
+				case "_ol_":
+					frame = "overlapsLeft"; break;
+				case "_or_":
+					frame = "overlapsRight"; break;
+			}
+			relation.put("operation", "operation:position");
+			relation.put("frame", "frame:"+frame);
 		}
 		else if (operator.equals("commonparent")) {
 			
@@ -417,7 +484,6 @@
 		ParseTree qNameNode = edgeAnnoSpec.getChild(0);
 		ParseTree matchOperatorNode = edgeAnnoSpec.getChild(1);
 		ParseTree textSpecNode = edgeAnnoSpec.getChild(2);
-		System.err.println(edgeAnnoSpec.toStringTree(parser));
 		ParseTree layerNode = getFirstChildWithCat(qNameNode, "layer");
 		ParseTree foundryNode = getFirstChildWithCat(qNameNode, "foundry");
 		if (foundryNode!=null) edgeAnno.put("foundry", foundryNode.getChild(0).toStringTree(parser));
@@ -508,9 +574,19 @@
 	
 	@SuppressWarnings({ "unchecked" })
 	private void putIntoSuperObject(LinkedHashMap<String, Object> object, int objStackPosition) {
-		if (objectStack.size()>objStackPosition) {
+//		if (distributedOperandsLists.size()>0) {
+//			ArrayList<ArrayList<Object>> distributedOperands = distributedOperandsLists.pop();
+//			for (ArrayList<Object> operands : distributedOperands) {
+//				operands.add(object);
+//			}
+//		} else
+			if (objectStack.size()>objStackPosition) {
 			ArrayList<Object> topObjectOperands = (ArrayList<Object>) objectStack.get(objStackPosition).get("operands");
-			topObjectOperands.add(0, object);
+			if (!invertedOperandsLists.contains(topObjectOperands)) {
+				topObjectOperands.add(object);
+			} else {
+				topObjectOperands.add(0, object);
+			}
 			
 		} else {
 			requestMap.put("query", object);
@@ -548,10 +624,12 @@
 
 	    // Some things went wrong ...
 	    catch (Exception e) {
-	      System.err.println( e.getMessage() );
+	    	log.error(e.getMessage());
+	    	System.err.println( e.getMessage() );
 	    }
 	    
 	    if (tree == null) {
+	    	log.error("Could not parse query. Make sure it is correct ANNIS QL syntax.");
 	    	throw new QueryException("Could not parse query. Make sure it is correct ANNIS QL syntax.");
 	    }
 
@@ -565,43 +643,20 @@
 		 */
 		String[] queries = new String[] {
 			
-//			"#1 . #2 ",
-//			"#1 . #2 & meta::Genre=\"Sport\"",
-//			"A _i_ B",
-//			"A .* B",
-//			"A >* B",
-//			"#1 > [label=\"foo\"] #2",
-//			"pos=\"VVFIN\" & cas=\"Nom\" & #1 . #2",
-//			"A .* B ",
-//			"A .* B .* C",
-//			
-//			"#1 ->LABEL[lbl=\"foo\"] #2",
-//			"#1 ->LABEL[lbl=/foo/] #2",
-//			"#1 ->LABEL[foundry/layer=\"foo\"] #2",
-//			"#1 ->LABEL[foundry/layer=\"foo\"] #2",
-//			"node & pos=\"VVFIN\" & #2 > #1",
-//			"node & pos=\"VVFIN\" & #2 > #1",
-//			"pos=\"VVFIN\" > cas=\"Nom\" ",
-//			"pos=\"VVFIN\" >* cas=\"Nom\" ",
-//			"tiger/pos=\"NN\" >  node",
-//			"ref#node & pos=\"NN\" > #ref",
-//			"node & tree/pos=\"NN\"",
-//			"/node/",
-//			"\"Mann\"",
-//			"tok!=/Frau/",
-//			"node",
-//			"treetagger/pos=\"NN\"",
-//			"node & node & #2 ->foundry/dep[anno=\"key\"],2,4 #1",
-//			"tiger/pos=\"NN\" >cnx/cat  node",
-//			 "\"Mann\" & node & #2 >[cat=\"NP\"] #1",
 			 "node & node & #1 . #2",
 			 "node & node & #1 .2,6 #2",
 			 "node & node & #1 .* #2",
-			 "node & node & #2 ->label[mate/coref=\"true\"] #1"
-
-//			"node & node & #2 ->[foundry/layer=\"key\"],2,4 #1",
+			 "node & node & #2 ->label[mate/coref=\"true\"] #1",
+			 "node & node & #1 _ol_ #2",
+			 "node & cat=\"NP\" & #2 _r_ #1",
+			 "node > cat=\"NP\"",
+			 "cat=\"NP\" > node",
+			 "node > cat=\"NP\"",
+//			 "node > node",
+			 "node & node & #2 _=_ #1",
+			 "node & node & #2 _i_ #1"
 			};
-//		AqlTree.verbose=true;
+		AqlTree.verbose=true;
 		for (String q : queries) {
 			try {
 				System.out.println(q);
diff --git a/src/test/java/AqlTreeTest.java b/src/test/java/AqlTreeTest.java
index 00be416..a39ccc4 100644
--- a/src/test/java/AqlTreeTest.java
+++ b/src/test/java/AqlTreeTest.java
@@ -78,6 +78,32 @@
 	}
 	
 	@Test
+	public void testDirectDeclarationRelations() throws QueryException {
+		query = "node > node";
+		String ddr1 = 
+				"{@type=korap:group, operation=operation:relation, operands=[" +
+						"{@type=korap:span}," +
+						"{@type=korap:span}" +
+				"], relation={@type=korap:treeRelation, reltype=dominance}" +
+				"}";
+		aqlt = new AqlTree(query);
+		map = aqlt.getRequestMap().get("query").toString();
+		assertEquals(ddr1.replaceAll(" ", ""), map.replaceAll(" ", ""));
+		
+		query = "node > cnx/cat=\"NP\"";
+		String ddr2 = 
+				"{@type=korap:group, operation=operation:relation, operands=[" +
+						"{@type=korap:span}," +
+						"{@type=korap:span, foundry=cnx, layer=cat, key=NP, match=match:eq}" +
+				"], relation={@type=korap:treeRelation, reltype=dominance}" +
+				"}";
+		aqlt = new AqlTree(query);
+		map = aqlt.getRequestMap().get("query").toString();
+		assertEquals(ddr2.replaceAll(" ", ""), map.replaceAll(" ", ""));
+		
+	}
+	
+	@Test
 	public void testSimpleDominance() throws QueryException {
 		query = "node & node & #2 > #1";
 		String dom1 = 
@@ -159,7 +185,6 @@
 		aqlt = new AqlTree(query);
 		map = aqlt.getRequestMap().get("query").toString();
 		assertEquals(dom2.replaceAll(" ", ""), map.replaceAll(" ", ""));
-		
 	}
 	
 	@Test
@@ -178,8 +203,7 @@
 		
 		query = "node & node & #1 .* #2";
 		String dom2 = 
-				"{@type=korap:group, operation=operation:sequence,  " +
-						"operands=[" +
+				"{@type=korap:group, operation=operation:sequence, operands=[" +
 						"{@type=korap:span}," +
 						"{@type=korap:span}" +
 					"], distances=[" +
@@ -189,6 +213,60 @@
 		aqlt = new AqlTree(query);
 		map = aqlt.getRequestMap().get("query").toString();
 		assertEquals(dom2.replaceAll(" ", ""), map.replaceAll(" ", ""));
+	}
+	
+	@Test
+	public void testPositions() throws QueryException {
+		query = "node & node & #2 _=_ #1";
+		String pos1 = 
+				"{@type=korap:group, operation=operation:position, operands=[" +
+						"{@type=korap:span}," +
+						"{@type=korap:span}" +
+				"], frame=frame:matches" +
+				"}";
+		aqlt = new AqlTree(query);
+		map = aqlt.getRequestMap().get("query").toString();
+		assertEquals(pos1.replaceAll(" ", ""), map.replaceAll(" ", ""));
+		
+//		query = "node & node & #2 _i_ #1";
+//		String pos2 = 
+//				"{@type=korap:group, operation=operation:position, operands=[" +
+//						"{@type=korap:span}," +
+//						"{@type=korap:span}" +
+//				"], frame=frame:contains" +
+//				"}";
+//		aqlt = new AqlTree(query);
+//		map = aqlt.getRequestMap().get("query").toString();
+//		assertEquals(pos2.replaceAll(" ", ""), map.replaceAll(" ", ""));
+//		
+//		query = "node & node & #2 _l_ #1";
+//		String pos3 = 
+//				"{@type=korap:group, operation=operation:position, operands=[" +
+//						"{@type=korap:span}," +
+//						"{@type=korap:span}" +
+//				"], frame=frame:startswith" +
+//				"}";
+//		aqlt = new AqlTree(query);
+//		map = aqlt.getRequestMap().get("query").toString();
+//		assertEquals(pos3.replaceAll(" ", ""), map.replaceAll(" ", ""));
+		
+		query = "node & \"Mann\" & #2 _r_ #1";
+		String pos4 = 
+				"{@type=korap:group, operation=operation:or, operands=[" +
+					"{@type=korap:group, operation=operation:position, operands=[" +
+						"{@type=korap:span}," +
+						"{@type=korap:token, wrap={@type=korap:term, type=type:regex, key=Mann, match=match:eq}}" +
+						"], frame=frame:endswith" +
+					"}," +
+					"{@type=korap:group, operation=operation:position, operands=[" +
+						"{@type=korap:token, wrap={@type=korap:term, type=type:regex, key=Mann, match=match:eq}}," +
+						"{@type=korap:span}" +
+						"], frame=frame:endswith" +
+					"}" +
+				"]}";
+		aqlt = new AqlTree(query);
+		map = aqlt.getRequestMap().get("query").toString();
+		assertEquals(pos4.replaceAll(" ", ""), map.replaceAll(" ", ""));
 		
 	}