Support CQP

Change-Id: I009162c0779bbc7408a44208b4c06ddb86aab891
Reviewed-on: https://korap.ids-mannheim.de/gerrit/c/KorAP/Koral/+/7372
Reviewed-by: Nils Diewald <nils@diewald-online.de>
diff --git a/.gitignore b/.gitignore
index 1f3d6a1..cace822 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,4 +77,4 @@
 # /src/main/java/de/ids_mannheim/korap/query/serialize/
 /src/main/java/de/ids_mannheim/korap/query/serialize/SerializationSandbox.java
 /lib/
-/bin/
+/bin/
\ No newline at end of file
diff --git a/Changes b/Changes
index 0e4d5bb..ecd5b3d 100644
--- a/Changes
+++ b/Changes
@@ -1,9 +1,13 @@
-0.39.1
+0.40 2023-07-26
+    - [feature] Initial support for CQP
+      (irimia, margaretha, diewald)
+      The commit consists of ~15 squashed commits mainly
+      authored by irimia.
 
 0.39 2023-01-12
-    - Updated to Java 11
-    - Dependencies updated
-    - Fix reference bug in Annis (resolved #129, diewald)
+    - [cleanup] Updated to Java 11
+    - [security] Dependencies updated
+    - [bugfix] Fix reference bug in Annis (resolved #129, diewald)
 
 0.38.2 2022-10-05
     - [feature] Improve regex treatment of negative
@@ -13,7 +17,7 @@
 
 0.38.1 2022-10-05
     - [security] Updated log4j (diewald)
-    - Fixed Annis OR group (resolved #96) 
+    - [bugfix] Fixed Annis OR group (resolved #96) 
     - [security] Updated jackson (diewald)
 
 0.38 2021-12-10
diff --git a/README.md b/README.md
index 121fe00..d737008 100644
--- a/README.md
+++ b/README.md
@@ -10,11 +10,12 @@
 * [ANNIS QL](http://annis-tools.org/aql.html)
 * [Poliqarp QL](http://korpus.pl/en/cheatsheet/node3.html) (extended by numerous operators to "PoliqarpPlus" QL)
 * [CQL](http://www.loc.gov/standards/sru/cql/spec.html) (for basic search as described in [the CLARIN FCS 1.0 Specification](https://www.clarin.eu/content/federated-content-search-clarin-fcs) )
+* [CQP](http://cwb.sourceforge.net/files/CQP_Tutorial/)
 * FCSQL (based on [CQP](http://cwb.sourceforge.net/files/CQP_Tutorial/), for advanced search as described in the CLARIN FCS 2.0 specification draft)
 
 ## Usage Example
 
-You can use the main class QuerySerializer to translate and serialize queries. Valid QL identifiers are `cosmas2`, `annis`, `poliqarp`, `poliqarpplus`, `cql` and `fcsql`.
+You can use the main class QuerySerializer to translate and serialize queries. Valid QL identifiers are `cosmas2`, `annis`, `poliqarp`, `poliqarpplus`, `cql`, `cqp`, and `fcsql`.
 
 ```java
 import de.ids_mannheim.korap.query.serialize.QuerySerialzer;
@@ -135,6 +136,8 @@
 [Leibniz Institute for the German Language (IDS)](https://www.ids-mannheim.de/),
 member of the [Leibniz Association](https://www.leibniz-gemeinschaft.de).
 
+The CQP implementation was authored by Elena Irimia.
+
 The ANTLR grammars for parsing ANNIS QL and COSMAS II QL were developed by
 Thomas Krause (HU Berlin) and Franck Bodmer (IDS Mannheim), respectively.
 Minor adaptations of those grammars were implemented by the Koral authors.
diff --git a/pom.xml b/pom.xml
index 24f4f75..4b2b09e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
 
 	<groupId>de.ids_mannheim.korap</groupId>
 	<artifactId>Koral</artifactId>
-	<version>0.39.1</version>
+	<version>0.40.0</version>
 	<packaging>jar</packaging>
 	<name>Koral</name>
 	<url>https://korap.ids-mannheim.de</url>
@@ -32,6 +32,9 @@
 			<name>Eliza Margaretha</name>
 			<email>margaretha@ids-mannheim.de</email>
 		</developer>
+		<developer>
+			<name>Elena Irimia</name>
+		</developer>
 	</developers>
 
 	<properties>
@@ -149,7 +152,7 @@
 			<artifactId>fcs-simple-endpoint</artifactId>
 			<version>1.6.0</version>
 		</dependency>
-	</dependencies>
+  </dependencies>
 	<build>
 		<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
 		<outputDirectory>${basedir}/target/classes</outputDirectory>
@@ -337,7 +340,18 @@
 						</configuration>
 						<!-- <phase>generate-sources</phase> -->
 					</execution>
-				</executions>
+					<execution>
+						<id>cqp</id>
+						<goals>
+							<goal>antlr4</goal>
+						</goals>
+						<configuration>
+							<sourceDirectory>${basedir}/src/main/antlr/cqp</sourceDirectory>
+							<outputDirectory>${basedir}/src/main/java/de/ids_mannheim/korap/query/parse/cqp</outputDirectory>
+							<libDirectory>${basedir}/src/main/antlr/cqp</libDirectory>
+						</configuration>
+					</execution>
+        </executions>
 			</plugin>
 			<plugin>
 				<!-- This plugin will help to build the ANTLR3 grammar on the fly. The 
@@ -369,5 +383,38 @@
 				</includes>
 			</resource>
 		</resources>
+		<pluginManagement>
+			<plugins>
+				<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
+				<plugin>
+					<groupId>org.eclipse.m2e</groupId>
+					<artifactId>lifecycle-mapping</artifactId>
+					<version>1.0.0</version>
+					<configuration>
+						<lifecycleMappingMetadata>
+							<pluginExecutions>
+								<pluginExecution>
+									<pluginExecutionFilter>
+										<groupId>org.antlr</groupId>
+										<artifactId>
+											antlr3-maven-plugin
+										</artifactId>
+										<versionRange>
+											[3.5.2,)
+										</versionRange>
+										<goals>
+											<goal>antlr</goal>
+										</goals>
+									</pluginExecutionFilter>
+									<action>
+										<ignore></ignore>
+									</action>
+								</pluginExecution>
+							</pluginExecutions>
+						</lifecycleMappingMetadata>
+					</configuration>
+				</plugin>
+			</plugins>
+		</pluginManagement>
 	</build>
 </project>
diff --git a/src/main/antlr/cqp/CQP.g4 b/src/main/antlr/cqp/CQP.g4
new file mode 100644
index 0000000..29475c5
--- /dev/null
+++ b/src/main/antlr/cqp/CQP.g4
@@ -0,0 +1,374 @@
+grammar CQP;
+
+@header {package de.ids_mannheim.korap.query.parse.cqp;}
+
+
+
+options
+{
+language=Java;
+}
+
+@members {
+String text1, text2;
+}
+/*
+
+CQP grammar 
+--author Elena IRimia, based on PQ Plus grammar (author: Joachim BIngel)
+Based on CQP
+ - http://cwb.sourceforge.net/files/CQP_Tutorial/
+ */
+
+POSITION_OP : 'lbound' | 'rbound';
+REGION_OP: 'region';
+RELATION_OP	: ('dominates' | 'dependency' | 'relatesTo'); //not in PQ tutorial
+MATCH_OPM : 'meet' ;
+// SUBMATCH_OP	: 'submatch';  from PQ+ implementation
+WITHIN		: 'within';
+MU: 'MU';
+META		: 'meta';
+SPANCLASS_ID: ('@'| '@1');
+
+
+/*
+ Regular expression
+ %c means case insensitivity
+ %d ignore diacritics */
+ /* l flag: alternative for escape character "\"; word="?"; not implemented in KoralQuery %*/
+
+FLAG_lcd: '%' (('l'|'c'|'d'|'L'|'C'|'D') ('l'|'c'|'d'|'L'|'C'|'D')? ('l'|'c'|'d'|'L'|'C'|'D')?);
+
+
+
+
+/** Simple strings and Simple queries */
+WS                  : [ \t]  -> channel(HIDDEN);
+fragment FOCC       : '{' WS* ( [0-9]* WS* ',' WS* [0-9]+ | [0-9]+ WS* ','? ) WS* '}'; 
+fragment ALPHABET   : ~('\t' | ' ' | '/' | '*' | '+' | '{' | '}' | '[' | ']'
+                    | '(' | ')' | '|' | '"' | ',' | ':' | '\'' | '\\' | '!' | '=' | '~' | '&' | '^' | '<' | '>' | ';' | '?' | '@' |'%');
+NUMBER              : ('-')? [0-9]+;
+
+NL                  : [\r\n] -> skip;
+
+WORD: ALPHABET+ ;
+
+/*Complex queries*/
+LPAREN      : '[';
+RPAREN      : ']';
+LRPAREN     : '(';
+RRPAREN     : ')';
+NEG         : '!';
+QMARK		: '?';
+CONJ        : '&';
+DISJ        : '|';
+COMMA		: ',';
+LT			: '<';
+GT			: '>';
+LBRACE		: '{';
+RBRACE		: '}';
+SLASH		: '/';
+COLON		: ':';
+TILDE		: '~';
+EQ			: '=';
+CARET 		: '^';
+STAR		: '*';
+PLUS		: '+';
+EMPTYREL	: '@'; //take care: two tokens, EMPTYREL and SPANCLAS_ID, matching the same input;
+BACKSLASH	: '\\';
+SQUOTE      : '\'';
+//in PQ+, DQUOTE was not defined
+DQUOTE: ('"'|'„'|'“'|'“'|'”');
+
+
+/* Regular expressions and Regex queries */
+fragment RE_symbol     : ~('*' | '?' | '+' | '{' | '}' | '[' | ']'
+                     | '(' | ')' | '|' | '\\' | '"' | ':' | '\'');
+fragment RE_esc      : ('\\' ('.' | '*' | '?' | '+' | '{' | '}' | '[' | ']'
+                     | '(' | ')' | '|' | '\\' | ':' | '"' | '\''))| '\'' '\'' |  '"' '"';
+fragment RE_char     : (RE_symbol | RE_esc );
+fragment RE_alter    : ((RE_char | ('(' RE_expr ')') | RE_chgroup) '|' RE_expr )+;
+
+fragment RE_chgroup  : '[' RE_char+ ']';
+fragment RE_quant      : ('.' | RE_char | RE_chgroup | ( '(' RE_expr ')')) ('?' | '*' | '+' | FOCC) QMARK?;
+fragment RE_group    : '(' RE_expr ')';
+fragment RE_expr     : ('.' | RE_char | RE_alter | RE_chgroup | RE_quant | RE_group)+;
+/* you can search for DQUOTE inside SQUOUTE, and viceversa:  '"' or "'"; */
+fragment RE_dquote   : DQUOTE  (RE_expr | '\'' | ':' )* DQUOTE; // DQOUTE is not good, modify like verbatim in PQ+!
+fragment RE_squote   : SQUOTE  (RE_expr | '"' | ':')* SQUOTE; 
+
+
+
+REGEX: RE_dquote|RE_squote;
+
+
+
+
+
+/*parser*/
+
+
+flag
+: FLAG_lcd
+;
+
+
+boolOp
+: CONJ | DISJ
+;
+
+regex
+: REGEX
+;
+
+
+
+key
+: regex; // | verbatim
+
+
+// key for spans
+skey
+: WORD
+; 
+
+// think of the whole "foundry\layer" string as a key (p-attribute in cqp), like in PQPlus grammar
+foundry
+: WORD
+;
+
+layer
+: WORD
+;
+
+value
+: WORD 
+| NUMBER 
+| regex
+;
+ ///////
+/* Fields */
+term
+: NEG* (foundry SLASH)? layer termOp key (COLON value)? flag?
+| NEG* foundry flag layer? termOp key (COLON value)? flag?
+| NEG* LRPAREN (foundry SLASH)? layer termOp key (COLON value)? flag? RRPAREN  (CONJ position)+ // for position ops
+| NEG* LRPAREN foundry flag layer? termOp key (COLON value)? flag? RRPAREN (CONJ position)+ // for position ops
+| NEG* LRPAREN term RRPAREN 
+| key flag? (CONJ position)+ // for position ops
+;
+
+
+termOp
+: (NEG? EQ? EQ | NEG? TILDE? TILDE)
+;
+
+min
+: NUMBER
+;
+
+max
+: NUMBER
+;
+
+
+/*startpos 
+: NUMBER
+;
+
+length 
+: NUMBER
+;*/
+
+
+range
+: LBRACE 
+  (
+    min COMMA max 
+    | COMMA max 
+    | min COMMA 
+    | max
+  ) 
+  RBRACE
+;
+
+emptyToken
+: LPAREN RPAREN
+;
+
+termGroup
+: (term | LRPAREN termGroup RRPAREN) boolOp (term | LRPAREN termGroup RRPAREN | termGroup)
+| NEG* LRPAREN termGroup RRPAREN
+;
+
+repetition
+: kleene
+| range
+;
+
+kleene
+: QMARK
+| STAR
+| PLUS
+;
+
+token
+: NEG* 
+    ( LPAREN term RPAREN
+    | LPAREN termGroup RPAREN
+    | key flag?
+    )
+;
+
+
+span
+: skey // for lbound/sbound; check how it works for meet!
+  | LT ((foundry SLASH)? layer termOp)? skey ((  NEG* (LRPAREN term RRPAREN|  LRPAREN termGroup RRPAREN | NEG* term | NEG* termGroup))? GT)
+;
+
+closingspan
+:
+LT '/' ((foundry SLASH)? layer termOp)? skey ((  NEG* (LRPAREN term RRPAREN|  LRPAREN termGroup RRPAREN | NEG* term | NEG* termGroup))? GT)
+;
+
+position
+//: POSITION_OP LRPAREN (segment|sequence) COMMA (segment|sequence) RRPAREN
+: POSITION_OP LRPAREN span RRPAREN 
+;
+
+
+disjunction
+: (segment|sequence|group) (DISJ (segment|sequence|group))+ // do i need group here? test!
+;
+
+group
+: LRPAREN ( disjunction | sequence ) RRPAREN 
+; 
+
+spanclass
+: (SPANCLASS_ID (token|segment|group| ) //LRBRACE sequence RRBRACE) // ai adaugat sequence! vezi de ce nu intra pe ea, si daca intra, vezi cum ruleaza procesorul; am nevoie de token si group aici?
+  | label COLON (segment|sequence) 
+  | MATCH_OPM LRPAREN meetunion RRPAREN // for recursive meet
+  | MATCH_OPM segment // for simple meet
+  )
+;
+
+label: WORD; //&&
+
+emptyTokenSequence
+: (emptyToken repetition?)+
+;
+
+emptyTokenSequenceAround
+: (emptyToken PLUS?)+
+;
+
+
+emptyTokenSequenceClass
+: (SPANCLASS_ID emptyTokenSequence | label COLON emptyTokenSequence)    // class defined around empty tokens 
+;
+
+sstruct: endswith | startswith   ; 
+qstruct:  isaround { 
+  text1 = _localctx.getChild(0).getChild(0).getText();
+  text1 = text1.substring(1, text1.length()-1);
+  text2 = _localctx.getChild(0).getChild(_localctx.getChild(0).getChildCount()-1).getText();
+  text2= text2.substring(2, text2.length()-1);
+}
+{text1.equals(text2)}?|  matches { 
+  text1 = _localctx.getChild(0).getChild(0).getText();
+  text1 = text1.substring(1, text1.length()-1);
+  text2 = _localctx.getChild(0).getChild(_localctx.getChild(0).getChildCount()-1).getText();
+  text2= text2.substring(2, text2.length()-1);
+}
+
+{text1.equals(text2)}?; 
+isaround :  span emptyTokenSequenceAround (segment|sequence) emptyTokenSequenceAround closingspan; 
+matches:  span  (sequence | segment)  closingspan;
+
+
+startswith: span (sequence|segment);
+endswith: (sequence|segment) closingspan;
+region: SLASH REGION_OP LPAREN span RPAREN;
+
+
+
+sequence
+: alignment segment*  // give precedence to this subrule over the next to make sure preceding segments come into 'alignment'
+| segment+ alignment segment*
+| segment segment+
+| alignment (segment|sequence) alignment?
+;
+
+segment
+: (  token 
+  | group
+  | region
+  | spanclass 
+  | matching
+ // | submatch
+  | relation
+  | LRPAREN segment RRPAREN
+  | emptyTokenSequence
+  | emptyTokenSequenceClass
+  | qstruct
+  | span
+  | closingspan
+  ) 
+  repetition?
+ ; 
+
+/** Entry point for linguistic queries */
+
+query
+: qstruct  | (sequence|segment)* qstruct (sequence|segment)* | sstruct |segment | sequence | disjunction 
+;
+
+within
+: WITHIN span   //WORD
+;
+
+/**
+ === META section ===
+ defines metadata filters on request
+*/
+
+meta               : META metaTermGroup;
+metaTermGroup	   : ( term | termGroup )+;
+
+
+
+// PQ Plus rules that are not described in https://korap.ids-mannheim.de/doc/ql/fcsql#page-top; i don't know how they work;
+relation
+: RELATION_OP LRPAREN ((EMPTYREL|relSpec)? repetition? COLON)? (segment|sequence) COMMA (segment|sequence) RRPAREN
+;
+
+relSpec
+: (foundry SLASH)? layer (termOp key)?
+;
+
+/*submatch
+: SUBMATCH_OP LRPAREN startpos (COMMA length)? COLON (segment|sequence) RRPAREN
+;*/
+
+
+alignment
+: segment? ( (CARET segment)+ | CARET)
+;
+
+
+matching
+: (MU LRPAREN meetunion RRPAREN);
+// //| (MATCH_OPF LRPAREN SPANCLASS_ID? (segment|sequence)? RRPAREN)
+
+
+meetunion
+: 
+(((LRPAREN meetunion RRPAREN) | segment) ((LRPAREN meetunion RRPAREN) | segment) ((NUMBER  NUMBER) | span))
+;
+
+/**
+    Entry point for all requests. Linguistic query is obligatory, metadata filtering
+    is optional.
+*/
+
+request: query within? meta?  ';'? ;
diff --git a/src/main/antlr/poliqarpplus/PoliqarpPlusParser.g4 b/src/main/antlr/poliqarpplus/PoliqarpPlusParser.g4
index e65d8db..a6f70cc 100644
--- a/src/main/antlr/poliqarpplus/PoliqarpPlusParser.g4
+++ b/src/main/antlr/poliqarpplus/PoliqarpPlusParser.g4
@@ -133,7 +133,7 @@
 ;
 
 span
-: LT ((foundry SLASH)? layer termOp)? key NEG* ((LRPAREN term RRPAREN|LRPAREN termGroup RRPAREN)? | (term|termGroup)?) GT
+: LT ((foundry SLASH)? layer termOp)? key  ((NEG* LRPAREN term RRPAREN| NEG* LRPAREN termGroup RRPAREN)? | (NEG* term| NEG* termGroup)?) GT
 ;
 
 position
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractQueryProcessor.java b/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractQueryProcessor.java
index c6a909b..12a584c 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractQueryProcessor.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/AbstractQueryProcessor.java
@@ -38,6 +38,11 @@
      * Keeps track of open node categories.
      */
     protected LinkedList<String> openNodeCats = new LinkedList<String>();
+    
+    /**
+     * Keeps track of span node categories.
+     */
+    protected LinkedList<String> spanNodeCats = new LinkedList<String>();
     /**
      * Keeps track of all visited nodes in a tree.
      */
@@ -212,6 +217,13 @@
         highlightClasses.add(classId);
         meta.put("highlight", highlightClasses);
     }
+    public void removeExcessHighlightClass () {
+        for (int i=1; i<highlightClasses.size(); i++)
+        {
+    	highlightClasses.remove(i);
+        }
+       
+    }
 
 
     public void addAlignment (int leftClassId, int rightClassId) {
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/Antlr4AbstractQueryProcessor.java b/src/main/java/de/ids_mannheim/korap/query/serialize/Antlr4AbstractQueryProcessor.java
index 042b177..1ea35cf 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/Antlr4AbstractQueryProcessor.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/Antlr4AbstractQueryProcessor.java
@@ -141,6 +141,30 @@
 
 
     /**
+     * Returns all children of a node which are not terminal nodes
+     * 
+     * @param node
+     *            The node.
+   
+     * @return A (possibly empty) list containing all non-terminal children    */
+    protected List<ParseTree> getChildrenWithChildren (ParseTree node) {
+        ArrayList<ParseTree> children = new ArrayList<ParseTree>();
+        int ccount = node.getChildCount();
+        for (int i = 0; i < ccount; i++) {
+        	ArrayList<ParseTree> childrenofchildren = new ArrayList<ParseTree>();
+            ParseTree child = node.getChild(i);
+            int cccount = child.getChildCount();
+        	for (int j=0; j < cccount; j++)	{
+        		childrenofchildren.add(child.getChild(j));
+        	}
+            if (!childrenofchildren.isEmpty()) {
+                children.add(child);
+            }
+        }
+        return children;
+    }
+
+    /**
      * Returns all descendants (direct or indirect children) of a node
      * which
      * are of a given category.
@@ -213,4 +237,4 @@
         }
         return null;
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/CQPQueryProcessor.java b/src/main/java/de/ids_mannheim/korap/query/serialize/CQPQueryProcessor.java
new file mode 100644
index 0000000..fb41fe9
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/CQPQueryProcessor.java
@@ -0,0 +1,1810 @@
+package de.ids_mannheim.korap.query.serialize;
+
+import de.ids_mannheim.korap.query.object.ClassRefOp;
+import de.ids_mannheim.korap.query.object.KoralFrame;
+import de.ids_mannheim.korap.query.object.KoralMatchOperator;
+import de.ids_mannheim.korap.query.object.KoralOperation;
+import de.ids_mannheim.korap.query.object.KoralTermGroupRelation;
+import de.ids_mannheim.korap.query.parse.cqp.CQPLexer;
+import de.ids_mannheim.korap.query.parse.cqp.CQPParser;
+import de.ids_mannheim.korap.query.serialize.util.Antlr4DescriptiveErrorListener;
+import de.ids_mannheim.korap.query.serialize.util.KoralObjectGenerator;
+import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
+
+import org.antlr.v4.runtime.*;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+/**
+ * Map representation of CQP syntax tree as returned by
+ * ANTLR.
+ * Most centrally, this class maintains a set of nested maps and
+ * lists which represent the JSON tree, which is built by the JSON
+ * serialiser on basis of the {@link #requestMap} at the root of
+ * the tree. <br/>
+ * The class further maintains a set of stacks which effectively
+ * keep track of which objects to embed in which containing
+ * objects.
+ * 
+ * This class expects the CQP ANTLR grammar shipped with Koral
+ * v0.3.0.
+ * 
+ * @author Joachim Bingel (bingel@ids-mannheim.de)
+ * @author Eliza Margaretha (margaretha@ids-mannheim.de)
+ * @author Nils Diewald (diewald@ids-mannheim.de)
+ * @author Elena Irimia (modified from PQProcessor; elena@racai.ro)
+ * @version 0.3.0
+ * @since 0.1.0
+ */
+public class CQPQueryProcessor extends Antlr4AbstractQueryProcessor {
+
+    private static final boolean DEBUG = true;
+    private static Logger log = LoggerFactory
+            .getLogger(CQPQueryProcessor.class);
+    private int classCounter = 1;
+
+    Map<ParseTree, Integer> classWrapRegistry = new HashMap<ParseTree, Integer>();
+
+
+    /**
+     * Constructor
+     * 
+     * @param query
+     *            The syntax tree as returned by ANTLR
+     */
+    public CQPQueryProcessor (String query) {
+        KoralObjectGenerator.setQueryProcessor(this);
+        process(query);
+        if (DEBUG) { 
+            log.debug(">>> " + requestMap.get("query") + " <<<");
+        }
+    }
+
+
+    @Override
+    public void process (String query) {
+
+        ParseTree tree;
+        tree = parseCQPQuery(query);  
+
+       
+        super.parser = this.parser;
+        if (DEBUG) {
+            log.debug("Processing CQP query: " + query);
+        }
+        if (tree != null) {
+            if (DEBUG){
+                log.debug("ANTLR parse tree: " + tree.toStringTree(parser));
+            }
+
+            processNode(tree, true);
+        }
+        else {
+            addError(StatusCodes.MALFORMED_QUERY,
+                    "Could not parse query >>> " + query + " <<<.");
+        }
+    }
+
+
+    /**
+     * Recursively calls itself with the children of the currently
+     * active node, traversing the tree nodes in a top-down,
+     * depth-first fashion. A list is maintained that contains all
+     * visited nodes which have been directly addressed by their
+     * (grand-/grand-grand-/...) parent nodes, such that some
+     * processing time is saved, as these node will not be processed.
+     * This method is effectively a list of if-statements that are
+     * responsible for treating the different node types correctly and
+     * filling the respective maps/lists.
+     * 
+     * @param node
+     *            The currently processed node. The process(String
+     *            query) method calls this method with the root.
+     */
+    private void processNode (ParseTree node, boolean putvisited) {
+        // Top-down processing
+        if (visited.contains(node))  /*** if the node was visited in previous steps ***/
+            return;
+        else
+        	/** if putvisited is false, we need the node to be visited again in future operations;  ***/
+        	/** so we don't put it in visited! ***/ 
+        	if (putvisited) visited.add(node); 
+
+        String nodeCat = getNodeCat(node);
+        openNodeCats.push(nodeCat);
+       
+
+        stackedObjects = 0;
+
+        if (verbose) {
+            System.err.println(" " + objectStack);
+            System.out.println(openNodeCats);
+        }
+
+        // Check if (the translation of) this node is registered to be wrapped
+        // in a class, e.g. by an alignment operation
+        if (classWrapRegistry.containsKey(node)) {
+            Integer classId = classWrapRegistry.get(node);
+            Map<String, Object> spanClass = KoralObjectGenerator
+                    .makeSpanClass(classId);
+            putIntoSuperObject(spanClass);
+            objectStack.push(spanClass);
+            stackedObjects++;
+        }
+
+        /*
+         ****************************************************************
+         **************************************************************** 
+         *          Processing individual node categories               *
+         ****************************************************************
+         ****************************************************************
+         */
+        if (nodeCat.equals("segment")) {
+            processSegment(node);
+        }
+
+        if (nodeCat.equals("sequence")) {
+            processSequence(node);
+        }
+        if (nodeCat.equals("meetunion")) {
+            
+        	// for the outer meet, of whatever type, putvisited is
+            // true and the node is put in visited
+        	processMeetUnion(node, putvisited);
+        }
+
+        if (nodeCat.equals("emptyTokenSequence")) {
+            processEmptyTokenSequence(node);
+        }
+
+        if (nodeCat.equals("emptyTokenSequenceClass")) {
+            processEmptyTokenSequenceClass(node);
+        }
+
+        if (nodeCat.equals("token")) {
+            processToken(node);
+        }
+        /*
+        if (nodeCat.equals("tokenstruct")) {
+            processTokenStruct(node);
+        }
+        */
+        
+        if (nodeCat.equals("alignment")) {
+            processAlignment(node);
+        }
+
+        if ((nodeCat.equals("span") || nodeCat.equals("closingspan")) && getNodeCat(node.getChild(0))!="skey") {
+            String nCat0 = getNodeCat(node.getChild(0));
+            if (nCat0.equals("skey")) {
+                // for unlayered spans: \region[np], lbound(np), etc!
+                processSpan(node);
+            } else {
+                // for struct like <s> ... </s>; we don't want to serialize span s two times;
+        		String spankey ="";
+                int ccount = node.getChildCount();
+                for (int i = 0; i < ccount - 1; i++ ) {
+        		    String nCat = getNodeCat(node.getChild(i));
+                    if (nCat.equals("skey")) {
+                        spankey = node.getChild(i).getText();
+                    }
+                }
+
+        		if (!spanNodeCats.contains(spankey)) { 
+                    spanNodeCats.push(spankey);
+                    processSpan(node);
+                }
+            }
+        }
+      
+        if (nodeCat.equals("disjunction")) {
+            processDisjunction(node);
+        }
+
+        if (nodeCat.equals("relation")) {
+            processRelation(node);
+        }
+
+        if (nodeCat.equals("spanclass")) {
+            processSpanClass(node);
+        }
+
+        if (nodeCat.equals("matching")) {
+            processMatching(node);
+        }
+
+        if (nodeCat.equals("submatch")) {
+            processSubMatch(node);
+        }
+
+        if (nodeCat.equals("queryref")) {
+            processQueryRef(node);
+        }
+
+        if (nodeCat.equals("meta")) {
+            processMeta(node);
+        }
+       
+        if (nodeCat.equals("sstruct")){
+        	
+        	processPosition(node);
+        }
+           
+        if (nodeCat.equals("qstruct")){
+        	
+        	processPosition(node);
+        }
+        if (nodeCat.equals("position")){
+        	
+        	processPosition(node);
+        }
+        	
+        if (nodeCat.equals("within")
+                && !getNodeCat(node.getParent()).equals("position")) { // why this condition??: elena: it is from
+                    // PQ+ implementation; couldn't find illustrative tests; 
+            processWithin(node);
+        }
+
+        objectsToPop.push(stackedObjects);
+
+        /*
+         ****************************************************************
+         **************************************************************** 
+         *  Recursion until 'request' node (root of tree) is processed  *
+         ****************************************************************
+         ****************************************************************
+         */
+        int ccount = node.getChildCount();
+        for (int i = 0; i < ccount; i++) {
+            processNode(node.getChild(i), putvisited);
+        }
+
+        // Stuff that happens when leaving a node (taking items off stacks)
+        for (int i = 0; i < objectsToPop.get(0); i++) {
+            objectStack.pop();
+        }
+        objectsToPop.pop();
+        openNodeCats.pop();
+    }
+
+
+    /**
+     * Processes a 'segment' node.
+     * 
+     * @param node
+     */
+    private void processSegment (ParseTree node) {
+        // Cover possible quantification (i.e. repetition) of segment
+        ParseTree quantification = getFirstChildWithCat(node, "repetition");
+        if (quantification != null) {
+            Map<String, Object> quantGroup = KoralObjectGenerator
+                .makeGroup(KoralOperation.REPETITION);
+            Integer[] minmax = parseRepetition(quantification);
+            quantGroup.put("boundary",
+                           KoralObjectGenerator.makeBoundary(minmax[0], minmax[1]));
+            putIntoSuperObject(quantGroup);
+            objectStack.push(quantGroup);
+            stackedObjects++;
+        }
+        
+        // for segments following closingspan or preceding span
+        if (node.getParent().getChild(0).getChild(0) != null) {
+            if (getNodeCat(node.getParent().getChild(0).getChild(0)).equals("closingspan") &&
+                !node.equals(node.getParent().getChild(0))) {
+                Map<String, Object> classGroup = KoralObjectGenerator
+                    .makeSpanClass(200);
+        			        
+                //  addHighlightClass(200);
+                putIntoSuperObject(classGroup);
+                objectStack.push(classGroup);
+                stackedObjects++;
+            }
+        }
+
+        int nchild = node.getParent().getChildCount();
+        if (node.getParent().getChild(nchild-1).getChild(0) != null) { 
+            if (getNodeCat( node.getParent().getChild(nchild-1).getChild(0)).equals("span") &&
+                !node.equals(node.getParent().getChild(nchild-1))) {
+                Map<String, Object> classGroup = KoralObjectGenerator
+                    .makeSpanClass(200);
+        			        
+                //   addHighlightClass(200);
+                putIntoSuperObject(classGroup);
+                objectStack.push(classGroup);
+                stackedObjects++;
+            }
+        }
+        
+        if (getNodeCat(node.getParent().getParent()).equals("startswith") ||
+            getNodeCat(node.getParent().getParent()).equals("endswith")) {
+            Map<String, Object> classGroup = KoralObjectGenerator
+                .makeSpanClass(200);
+        			        
+            //   addHighlightClass(200);
+            putIntoSuperObject(classGroup);
+            objectStack.push(classGroup);
+            stackedObjects++;
+        }
+    }
+
+
+    /**
+     * Process a 'sequence' node.
+     * 
+     * @param node
+     */
+    private void processSequence (ParseTree node) {
+        // skip in case of emptyTokenSequence or emptyTokenSequenceClass
+        if (node.getChildCount() == 1 && getNodeCat(node.getChild(0))
+            .startsWith("emptyTokenSequence")) {
+            return;
+        }
+        // skip in case this sequence is just a container for an alignment 
+        // node with just one child
+        if (node.getChildCount() == 1
+            && getNodeCat(node.getChild(0)).equals("alignment")) {
+            ParseTree alignmentNode = node.getChild(0);
+            if (alignmentNode.getChildCount() == 2) { // one child is the 
+                // alignment operator (^), the other a segment
+                return;
+            }
+        }
+
+        if (getNodeCat(node.getChild(0).getChild(0)).equals("closingspan") ||
+            getNodeCat(node.getChild(node.getChildCount()-1).getChild(0)).equals("span") ||
+            getNodeCat(node.getParent()).equals("startswith") ||
+            getNodeCat(node.getParent()).equals("endswith")) {
+            
+            ArrayList<Integer> classRefs = new ArrayList<Integer>();
+           
+            classRefs.add(200); 
+            Map<String, Object> referenceGroup = KoralObjectGenerator.makeReference(classRefs);
+            // Default is focus(), if deviating catch here
+    
+            referenceGroup.put("operation", "operation:focus");
+            ArrayList<Object> referenceOperands = new ArrayList<Object>();
+            referenceGroup.put("operands", referenceOperands);
+            // Step II: decide where to put the group
+            putIntoSuperObject(referenceGroup);
+            objectStack.push(referenceGroup);
+            stackedObjects++;
+            // visited.add(node.getChild(0));
+        }
+
+        Map<String, Object> sequence = KoralObjectGenerator
+            .makeGroup(KoralOperation.SEQUENCE);
+
+        putIntoSuperObject(sequence);
+        objectStack.push(sequence);
+        stackedObjects++;
+    }
+    
+    private boolean checkIfRecurrsiveMeet(ParseTree node) {
+        boolean recurrence=false;
+        // first segment and second segment are parsed differently in the antlr grammar
+    	ParseTree firstsegment;
+    	ParseTree secondsegment;	
+    	List<ParseTree> segments = getChildrenWithChildren(node); // the others are terminal nodes in the MU syntax
+        firstsegment = segments.get(0);
+        secondsegment = segments.get(1);
+        List<ParseTree> desc1=  getDescendantsWithCat(firstsegment, "meetunion");
+        if (getNodeCat(firstsegment).equals("meetunion")) desc1.add(firstsegment);
+        List<ParseTree> desc2=  getDescendantsWithCat(secondsegment, "meetunion");
+        if (getNodeCat(secondsegment).equals("meetunion")) desc2.add(secondsegment);
+        if ((!desc1.isEmpty())||(!desc2.isEmpty()))
+            recurrence = true;
+        return recurrence;
+    }
+
+/*
+        // check if last child of the parent meet is integer, otherwise the parent meet is a span meet
+        // not used!!!
+
+        private boolean checkIfParentIsSpanMeet (ParseTree node) {
+
+    	boolean parentspanmeet=false;
+    	
+        if (getNodeCat(node.getParent().getParent().getParent()).equals("meetunion") )
+                {
+                    
+                    ParseTree testnode = node.getParent().getParent().getParent();
+                    int lastchild = testnode.getChildCount()-1;
+                    
+                    try {
+                    	Integer.parseInt(testnode.getChild(lastchild).getText());
+                    
+                        }
+                    catch (NumberFormatException nfe)
+                    	{ 
+                        	parentspanmeet=true;
+                    	}     
+                }
+                
+                if (getNodeCat(node.getParent()).equals("meetunion") )
+                {
+                
+                    ParseTree testnode = node.getParent();
+                    int lastchild = testnode.getChildCount()-1;
+                    
+                    try {
+                    Integer.parseInt(testnode.getChild(lastchild).getText());
+                    
+                            }
+                    catch (NumberFormatException nfe)
+                    { 
+                        parentspanmeet=true;
+                    }
+                    
+                }
+            return parentspanmeet;    
+    }
+     */
+
+    private Integer computeSegmentDistance(ParseTree node) {
+    	
+    	int distance=0;
+    	//outer offsets; they are equal
+    	int ooffs1=0;
+    	//first inner offsets; if there is no 1st inner meet, the offsets remain 0
+    	int foffs1=0;
+    	//second inner offsets;  if there is no 2nd inner meet, the offsets remain 0
+    	int soffs1=0;
+    	ooffs1= Integer.parseInt(node.getChild(node.getChildCount()-2).getText());
+    
+    	
+       	ParseTree firstsegment;
+    	ParseTree secondsegment;	
+    	List<ParseTree> segments = getChildrenWithChildren(node); // the others are terminal nodes in the MU syntax
+        firstsegment = segments.get(0);
+        secondsegment = segments.get(1);
+        List<ParseTree> desc1=  getDescendantsWithCat(firstsegment, "meetunion");
+        if (getNodeCat(firstsegment).equals("meetunion")) desc1.add(firstsegment);
+        List<ParseTree> desc2=  getDescendantsWithCat(secondsegment, "meetunion");
+        if (getNodeCat(secondsegment).equals("meetunion")) desc2.add(secondsegment);
+        if (!desc1.isEmpty()) {
+            // we don't need both offs because they are equal!
+            foffs1= Integer.parseInt(desc1.get(0).getChild(desc1.get(0).getChildCount()-2).getText());
+        }
+        
+        if (!desc2.isEmpty()) {
+            // we don't need both offs because they are equal!
+            soffs1= Integer.parseInt(desc2.get(0).getChild(desc2.get(0).getChildCount()-2).getText());
+        }
+
+        if ((foffs1 > 0 && soffs1 > 0 && ooffs1 > 0) ||
+            (foffs1 > 0 && soffs1 > 0 && ooffs1 < 0) ||
+            (foffs1 > 0 && soffs1 == 0 && ooffs1 > 0) ||
+            (foffs1 < 0 && soffs1 == 0 && ooffs1 < 0)) { 
+            if (Math.abs(ooffs1) - Math.abs(foffs1) - 1 >= 0) {
+                distance = Math.abs(ooffs1)-Math.abs(foffs1)-1;
+            } else {
+                System.out.println("Incompatible offset values! The differene between the absolute values of the outer offsets and the first inner offsets should not be smaller than 1!");
+            }
+        } else {
+        	if ((foffs1 < 0 && soffs1 >0 && ooffs1 > 0) ||
+                (foffs1 < 0 && soffs1 ==0 && ooffs1 > 0) ||
+                (foffs1 > 0 && soffs1 ==0 && ooffs1 < 0) ||
+                (foffs1 == 0 && soffs1 >0 && ooffs1 > 0)) { 
+                if (Math.abs(ooffs1) - 1 >= 0) {
+                    distance = Math.abs(ooffs1)-1;
+                } else {
+                    // this is also checked in the processMeetUnion function, when comparing offsets with zero
+                    System.out.println("Incompatible offset values! The absolute values of the outer offsets should not be smaller than 1!");
+                }
+            } else {
+        		if (foffs1 < 0 && soffs1 > 0 && ooffs1 < 0) { 
+                    if (Math.abs(ooffs1) - Math.abs(foffs1) - Math.abs(soffs1) - 1 >= 0) {
+                        distance = Math.abs(ooffs1) - Math.abs(foffs1) - Math.abs(soffs1) - 1;
+                    } else {
+                        System.out.println("Incompatible offset values! The differene between the absolute values of the outer offsets and the sum of the first inner and the second inner offsets should not be smaller than 1!");
+                    }
+                } else {
+        			if (foffs1 == 0 && soffs1 > 0 && ooffs1 < 0) { 
+                        if (Math.abs(ooffs1) - Math.abs(soffs1) - 1 >= 0) {
+                            distance = Math.abs(ooffs1) - Math.abs(soffs1) - 1;
+                        } else {
+                            System.out.println("Incompatible offset values! The differene between the absolute values of the outer offsets and the second inner offsets should not be smaller than 1!");
+                        }
+                    } else {
+        				System.out.println("Incompatible offset values! See the cqp tutorial!");
+        			}
+        		}
+        	}
+        }
+    	return distance;
+    }
+
+   
+    
+    @SuppressWarnings("unchecked")
+	private void processMeetUnion (ParseTree node, boolean putvisited) {
+    	
+		ParseTree firstsegment;
+        ParseTree secondsegment;	
+        List<ParseTree> segments = getChildrenWithChildren(node); // the others are terminal nodes in the MU syntax
+        firstsegment = segments.get(0);
+        secondsegment = segments.get(1);
+	
+        try { 
+            /************** this processes meet with offsets ************/
+    	
+            Integer[] window = new Integer[] {0,0};
+            int offposition1= node.getChildCount()-2;
+            int offposition2= node.getChildCount()-1;
+
+            window = new Integer[] {
+                Integer.parseInt(node.getChild(offposition1).getText()),
+                Integer.parseInt(node.getChild(offposition2).getText())
+            }; // if fails, it is a meet span
+
+            if ((window[0]==0)||(window[1]==0)) {
+                addError(StatusCodes.MALFORMED_QUERY,"The MeetUnion offsets cannot be 0!!");
+                return;
+            } else {
+                if (window[0] > window[1]) {
+                    addError(StatusCodes.MALFORMED_QUERY, "Left meetunion offset is bigger than the right one!");
+                    return;
+                } else {
+                    /****  correct offsets ****/
+                    /******* check if this is a recurrent meetunion *******/ 
+                    boolean recurrence = false;    
+                    recurrence = checkIfRecurrsiveMeet(node); // check if the actual meet is a recursive one
+		
+                    // the numbers in meet function behave like window offsets, not like the quantifiers in repetitions; 
+                    //they are translated into quantifiers here below!
+   
+                    if(window[0].equals(window[1])) {
+                        /***** equal offsets; the distance is exactly window[1]=window[2] ****/
+                        Integer segmentDistance=0; /*** the distance between the first inner meet seq and the second inner meet seq; ****/
+                        /**** create the meet sequence ****/
+                        Map<String, Object> sequence = KoralObjectGenerator.makeGroup(KoralOperation.SEQUENCE);
+                        putIntoSuperObject(sequence);
+                        objectStack.push(sequence);
+                        stackedObjects++;
+					
+                        if (window[0] > 0) {
+                            /** positive equal offsets; segmentDistance is exactly window[0]=window[1] and the order of the segments is kept ***/
+        					
+        			    	/*** first meet segment *****/
+                            // propagate the putvisited to the segments
+        			    	processNode(firstsegment, putvisited);
+
+        			    	if (recurrence == false) {
+        			    		/*** non-recurrence ***/
+        			    		/***** if window=[1, 1] no distance is necessary; ****/
+        			    		if (window[0] > 1) {
+        			    			ArrayList<Object> distances = new ArrayList<Object>();
+        			    	        sequence.put("distances", distances);
+        			    	        ((ArrayList<Object>) sequence.get("distances")).add(
+                                        KoralObjectGenerator.makeDistance("w", window[0]-1 , window[1]-1)
+                                        );
+        			    		}
+        			    	} else {
+        			    		/*** recurrence ****/
+        			    		segmentDistance = computeSegmentDistance(node);
+            			    	//insert empty token repetition 
+            					if (segmentDistance > 0) {
+            						ArrayList<Object> distances = new ArrayList<Object>();
+        			    	        sequence.put("distances", distances);
+        			    	        ((ArrayList<Object>) sequence.get("distances")).add(
+                                        KoralObjectGenerator.makeDistance("w", segmentDistance, segmentDistance)
+                                        );
+            					}
+            				}
+        					
+        			    	/*** second meet segment; put again into stack for recurrence situations ****/
+        			    	if (recurrence == true)
+                                objectStack.push(sequence);
+                            //propagate the putvisited to the segments
+        			    	processNode(secondsegment, putvisited);
+        				} else {
+        					/**** negative equal offsets; the distance is exactly  window[0]=window[1] and the order of the segments is changed ***/
+        					/*** second meet segment ***/
+        				    processNode(secondsegment, putvisited);
+        					   
+                            if (recurrence==false) {
+                                if (window[0] < -1) {
+                                    ArrayList<Object> distances = new ArrayList<Object>();
+                                    sequence.put("distances", distances);
+                                    ((ArrayList<Object>) sequence.get("distances")).add(
+                                        KoralObjectGenerator.makeDistance("w", (-window[1])-1 , (-window[0])-1)
+                                        );
+                                }
+                                //if window=[-1, -1], we just need to change the order of processing the segments
+                            } else {	
+                                segmentDistance = computeSegmentDistance(node);
+                                if (segmentDistance > 0) {
+                                    ArrayList<Object> distances = new ArrayList<Object>();
+                                    sequence.put("distances", distances);
+                                    ((ArrayList<Object>) sequence.get("distances")).add(
+                                        KoralObjectGenerator.makeDistance("w", segmentDistance, segmentDistance)
+                                        );
+                                }
+                            }
+            					
+                            /*** first meet segment; put again into stack for recurrence situations ****/
+                            if (recurrence==true)
+                                objectStack.push(sequence);
+                            processNode(firstsegment, putvisited);
+        				}
+                    } else {
+                        /*** the offsets are not equal; we don't use putvisited here, because we did not implement recursive meet for this situation ***/
+                        if (recurrence==true) {
+                            addError(StatusCodes.MALFORMED_QUERY,"We did not implement recursive meet with different offsets!!");
+                            return;
+                        } else {
+                            if ((window[0] < 0) && (window[1] > 0)) {
+
+                                /***  implementing disjunction of sequences ***/
+        				    
+                                Map<String, Object> disjunction = KoralObjectGenerator.makeGroup(KoralOperation.DISJUNCTION);
+                                putIntoSuperObject(disjunction);
+                                objectStack.push(disjunction);
+                                stackedObjects++;
+        		
+                                /**** first disjunct, keep order of segments, distance is []{0,window[1]} ****/
+        				    
+                                Map<String, Object> firstsequence = KoralObjectGenerator.makeGroup(KoralOperation.SEQUENCE);
+                                putIntoSuperObject(firstsequence);
+                                objectStack.push(firstsequence);
+                                stackedObjects++;
+                                processNode(firstsegment, false);
+                                if (window[1] > 1) {
+                                    ArrayList<Object> distances = new ArrayList<Object>();
+                                    firstsequence.put("distances", distances);
+                                    ((ArrayList<Object>) firstsequence.get("distances")).add(
+                                        KoralObjectGenerator.makeDistance("w", 0, window[1]-1)
+                                        );
+                                }
+                                processNode(secondsegment, false);
+				
+                                /*** second disjunct, change order of segments, distance is []{0,-window[0]} ***/
+        				    
+                                Map<String, Object> secondsequence = KoralObjectGenerator.makeGroup(KoralOperation.SEQUENCE);
+                                objectStack.push(secondsequence);
+                                stackedObjects++;
+                                ((ArrayList<Object>) disjunction.get("operands")).add(secondsequence);
+                                processNode(secondsegment, true);
+                                if (window[0] < -1) {
+                                    ArrayList<Object> distances = new ArrayList<Object>();
+                                    secondsequence.put("distances", distances);
+                                    ((ArrayList<Object>) secondsequence.get("distances")).add(
+                                        KoralObjectGenerator.makeDistance("w", 0, (-window[0])-1)
+                                        );
+                                }
+                                processNode(firstsegment, true);
+                            } else {
+                                /**** offsets are either both > 0 or both < 0 ****/
+                                Map<String, Object> sequence = KoralObjectGenerator.makeGroup(KoralOperation.SEQUENCE);
+                                putIntoSuperObject(sequence);
+                                objectStack.push(sequence);
+                                stackedObjects++;
+                            
+                                if (window[0] > 0 && window[1] > 0) {
+                                    /*** both offsets are positive ***/
+                                    /**** first meet segment ****/
+                               			
+                                    processNode(firstsegment, true);
+                                    ArrayList<Object> distances = new ArrayList<Object>();
+                                    sequence.put("distances", distances);
+                                    ((ArrayList<Object>) sequence.get("distances")).add(
+                                        KoralObjectGenerator.makeDistance("w", window[0]-1, window[1]-1)
+                                        );
+            					
+                                    processNode(secondsegment, true);
+                                } else {
+                                    /*** both offsets are negative; change order of segments ****/
+                                    /**** second meet segment ****/
+    				         		
+                                    processNode(secondsegment, true);
+    				            
+                                    ArrayList<Object> distances = new ArrayList<Object>();
+                                    sequence.put("distances", distances);
+                                    ((ArrayList<Object>) sequence.get("distances")).add(
+                                        KoralObjectGenerator.makeDistance("w", (-window[1])-1,(-window[0])-1)
+                                        );
+
+                                    /**** first meet segment ******/
+                                    processNode(firstsegment, true);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (NumberFormatException nfe) {
+    	
+            /**** process meet  with span ***/
+    	
+            ParseTree firstsequence = firstsegment;
+            ParseTree secondsequence = secondsegment;
+            Map<String, Object> position = parseFrame(node.getChild(node.getChildCount()-1));
+            putIntoSuperObject(position);
+            objectStack.push(position);
+            stackedObjects++; 
+            processNode(node.getChild(node.getChildCount()-1), true);		
+            stackedObjects++;
+   
+            Map<String, Object> spannedsequence = KoralObjectGenerator.makeGroup(KoralOperation.SEQUENCE);
+            putIntoSuperObject(spannedsequence);
+            objectStack.push(spannedsequence);
+            stackedObjects++;		
+            processNode(firstsequence, true); 
+            objectStack.push(spannedsequence); // for recurrence; the firstsequence was first in stack before;
+            processNode(secondsequence, true);  
+            /** distance between first sequence and second sequence can be anything in meet span**/
+            ArrayList<Object> distances = new ArrayList<Object>();
+            spannedsequence.put("distances", distances);
+            ((ArrayList<Object>) spannedsequence.get("distances")).add( KoralObjectGenerator.makeDistance("w", 0, null));
+            spannedsequence.put("inOrder", false);		
+        }
+    }
+
+	@SuppressWarnings("unchecked")
+    /**
+     * empty tokens at beginning/end of sequence
+     * 
+     * @param node
+     */
+    private void processEmptyTokenSequence (ParseTree node) {
+	    
+		Map<String, Object> lastobj= objectStack.getLast();
+		
+        Integer[] minmax = parseEmptySegments(node);
+        // object will be either a repetition group or a single empty token
+			
+        Map<String, Object> object;
+        Map<String, Object> emptyToken = KoralObjectGenerator.makeToken();
+        if (minmax[0] != 1 || minmax[1] == null || minmax[1] != 1) {
+            object = KoralObjectGenerator.makeRepetition(minmax[0], minmax[1]);
+            ((ArrayList<Object>) object.get("operands")).add(emptyToken);
+        } else {
+            object = emptyToken;
+        }
+
+        // check if frames:isAround and ignore the emptyTokens if the case, for the <s> []* token []* </s> situations;
+        if (lastobj.containsKey("frames")) {
+            if (!getNodeCat(node.getParent()).equals("emptyTokenSequenceAround")) {
+                putIntoSuperObject(object);
+                objectStack.push(object);
+                stackedObjects++;
+            }
+        } else {
+            putIntoSuperObject(object);
+            objectStack.push(object);
+            stackedObjects++;
+        }
+    }
+
+
+    private void processQueryRef (ParseTree node) {
+
+        String queryNameStr = "";
+        
+        if (getNodeCat(node.getChild(2)).equals("user")) {
+            queryNameStr = node.getChild(2).getText();
+            queryNameStr += '/';
+            queryNameStr += node.getChild(4).getText();
+        }
+        else {
+            queryNameStr = node.getChild(2).getText();
+        }
+
+        Map<String, Object> object = KoralObjectGenerator.makeQueryRef(queryNameStr);
+        putIntoSuperObject(object);
+        objectStack.push(object);
+        stackedObjects++;
+    }
+    
+
+    private void processEmptyTokenSequenceClass (ParseTree node) {
+        int classId = 1;
+        if (hasChild(node, "spanclass_id")) {
+            classId = Integer.parseInt(
+                node.getChild(1).getChild(0).toStringTree(parser));
+        }
+        Map<String, Object> classGroup = KoralObjectGenerator
+            .makeSpanClass(classId);
+        addHighlightClass(classId);
+        putIntoSuperObject(classGroup);
+        objectStack.push(classGroup);
+        stackedObjects++;
+    }
+
+
+    private void processToken (ParseTree node) {
+        Map<String, Object> token = KoralObjectGenerator.makeToken();
+        // handle negation
+        List<ParseTree> negations = getChildrenWithCat(node, "!");
+        int termOrTermGroupChildId = 1;
+        boolean negated = false;
+        if (negations.size() % 2 == 1) {
+            negated = true;
+            termOrTermGroupChildId += negations.size();
+        }
+
+        if (getNodeCat(node.getChild(0)).equals("key")) {
+            // no 'term' child, but direct key specification: process here
+            Map<String, Object> term = KoralObjectGenerator
+                .makeTerm();
+
+            String key = node.getChild(0).getText();
+
+            if (getNodeCat(node.getChild(0).getChild(0)).equals("regex")) {
+                //  isRegex = true;
+                term.put("type", "type:regex");
+
+                // fixme: use stream with offset to get text!
+                // TokenStream stream = parser.getTokenStream();
+                // key = stream.getText(node.getChild(0).getSourceInterval());
+                String first = key.substring(0, 1);
+                String last = key.substring(key.length()-1, key.length());
+                key = key.substring(1, key.length() - 1);
+                // treat the doubleqoutes and singlequoutes inside regex!
+                if (first.equals("\"") && last.equals("\"")) {
+                    key = key.replaceAll("\"\"", "\"");
+                }
+
+                if (first.equals("'") && last.equals("'")) {
+                    key =  key.replaceAll("''", "'");
+                }
+            }
+            term.put("layer", "orth");
+            term.put("key", key);
+            KoralMatchOperator matches = negated ? KoralMatchOperator.NOT_EQUALS
+                : KoralMatchOperator.EQUALS;
+            term.put("match", matches.toString());
+            ParseTree flagNode = getFirstChildWithCat(node, "flag");
+            if (flagNode != null) {
+                ArrayList<String> flags = new ArrayList<String>();
+                // substring removes leading slash '/'
+                String flag = getNodeCat(flagNode.getChild(0)).substring(1);
+                if (flag.contains("c") || flag.contains("C"))
+                    flags.add("flags:caseInsensitive");
+                if (flag.contains("d") || flag.contains("D"))
+                    flags.add("flags:diacriticsInsensitive");
+                
+                if (flag.contains("l")|| flag.contains("L")) { 
+                    ParseTree keyNode = node.getChild(0);
+
+                    // Get stream from hidden channel
+                    TokenStream stream = parser.getTokenStream();
+                    key = stream.getText(keyNode.getChild(0).getSourceInterval());
+                    key = key.substring(1, key.length()-1).replaceAll("\\\\\\\\","\\\\").replaceAll("\\\\'", "'");
+                    //override key and type:string
+                    term.put("key", key);
+                    term.put("type", "type:string");
+                }
+
+                if (!flags.isEmpty())
+                    term.put("flags", flags);
+            }
+            token.put("wrap", term);
+        }
+
+        else {
+            // child is 'term' or 'termGroup' -> process in extra method
+            //check if the token has a position condition
+        	
+        	if  (hasChild(node.getChild(termOrTermGroupChildId), "position")) {
+        		processPosition(getFirstChildWithCat(node.getChild(termOrTermGroupChildId), "position"));
+        	}
+        	
+        	Map<String, Object> termOrTermGroup = parseTermOrTermGroup(
+                node.getChild(termOrTermGroupChildId), negated);
+            token.put("wrap", termOrTermGroup);
+        }
+        putIntoSuperObject(token);
+        visited.addAll(getChildren(node));
+    }
+    
+    /*
+    !! not used!
+    private void processTokenStruct (ParseTree node) {
+    	// differs from processToken because it doesn't require/have [] around the token
+        Map<String, Object> token = KoralObjectGenerator.makeToken();
+        // handle negation
+        List<ParseTree> negations = getChildrenWithCat(node, "!");
+        int termOrTermGroupChildId = 0;
+        boolean negated = false;
+      //  boolean isRegex = false;
+        if (negations.size() % 2 == 1) {
+            negated = true;
+            termOrTermGroupChildId += negations.size();
+        }
+
+        if (getNodeCat(node.getChild(0)).equals("key")) {
+            // no 'term' child, but direct key specification: process here
+            Map<String, Object> term = KoralObjectGenerator
+                    .makeTerm();
+
+            String key = node.getChild(0).getText();
+
+            if (getNodeCat(node.getChild(0).getChild(0)).equals("regex")) {
+            //    isRegex = true;
+                term.put("type", "type:regex");
+
+                // fixme: use stream with offset to get text!
+                // TokenStream stream = parser.getTokenStream();
+                // key = stream.getText(node.getChild(0).getSourceInterval());
+                key = key.substring(1, key.length() - 1);
+            }
+            term.put("layer", "orth");
+            term.put("key", key);
+            KoralMatchOperator matches = negated ? KoralMatchOperator.NOT_EQUALS
+                    : KoralMatchOperator.EQUALS;
+            term.put("match", matches.toString());
+            ParseTree flagNode = getFirstChildWithCat(node, "flag");
+            if (flagNode != null) {
+                ArrayList<String> flags = new ArrayList<String>();
+                // substring removes leading slash '/'
+                String flag = getNodeCat(flagNode.getChild(0)).substring(1);
+                if (flag.contains("c") || flag.contains("C"))
+                    flags.add("flags:caseInsensitive");
+                if (flag.contains("d") || flag.contains("D"))
+                    flags.add("flags:diacriticsInsensitive");
+                if (flag.contains("l")|| flag.contains("L"))
+                { 
+                		ParseTree keyNode = node.getChild(0);
+
+        				// Get stream from hidden channel
+        				TokenStream stream = parser.getTokenStream();
+        				key = stream.getText(keyNode.getChild(0).getSourceInterval());
+        				key = key.substring(1, key.length()-1).replaceAll("\\\\\\\\","\\\\").replaceAll("\\\\'", "'");
+        				//override key and type:string
+        				term.put("key", key);
+        				term.put("type", "type:string");
+                }
+                if (!flags.isEmpty()) {
+                    term.put("flags", flags);
+                }
+            }
+            token.put("wrap", term);
+        }
+        else {
+            // child is 'term' or 'termGroup' -> process in extra method
+            Map<String, Object> termOrTermGroup = parseTermOrTermGroup(
+                    node.getChild(termOrTermGroupChildId), negated);
+            token.put("wrap", termOrTermGroup);
+        }
+        putIntoSuperObject(token);
+        visited.addAll(getChildren(node));
+    }
+
+ */
+
+    /**
+     * Processes an 'alignment' node. These nodes represent alignment
+     * anchors
+     * which introduce an alignment ruler in KWIC display. The
+     * serialization
+     * for this expects the two segments to the left and to the right
+     * of each
+     * anchor to be wrapped in classes, then these classes are
+     * referenced in
+     * the <tt>alignment</tt> array of the request tree.
+     * 
+     * @param node
+     */
+    private void processAlignment (ParseTree node) {
+        int i = 1;
+        if (node.getChild(0).getText().equals("^")) {
+            i = 0; // if there is no first child (anchor is at extreme left or
+                   // right of segment), start counting at 0 in the loop
+        }
+        // for every alignment anchor, get its left and right child and register
+        // these to be wrapped in classes.
+        for (; i < node.getChildCount(); i += 2) {
+            int alignmentFirstArg = -1;
+            int alignmentSecondArg = -1;
+            ParseTree leftChild = node.getChild(i - 1);
+            ParseTree rightChild = node.getChild(i + 1);
+            if (leftChild != null) {
+                if (!classWrapRegistry.containsKey(leftChild)) {
+                    alignmentFirstArg = classCounter++;
+                    classWrapRegistry.put(leftChild, alignmentFirstArg);
+                }
+                else {
+                    alignmentFirstArg = classWrapRegistry.get(leftChild);
+                }
+            }
+            if (rightChild != null) {
+                if (!classWrapRegistry.containsKey(rightChild)) {
+                    alignmentSecondArg = classCounter++;
+                    classWrapRegistry.put(rightChild, alignmentSecondArg);
+                }
+                else {
+                    alignmentSecondArg = classWrapRegistry.get(rightChild);
+                }
+            }
+            addAlignment(alignmentFirstArg, alignmentSecondArg);
+        }
+    }
+
+    
+    private void processSpan (ParseTree node) {
+        List<ParseTree> negations = getChildrenWithCat(node, "!");
+        boolean negated = false;
+        if (negations.size() % 2 == 1)
+            negated = true;
+        Map<String, Object> span = KoralObjectGenerator.makeSpan();
+        Map<String, Object> wrappedTerm = KoralObjectGenerator
+            .makeTerm();
+        span.put("wrap", wrappedTerm);
+        ParseTree keyNode = getFirstChildWithCat(node, "skey");
+        ParseTree layerNode = getFirstChildWithCat(node, "layer");
+        ParseTree foundryNode = getFirstChildWithCat(node, "foundry");
+        ParseTree termOpNode = getFirstChildWithCat(node, "termOp");
+        ParseTree termNode = getFirstChildWithCat(node, "term");
+        ParseTree termGroupNode = getFirstChildWithCat(node, "termGroup");
+        if (foundryNode != null)
+            wrappedTerm.put("foundry", foundryNode.getText());
+        if (layerNode != null) {
+            String layer = layerNode.getText();
+            if (layer.equals("base"))
+                layer = "lemma";
+            wrappedTerm.put("layer", layer);
+        }
+        
+        // modified for the new span with WORD instead of key
+        String key = "";
+        if (keyNode != null) {
+            // check if key is regular expression
+            if (hasChild(keyNode, "regex")) {
+                // remove leading/trailing double quotes
+                key = key.substring(1, key.length() - 1);
+                wrappedTerm.put("type", "type:regex");
+            }
+            else
+                key = keyNode.getText();
+        }
+        else key = node.getChild(node.getChildCount()-2).getText();
+       
+        wrappedTerm.put("key", key);
+        
+        if (termOpNode != null) {
+            String termOp = termOpNode.getText();
+            if (termOp.equals("=="))
+                wrappedTerm.put("match", KoralMatchOperator.EQUALS.toString());
+            else if (termOp.equals("!="))
+                wrappedTerm.put("match", KoralMatchOperator.NOT_EQUALS.toString());
+        }
+
+        if (termNode != null) {
+            Map<String, Object> termOrTermGroup = parseTermOrTermGroup(
+                termNode, negated, "span");
+            span.put("attr", termOrTermGroup);
+        }
+
+        if (termGroupNode != null) {
+            Map<String, Object> termOrTermGroup = parseTermOrTermGroup(
+                termGroupNode, negated, "span");
+            span.put("attr", termOrTermGroup);
+        }
+        putIntoSuperObject(span);    
+        objectStack.push(span);
+        stackedObjects++;
+    }
+
+
+    private void processDisjunction (ParseTree node) {
+        Map<String, Object> disjunction = KoralObjectGenerator
+            .makeGroup(KoralOperation.DISJUNCTION);
+        putIntoSuperObject(disjunction);
+        objectStack.push(disjunction);
+        stackedObjects++;
+    }
+
+
+    private void processPosition (ParseTree node) {
+        Map<String, Object> position = parseFrame(node.getChild(0));
+        
+        putIntoSuperObject(position);
+        objectStack.push(position);
+        stackedObjects++;
+
+        // for lboud and rbound, when span is child of position;
+        if (hasChild(node, "span")) {
+        	ParseTree spanchildnode = getFirstChildWithCat (node, "span");
+        	processSpan(spanchildnode);
+        	
+        	objectStack.pop();
+        	stackedObjects=stackedObjects-2;
+        }
+    }
+
+
+    private void processRelation (ParseTree node) {
+        Map<String, Object> relationGroup = KoralObjectGenerator
+            .makeGroup(KoralOperation.RELATION);
+        Map<String, Object> relation = KoralObjectGenerator
+            .makeRelation();
+        Map<String, Object> term = KoralObjectGenerator.makeTerm();
+        relationGroup.put("relation", relation);
+        relation.put("wrap", term);
+
+        if (node.getChild(0).getText().equals("dominates")) {
+            term.put("layer", "c");
+        }
+        else if (node.getChild(0).getText().equals("dependency")) {
+            term.put("layer", "d");
+        }
+        
+        ParseTree relSpec = getFirstChildWithCat(node, "relSpec");
+        ParseTree repetition = getFirstChildWithCat(node, "repetition");
+        if (relSpec != null) {
+            ParseTree foundry = getFirstChildWithCat(relSpec, "foundry");
+            ParseTree layer = getFirstChildWithCat(relSpec, "layer");
+            ParseTree key = getFirstChildWithCat(relSpec, "key");
+            if (foundry != null)
+                term.put("foundry", foundry.getText());
+            if (layer != null)
+                term.put("layer", layer.getText());
+            if (key != null)
+                term.put("key", key.getText());
+        }
+        if (repetition != null) {
+            Integer[] minmax = parseRepetition(repetition);
+            relation.put("boundary",
+                         KoralObjectGenerator.makeBoundary(minmax[0], minmax[1]));
+        }
+        putIntoSuperObject(relationGroup);
+        objectStack.push(relationGroup);
+        stackedObjects++;
+    }
+
+
+    private void processSpanClass (ParseTree node) {
+        // Step I: get info
+        int classId = 1;
+
+        if (getNodeCat(node.getChild(0)).equals("@1")) {
+            classId = 2;
+        }
+        // check meetunion
+        if (getNodeCat(node.getParent().getParent()).equals("meetunion")) {
+            // we want to maintain only the class of the focus of the leftmost  meet!		
+            if (!hasDescendantWithCat(node, "meetunion") &&
+                (!getNodeCat(node.getParent().getParent().getParent()).equals("meetunion"))) {
+                Map<String, Object> classGroup = KoralObjectGenerator
+                    .makeSpanClass(classId);
+                
+                if (classId==1)
+                    addHighlightClass(classId);
+                putIntoSuperObject(classGroup);
+                objectStack.push(classGroup);
+                stackedObjects++;
+            }
+        } else {
+            Map<String, Object> classGroup = KoralObjectGenerator
+                .makeSpanClass(classId);
+            
+            addHighlightClass(classId);
+            putIntoSuperObject(classGroup);
+            objectStack.push(classGroup);
+            stackedObjects++;
+        }
+    }
+
+    private void processMatching (ParseTree node) {
+        // Step I: get info
+        ArrayList<Integer> classRefs = new ArrayList<Integer>();
+        ClassRefOp classRefOp = null;
+
+        if (getNodeCat(node.getChild(2)).equals("spanclass_id")) {
+            ParseTree spanNode = node.getChild(2);
+            for (int i = 0; i < spanNode.getChildCount() - 1; i++) {
+                String ref = spanNode.getChild(i).getText();
+                if (ref.equals("|") || ref.equals("&")) {
+                    classRefOp = ref.equals("|") ? ClassRefOp.INTERSECTION : ClassRefOp.UNION;
+                }
+                else {
+                    try {
+                        int classRef = Integer.parseInt(ref);
+                        classRefs.add(classRef);
+                    }
+                    catch (NumberFormatException e) {
+                        String err = "The specified class reference in the "
+                            + "shrink/split-Operator is not a number.";
+                        addError(StatusCodes.INVALID_CLASS_REFERENCE, err);
+                    }
+                }
+            }
+        }
+        else {
+            classRefs.add(1); // default
+        }
+
+        Map<String, Object> referenceGroup = KoralObjectGenerator
+            .makeReference(classRefs);
+
+        String type = node.getChild(0).toStringTree(parser);
+        // Default is focus(), if deviating catch here
+        if (type.equals("MU"))
+            referenceGroup.put("operation", "operation:focus");
+        if (classRefOp != null) {
+            referenceGroup.put("classRefOp", classRefOp.toString());
+        }
+        ArrayList<Object> referenceOperands = new ArrayList<Object>();
+        referenceGroup.put("operands", referenceOperands);
+        // Step II: decide where to put the group
+        putIntoSuperObject(referenceGroup);
+        objectStack.push(referenceGroup);
+        stackedObjects++;
+        visited.add(node.getChild(0));
+    }
+
+
+    private void processSubMatch (ParseTree node) {
+        Map<String, Object> submatch = KoralObjectGenerator
+            .makeReference(null);
+        submatch.put("operands", new ArrayList<Object>());
+        ParseTree startpos = getFirstChildWithCat(node, "startpos");
+        ParseTree length = getFirstChildWithCat(node, "length");
+        ArrayList<Integer> spanRef = new ArrayList<Integer>();
+        spanRef.add(Integer.parseInt(startpos.getText()));
+        if (length != null) {
+            spanRef.add(Integer.parseInt(length.getText()));
+        }
+        submatch.put("spanRef", spanRef);
+        putIntoSuperObject(submatch);
+        objectStack.push(submatch);
+        stackedObjects++;
+        visited.add(node.getChild(0));
+    }
+
+
+    /**
+     * Creates meta field in requestMap, later filled by terms
+     * 
+     * @param node
+     */
+    private void processMeta (ParseTree node) {
+        addWarning("You used the 'meta' keyword in a CQP query. This"
+                   + " feature is currently not supported. Please use virtual "
+                   + "collections to restrict documents by metadata.");
+        CollectionQueryProcessor cq = new CollectionQueryProcessor(
+            node.getChild(1).getText());
+        requestMap.put("collection", cq.getRequestMap().get("collection"));
+        visited.addAll(getChildren(node));
+    }
+    
+
+    @SuppressWarnings("unchecked")
+    private void processWithin (ParseTree node) {
+        ParseTree domainNode = node.getChild(1);
+        String domain = getNodeCat(getFirstChildWithCat(domainNode,"skey").getChild(0));
+        Map<String, Object> span = KoralObjectGenerator
+            .makeSpan(domain);
+        Map<String, Object> queryObj = (Map<String, Object>) requestMap
+            .get("query");
+        ArrayList<KoralFrame> frames = new ArrayList<KoralFrame>();
+        frames.add(KoralFrame.IS_AROUND);
+        Map<String, Object> contains = KoralObjectGenerator
+            .makePosition(frames);
+        ArrayList<Object> operands = (ArrayList<Object>) contains
+            .get("operands");
+        operands.add(span);
+        
+        operands.add(queryObj);
+        requestMap.put("query", contains);
+        visited.add(node.getChild(0));
+        visited.add(node.getChild(1));
+    }
+
+
+    /**
+     * Parses a repetition node
+     * 
+     * @param node
+     * @return A two-element array, of which the first element is an
+     *         int representing the minimal number of repetitions of
+     *         the quantified element, and the second element
+     *         representing the maximal number of repetitions
+     */
+    private Integer[] parseRepetition (ParseTree node) {
+        Integer min = 0, max = 0;
+        boolean maxInfinite = false;
+        // (repetition) node can be of two types: 'kleene' or 'range'
+        ParseTree repetitionTypeNode = node.getChild(0);
+        String repetitionType = getNodeCat(repetitionTypeNode);
+        if (repetitionType.equals("kleene")) {
+            // kleene operators (+ and *) as well as optionality (?)
+            String kleeneOp = repetitionTypeNode.getText();
+            if (kleeneOp.equals("*")) {
+                maxInfinite = true;
+            }
+            else if (kleeneOp.equals("+")) {
+                min = 1;
+                maxInfinite = true;
+            }
+            if (kleeneOp.equals("?")) {
+                max = 1;
+            }
+        }
+        else {
+            // Range node of form "{ min , max }" or "{ max }" or
+            // "{ , max }" or "{ min , }"
+            ParseTree minNode = getFirstChildWithCat(repetitionTypeNode, "min");
+            ParseTree maxNode = getFirstChildWithCat(repetitionTypeNode, "max");
+            if (maxNode != null)
+                max = Integer.parseInt(maxNode.getText());
+            else
+                maxInfinite = true;
+            // min is optional: if not specified, min = max
+            if (minNode != null)
+                min = Integer.parseInt(minNode.getText());
+            else if (hasChild(repetitionTypeNode, ","))
+                min = 0;
+            else {
+                min = max;
+                
+                // addWarning("Your query contains a segment of the form {n}, where n is some number. This expression is ambiguous. " +
+                // "It could mean a repetition (\"Repeat the previous element n times!\") or a word form that equals the number, "+
+                // "enclosed by a \"class\" (which is denoted by braces like '{x}', see the documentation on classes)."+
+                // "KorAP has by default interpreted the segment as a repetition statement. If you want to express the"+
+                // "number as a word form inside a class, use the non-shorthand form {[orth=n]}.");
+            }
+        }
+
+        if (maxInfinite) {
+            max = null;
+        }
+
+        return new Integer[] { min, max };
+    }
+
+
+    private Map<String, Object> parseFrame (ParseTree node) {
+        String operator = node.toStringTree(parser).toLowerCase();
+
+        ArrayList<KoralFrame> frames = new ArrayList<KoralFrame>();
+        if (getNodeCat((node).getParent()).equals("meetunion")) {
+            frames.add(KoralFrame.IS_AROUND);
+        }
+
+        if (operator.contains("startswith")) {
+            frames.add(KoralFrame.STARTS_WITH);
+            frames.add(KoralFrame.MATCHES);
+        }
+
+        if (operator.contains("endswith")) {
+            frames.add(KoralFrame.ENDS_WITH);
+            frames.add(KoralFrame.MATCHES);
+        }
+
+        if (operator.contains("isaround")) {
+            frames.add(KoralFrame.IS_AROUND);
+        }
+    	
+        if (operator.contains("matches")) {
+            frames.add(KoralFrame.MATCHES);
+        }
+       
+        if (operator.contains("rbound")) {
+            frames.add(KoralFrame.ENDS_WITH);
+            frames.add(KoralFrame.MATCHES);
+        }
+
+        if (operator.contains("lbound")) {
+	   		frames.add(KoralFrame.STARTS_WITH);
+	   		frames.add(KoralFrame.MATCHES);
+        }
+        
+        // this is to eliminate doubled KoralFrame.MATCHES; to delete if cleared in the ifs above
+        int matchescount=0;
+        for (int f=0; f < frames.size(); f++) {
+            KoralFrame frame = frames.get(f);
+            String framevalue = frame.toString();
+            if (framevalue.equals("frames:matches")) {
+                matchescount++;
+                if (matchescount > 1)
+                    frames.remove(f);
+            }
+    	}
+       
+        return KoralObjectGenerator.makePosition(frames);
+    }
+
+
+    private Map<String, Object> parseTermOrTermGroup (ParseTree node,
+                                                      boolean negated) {
+        return parseTermOrTermGroup(node, negated, "token");
+    }
+
+    
+    /**
+     * Parses a (term) or (termGroup) node
+     * 
+     * @param node
+     * @param negatedGlobal
+     *            Indicates whether the term/termGroup is globally
+     *            negated, e.g. through a negation operator preceding
+     *            the related token like "![base=foo]". Global
+     *            negation affects the term's "match" parameter.
+     * @param mode
+     *            'token' or 'span' (tokens and spans are treated
+     *            differently).
+     * @return A term or termGroup object, depending on input
+     */
+    @SuppressWarnings("unchecked")
+    private Map<String, Object> parseTermOrTermGroup (ParseTree node,
+                                                      boolean negatedGlobal, String mode) {
+
+        String nodeCat = getNodeCat(node);
+
+        if (nodeCat.equals("term")) {
+
+            // Term is defined recursively with non-necessary brackets; exception for lbound and rbound; // is it necessary to verify termGroup too??
+            if (getNodeCat(node.getChild(0)).equals("(") &&
+                ((getNodeCat(node.getChild(1)).equals("term")) ||
+                 getNodeCat(node.getChild(1)).equals("termGroup"))){
+                return parseTermOrTermGroup(node.getChild(1), negatedGlobal,
+                                            mode);
+            };
+
+            //Term is negated outside brackets
+            if (getNodeCat(node.getChild(0)).equals("!") && getNodeCat(node.getChild(1)).equals("(")) { 
+                // negate negatedGLobal
+            	return parseTermOrTermGroup(node.getChild(2), !negatedGlobal, mode);
+            }
+
+            String key = null;
+            String value = null;
+            Map<String, Object> term = KoralObjectGenerator
+                .makeTerm();
+            // handle negation
+            boolean negated = negatedGlobal;
+            boolean isRegex = false;
+            List<ParseTree> negations = getChildrenWithCat(node, "!");
+            if (negations.size() % 2 == 1)
+                negated = !negated;
+            // retrieve possible nodes
+            ParseTree keyNode = getFirstChildWithCat(node, "key");
+            ParseTree valueNode = getFirstChildWithCat(node, "value");
+            ParseTree layerNode = getFirstChildWithCat(node, "layer");
+            ParseTree foundryNode = getFirstChildWithCat(node, "foundry");
+            ParseTree termOpNode = getFirstChildWithCat(node, "termOp");
+            ParseTree flagNode = getFirstChildWithCat(node, "flag");
+            // process foundry
+            if (foundryNode != null)
+                term.put("foundry", foundryNode.getText());
+
+            //  process regex
+			
+            if (getNodeCat(keyNode.getChild(0)).equals("regex")) {
+                isRegex = true;
+                term.put("type", "type:regex");
+                // remove leading and trailing quotes
+                //process verbatim flag %l
+                if (flagNode!=null) {
+                    if (getNodeCat(flagNode.getChild(0)).contains("l") ||
+                        getNodeCat(flagNode.getChild(0)).contains("L"))  {
+
+                        // Get stream from hidden channel
+                        TokenStream stream = parser.getTokenStream();
+                        key = stream.getText(keyNode.getChild(0).getSourceInterval());
+                        key = key.substring(1, key.length()-1).replaceAll("\\\\\\\\","\\\\").replaceAll("\\\\'", "'");
+                        //override with type:string
+                        term.put("type", "type:string");
+    				} else {
+        				key = keyNode.getText();
+        				key = key.substring(1, key.length() - 1);
+                	}
+                }
+
+    			else {
+    				key = keyNode.getText();
+    				key = key.substring(1, key.length() - 1);
+    			};
+            }
+
+            if (mode.equals("span"))
+                term.put("value", key);
+            else
+                term.put("key", key);
+
+            // process layer: map "base" -> "lemma"
+            if (layerNode != null) {
+                String layer = layerNode.getText();
+                if (mode.equals("span")) {
+                    term.put("key", layer);
+                }
+                else if (mode.equals("token")) {
+                    if (layer.equals("base")) {
+                        layer = "lemma";
+                    }
+                    else if (layer.equals("punct")) {
+                        layer = "orth";
+                        // will override "type":"type:regex"
+                        term.put("type", "type:punct");
+                    }
+                    term.put("layer", layer);
+                }
+            }
+
+            // process value
+            if (valueNode != null && getNodeCat(valueNode.getChild(0)).equals("regex")) {
+                isRegex = true;
+                term.put("type", "type:regex");
+                // remove leading and trailing quotes
+                value = valueNode.getText();
+                value = value.substring(1, value.length() - 1);
+                term.put("value", value);
+			};
+				
+            // process operator ("match" property)
+            if (termOpNode != null) {
+                String termOp = termOpNode.getText();
+                negated = termOp.contains("!") ? !negated : negated;
+                if (!negated)
+                    term.put("match", "match:eq");
+                else
+                    term.put("match", "match:ne");
+            }
+
+            // process possible flags
+            if (flagNode != null) {
+                // substring removes leading %
+                String flag = getNodeCat(flagNode.getChild(0)).substring(1);
+                
+                // EM: handling flagnode as layer
+                if (node.getChild(1).equals(flagNode)){
+                    if (layerNode == null) {
+                        term.put("layer", flag);
+                    }
+                    else {
+                        String layer = (String) term.get("layer");
+                        term.put("layer", flag+layer);
+                    }
+                    
+                    // EM: check for other flags
+                    List<ParseTree> list = getChildrenWithCat(node, "flag");
+                    for (int i=1; i < list.size(); i++) {
+                        ParseTree n = list.get(i);
+                        flag = getNodeCat(n.getChild(0)).substring(1);
+                        parseFlag(flag, isRegex, key, term);
+                    }
+                }
+                else {
+                    term = parseFlag(flag, isRegex, key, term);
+                }
+            }
+            return term;
+        }
+        else if (nodeCat.equals("termGroup")) {
+
+            // TermGroup is defined recursive with non-necessary brackets
+            if (getNodeCat(node.getChild(0)).equals("(") && node.getChildCount() == 3) {
+                return parseTermOrTermGroup(node.getChild(1), negatedGlobal,
+                                            mode);
+            };
+            
+            //TermGroup is negated outside brackets
+            if (getNodeCat(node.getChild(0)).equals("!") && getNodeCat(node.getChild(1)).equals("(")) { 
+                // negate negatedGLobal
+            	return parseTermOrTermGroup(node.getChild(2), !negatedGlobal,
+                                            mode);
+            }
+
+            // For termGroups, establish a boolean relation between
+            // operands and recursively call this function with
+            // the term or termGroup operands
+            Map<String, Object> termGroup = null;
+            ParseTree leftOp = null;
+            ParseTree rightOp = null;
+
+
+            // check for leading/trailing parentheses
+            if (!getNodeCat(node.getChild(0)).equals("("))
+                leftOp = node.getChild(0);
+            else
+                leftOp = node.getChild(1);
+
+            if (!getNodeCat(node.getChild(node.getChildCount() - 1))
+                .equals(")"))
+                rightOp = node.getChild(node.getChildCount() - 1);
+            else
+                rightOp = node.getChild(node.getChildCount() - 2);
+
+            // establish boolean relation
+            ParseTree boolOp = getFirstChildWithCat(node, "boolOp");
+
+			// Create group// De Morgan's laws
+            if (boolOp.getText().equals("&")) {
+                if (!negatedGlobal) {
+                    termGroup = KoralObjectGenerator
+                        .makeTermGroup(KoralTermGroupRelation.AND);
+                } else {
+                    termGroup = KoralObjectGenerator
+                        .makeTermGroup(KoralTermGroupRelation.OR);
+                }
+            }
+            else if (boolOp.getText().equals("|")) {
+                if (!negatedGlobal) {
+                    termGroup = KoralObjectGenerator
+                        .makeTermGroup(KoralTermGroupRelation.OR);
+                } else {
+                    termGroup = KoralObjectGenerator
+                        .makeTermGroup(KoralTermGroupRelation.AND);
+                }
+            }
+          
+            ArrayList<Object> operands = (ArrayList<Object>) termGroup
+                .get("operands");
+            // recursion with left/right operands
+            operands.add(parseTermOrTermGroup(leftOp, negatedGlobal, mode));
+            operands.add(parseTermOrTermGroup(rightOp, negatedGlobal, mode));
+            return termGroup;
+        }
+        return null;
+    }
+
+
+    private Map<String, Object> parseFlag (String flag, boolean isRegex,
+                                           String key, Map<String, Object> term) {
+        ArrayList<String> flags = new ArrayList<String>();
+
+        if (flag.contains("c")||flag.contains("C"))
+            flags.add("flags:caseInsensitive");
+    
+        if (flag.contains("d")|| flag.contains("D"))
+        	flags.add("flags:diacriticsInsensitive");
+     
+        if (!flags.isEmpty())
+            term.put("flags", flags);
+       
+        return term;
+    }
+    
+    /**
+     * Puts an object into the operands list of its governing (or
+     * "super") object which had been placed on the
+     * {@link #objectStack} before and is still on top of the stack.
+     * If this is the top object of the tree, it is put there instead
+     * of into some (non-existent) operand stack.
+     * 
+     * @param object
+     *            The object to be inserted
+     */
+    private void putIntoSuperObject (Map<String, Object> object) {
+        putIntoSuperObject(object, 0);
+    }
+
+
+    /**
+     * Puts an object into the operands list of its governing (or
+     * "super") object which had been placed on the
+     * {@link #objectStack} before. If this is the top object of the
+     * tree, it is put there instead of into some (non-existent)
+     * operand stack.
+     * 
+     * @param object
+     *            The object to be inserted
+     * @param objStackPosition
+     *            Indicated the position of the super object on the
+     *            {@link #objectStack} (in case not the top element of
+     *            the stack is the super object.
+     */
+    @SuppressWarnings({ "unchecked" })
+    private void putIntoSuperObject (Map<String, Object> object,
+                                     int objStackPosition) {
+        if (objectStack.size() > objStackPosition) {
+            ArrayList<Object> topObjectOperands = (ArrayList<Object>) objectStack
+                .get(objStackPosition).get("operands");
+            if (object.get("@type").equals("koral:span")) {
+            	if (!topObjectOperands.isEmpty() && objectStack.get(objStackPosition) .containsKey("frames"))
+                    topObjectOperands.add(0, object);
+            	else
+                    topObjectOperands.add(object);
+            }
+            else
+            	topObjectOperands.add(object);
+        }
+        else
+            requestMap.put("query", object);
+    }
+
+
+    /**
+     * Parses the min and max attributes for a boundary object as
+     * defined in
+     * a distance node.
+     * 
+     * @param distanceNode
+     *            A node of category 'distance'
+     * @return An array of two fields, where the first is the min
+     *         value and the
+     *         second is the max value and may be null.
+     
+    private Integer[] parseDistance (ParseTree distanceNode) {
+        int emptyTokenSeqIndex = getNodeCat(distanceNode).equals("distance") ? 0
+                : 2;
+        Integer[] minmax = parseEmptySegments(
+                distanceNode.getChild(emptyTokenSeqIndex));
+        Integer min = minmax[0];
+        Integer max = minmax[1];
+        //        min++;
+        //        if (max != null)
+        //            max++;
+        return new Integer[] { min, max };
+    }
+    */
+
+
+    private Integer[] parseEmptySegments (ParseTree emptySegments) {
+        Integer min = 0;
+        Integer max = 0;
+        ParseTree child;
+        for (int i = 0; i < emptySegments.getChildCount(); i++) {
+            child = emptySegments.getChild(i);
+            ParseTree nextSibling = emptySegments.getChild(i + 1);
+            if (child.toStringTree(parser).equals("(emptyToken [ ])")) {
+                if (nextSibling != null
+                    && getNodeCat(nextSibling).equals("repetition")) {
+                    Integer[] minmax = parseRepetition(nextSibling);
+                    min += minmax[0];
+                    if (minmax[1] != null) {
+                        max += minmax[1];
+                    }
+                    else {
+                        max = null;
+                    }
+                }
+                else {
+                    min++;
+                    max++;
+                }
+            }
+        }
+        // min = cropToMaxValue(min);
+        // max = cropToMaxValue(max);
+        return new Integer[] { min, max };
+    }
+
+
+    /**
+     * @param query
+     * @return
+     */
+    private ParserRuleContext parseCQPQuery (String query) {
+        Lexer lexer = new CQPLexer((CharStream) null);
+        ParserRuleContext tree = null;
+        Antlr4DescriptiveErrorListener errorListener = new Antlr4DescriptiveErrorListener(
+            query);
+        // Like p. 111
+        CommonTokenStream tokens = null;
+        try {
+            // Tokenize input data
+            ANTLRInputStream input = new ANTLRInputStream(query);
+            lexer.setInputStream(input);
+            tokens = new CommonTokenStream(lexer);
+            
+            parser = new CQPParser(tokens);
+
+            // Don't throw out erroneous stuff
+            parser.setErrorHandler(new BailErrorStrategy());
+            lexer.removeErrorListeners();
+            lexer.addErrorListener(errorListener);
+            parser.removeErrorListeners();
+            parser.addErrorListener(errorListener);
+            
+            // Get starting rule from parser
+            Method startRule = CQPParser.class.getMethod("request");
+            tree = (ParserRuleContext) startRule.invoke(parser,
+                    (Object[]) null);
+        }
+        // Some things went wrong ...
+        catch (Exception e) {
+//            log.error("Could not parse query. "
+//                    + "Please make sure it is well-formed.");
+//            log.error(errorListener.generateFullErrorMsg().toString());
+            addError(errorListener.generateFullErrorMsg());
+        }
+        return tree;
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusQueryProcessor.java b/src/main/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusQueryProcessor.java
index 6829202..e071231 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusQueryProcessor.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/PoliqarpPlusQueryProcessor.java
@@ -945,15 +945,26 @@
             // establish boolean relation
             ParseTree boolOp = getFirstChildWithCat(node, "boolOp");
 
-			// Create group
+			// Create group // de Morgan's Laws
             if (boolOp.getText().equals("&")) {
-                termGroup = KoralObjectGenerator
+                if (negatedGlobal==false) {
+                    termGroup = KoralObjectGenerator
                         .makeTermGroup(KoralTermGroupRelation.AND);
-            }
-            else {
-                termGroup = KoralObjectGenerator
+                } else {
+                    termGroup = KoralObjectGenerator
                         .makeTermGroup(KoralTermGroupRelation.OR);
+                }
             }
+            else if (boolOp.getText().equals("|")) {
+                if (negatedGlobal==false) {
+                    termGroup = KoralObjectGenerator
+                        .makeTermGroup(KoralTermGroupRelation.OR);
+                } else {
+                    termGroup = KoralObjectGenerator
+                        .makeTermGroup(KoralTermGroupRelation.AND);
+                }
+            }
+
             ArrayList<Object> operands = (ArrayList<Object>) termGroup
                     .get("operands");
             // recursion with left/right operands
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java b/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java
index 5ee9c77..8294dca 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/QuerySerializer.java
@@ -55,6 +55,8 @@
                 new HashMap<String, Class<? extends AbstractQueryProcessor>>();
         qlProcessorAssignment.put("poliqarpplus",
                 PoliqarpPlusQueryProcessor.class);
+        qlProcessorAssignment.put("cqp",
+                CQPQueryProcessor.class);
         qlProcessorAssignment.put("cosmas2", Cosmas2QueryProcessor.class);
         qlProcessorAssignment.put("annis", AnnisQueryProcessor.class);
         qlProcessorAssignment.put("cql", CqlQueryProcessor.class);
@@ -134,7 +136,7 @@
      *            The query string
      * @param queryLanguage
      *            The query language. As of 17 Dec 2014, this must be one of
-     *            'poliqarpplus', 'cosmas2', 'annis' or 'cql'.
+     *            'poliqarpplus', 'cqp', 'cosmas2', 'annis' or 'cql'.
      * @throws IOException
      */
     public void run (String query, String queryLanguage) throws IOException {
@@ -150,6 +152,9 @@
         else if (queryLanguage.equalsIgnoreCase("cql")) {
             ast = new CqlQueryProcessor(query);
         }
+        else if (queryLanguage.equalsIgnoreCase("cqp")) {
+            ast = new CQPQueryProcessor(query);
+        }
         else if (queryLanguage.equalsIgnoreCase("fcsql")) {
             ast = new FCSQLQueryProcessor(query);
         }
@@ -181,6 +186,9 @@
         else if (ql.equalsIgnoreCase("poliqarpplus")) {
             ast = new PoliqarpPlusQueryProcessor(query);
         }
+        else if (ql.equalsIgnoreCase("cqp")) {
+            ast = new CQPQueryProcessor(query);
+        }
         else if (ql.equalsIgnoreCase("cql")) {
             if (version == null) {
                 ast = new CqlQueryProcessor(query);
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr4DescriptiveErrorListener.java b/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr4DescriptiveErrorListener.java
index 37cd731..c046e83 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr4DescriptiveErrorListener.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr4DescriptiveErrorListener.java
@@ -59,23 +59,32 @@
 
     private String getDetailedErrorMessage () {
         // default message, in case no detailed info is available;
-        String msg = "Malformed query. Could not parse.";
-        char offendingSymbol = query.charAt(0);
-        if (query.length() > charPosition)
+        String msg ="";
+        if (message != null && message.contains("text1"))
+        {
+            // qstruct and sstruct FailedPredicateException message
+            msg = "unmatched span tags!";
+        }
+        else
+        {
+            msg = "Malformed query. Could not parse.";
+            char offendingSymbol = query.charAt(0);
+            if (query.length() > charPosition)
             offendingSymbol = query.charAt(charPosition);
-        msg = "Failing to parse at symbol: '" + offendingSymbol + "'";
-        // check for unbalanced parantheses
-        SimpleEntry<String, Integer> unbalanced = QueryUtils
+            msg = "Failing to parse at symbol: '" + offendingSymbol + "'";
+            // check for unbalanced parantheses
+            SimpleEntry<String, Integer> unbalanced = QueryUtils
                 .checkUnbalancedPars(query);
-        if (unbalanced != null) {
+            if (unbalanced != null) {
             msg = unbalanced.getKey();
             charPosition = unbalanced.getValue();
-        }
-        // check if more more arguments expected before closing operator
-        if (String.valueOf(offendingSymbol).equals(")")) {
+            }
+            // check if more arguments expected before closing operator
+            if (String.valueOf(offendingSymbol).equals(")")) {
             msg = "Early closing parenthesis. Possibly lacking arguments for operator.";
         }
+    }
         return msg;
     }
 
-}
\ No newline at end of file
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/util/KoralObjectGenerator.java b/src/main/java/de/ids_mannheim/korap/query/serialize/util/KoralObjectGenerator.java
index 9f2a72a..91f1a27 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/util/KoralObjectGenerator.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/util/KoralObjectGenerator.java
@@ -37,9 +37,10 @@
         Map<String, Object> term = makeTerm();
         term.put("key", key);
         // EM: adding structure layer
-        if (key.equals("s")){
+        //EI: remove structure layer
+       /* if (key.equals("s")){
             term.put("layer", "s");
-        }
+        }*/
         span.put("wrap", term);
         return span;
     }
diff --git a/src/test/java/de/ids_mannheim/korap/query/test/BaseQueryTest.java b/src/test/java/de/ids_mannheim/korap/query/test/BaseQueryTest.java
new file mode 100644
index 0000000..1c44279
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/test/BaseQueryTest.java
@@ -0,0 +1,34 @@
+package de.ids_mannheim.korap.query.test;
+
+import java.util.ArrayList;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import de.ids_mannheim.korap.query.serialize.QuerySerializer;
+
+/**
+ * @author margaretha
+ *
+ */
+public abstract class BaseQueryTest {
+    
+    protected String query;
+    protected ArrayList<JsonNode> operands;
+    
+    private QuerySerializer qs = new QuerySerializer();
+    private ObjectMapper mapper = new ObjectMapper();
+    private String queryLanguage;
+
+    public BaseQueryTest (String queryLanguage) {
+        this.queryLanguage = queryLanguage;
+    }
+    
+    protected JsonNode runQuery (String query)
+            throws JsonMappingException, JsonProcessingException {
+        qs.setQuery(query, queryLanguage);
+        return mapper.readTree(qs.toJSON());
+    }
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPFlagTest.java b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPFlagTest.java
new file mode 100644
index 0000000..d4688a5
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPFlagTest.java
@@ -0,0 +1,271 @@
+package de.ids_mannheim.korap.query.test.cqp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.Lists;
+
+import de.ids_mannheim.korap.query.serialize.QuerySerializer;
+import de.ids_mannheim.korap.query.test.BaseQueryTest;
+
+public class CQPFlagTest extends BaseQueryTest {
+
+    private JsonNode result;
+
+    public CQPFlagTest () {
+        super("CQP");
+       
+    }
+
+
+    @Test
+    public void testLiteralx () throws JsonProcessingException {
+        query = "[mate/b=\"Der + Mann\"]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("Der + Mann", result.at("/query/wrap/key").asText());
+        assertEquals("b", result.at("/query/wrap/layer").asText());
+        assertEquals("mate", result.at("/query/wrap/foundry").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+    }
+    @Test
+    public void testLiteral () throws JsonProcessingException {
+        query = "[mate/b=\"Der + Mann\"%l]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("Der + Mann", result.at("/query/wrap/key").asText());
+        assertEquals("b", result.at("/query/wrap/layer").asText());
+        assertEquals("mate", result.at("/query/wrap/foundry").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("type:string", result.at("/query/wrap/type").asText());
+    }
+
+    @Test
+    public void testLiteralWithEscape () throws JsonProcessingException {
+        // why do i need an escape for ' ?
+        // EM: because you use ' for the key
+        query = "[mate/b='D\\'Ma \\\\nn'%l]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("type:string", result.at("/query/wrap/type").asText());
+        assertEquals("D'Ma \\nn", result.at("/query/wrap/key").asText());
+        assertEquals("b", result.at("/query/wrap/layer").asText());
+    }
+
+
+    @Test
+    public void testLiteralWithSlash () throws JsonProcessingException {
+        query = "[mate/b=\"D'Ma\nn\"%l]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("D'Ma\nn", result.at("/query/wrap/key").asText());
+        assertEquals("b", result.at("/query/wrap/layer").asText());
+        assertEquals("mate", result.at("/query/wrap/foundry").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("type:string", result.at("/query/wrap/type").asText());
+    }
+
+
+    @Test
+    public void testSingleQuoteWithinDoubleQuote ()
+            throws JsonProcessingException {
+        query = "[mate/b=\"D'Ma\\\\nn\"]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("D'Ma\\\\nn", result.at("/query/wrap/key").asText());
+        assertEquals("b", result.at("/query/wrap/layer").asText());
+        assertEquals("mate", result.at("/query/wrap/foundry").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+
+        // with literal
+        query = "[mate/b=\"D'Ma\\\\nn\"%l]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("D'Ma\\nn", result.at("/query/wrap/key").asText());
+        assertEquals("b", result.at("/query/wrap/layer").asText());
+        assertEquals("mate", result.at("/query/wrap/foundry").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("type:string", result.at("/query/wrap/type").asText());
+    }
+
+
+    @Test
+    public void testDoubleQuoteWithinSingleQuote ()
+            throws JsonProcessingException {
+        query = "[mate/b='D\"Ma\\\\nn']";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("D\"Ma\\\\nn", result.at("/query/wrap/key").asText());
+        assertEquals("b", result.at("/query/wrap/layer").asText());
+        assertEquals("mate", result.at("/query/wrap/foundry").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+
+        // with literal
+        query = "[mate/b='D\"Ma\\\\nn'%l]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("D\"Ma\\nn", result.at("/query/wrap/key").asText());
+        assertEquals("b", result.at("/query/wrap/layer").asText());
+        assertEquals("mate", result.at("/query/wrap/foundry").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("type:string", result.at("/query/wrap/type").asText());
+
+      
+
+    }
+
+
+    @Test
+    public void testURL () throws JsonProcessingException {
+        query = "'http://racai.ro'%l";
+        result = runQuery(query);
+        assertEquals("http://racai.ro", result.at("/query/wrap/key").asText());
+        assertEquals("type:string", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+
+        query = "\"http://racai.ro\"%l";
+        result = runQuery(query);
+        assertEquals("http://racai.ro", result.at("/query/wrap/key").asText());
+        assertEquals("type:string", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+
+        query = "[mate/b='http://racai.ro'%l]";
+        result = runQuery(query);
+        assertEquals("http://racai.ro", result.at("/query/wrap/key").asText());
+        assertEquals("type:string", result.at("/query/wrap/type").asText());
+        assertEquals("b", result.at("/query/wrap/layer").asText());
+        assertEquals("mate", result.at("/query/wrap/foundry").asText());
+    }
+
+
+    @Test
+    public void testDiacritics () throws JsonProcessingException {
+        query = "\"deutscher\"%d";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("deutscher", result.at("/query/wrap/key").asText());
+        assertEquals("flags:diacriticsInsensitive",
+                result.at("/query/wrap/flags/0").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+    }
+
+
+    @Test
+    public void testCaseSensitivityFlag () throws JsonProcessingException {
+        query = "[orth=\"deutscher\"%c]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("deutscher", result.at("/query/wrap/key").asText());
+        assertTrue(
+                result.at("/query/wrap/flags:caseInsensitive").isMissingNode());
+        assertEquals("flags:caseInsensitive",
+                result.at("/query/wrap/flags/0").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        
+        query = "\"deutscher\"%cd";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("deutscher", result.at("/query/wrap/key").asText());
+        assertFalse(result.at("/query/wrap/flags/0").isMissingNode());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("flags:caseInsensitive",
+                result.at("/query/wrap/flags/0").asText());
+        assertEquals("flags:diacriticsInsensitive",
+                result.at("/query/wrap/flags/1").asText());
+    }
+
+
+    @Test
+    public void testCaseSensitivitySequence ()
+            throws JsonProcessingException {
+        query = "[orth=\"deutscher\"%c][orth=\"Bundestag\"]";
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:sequence",
+                result.at("/query/operation").asText());
+        operands = Lists.newArrayList(result.at("/query/operands").elements());
+        assertEquals("koral:token", operands.get(0).at("/@type").asText());
+        assertEquals("deutscher", operands.get(0).at("/wrap/key").asText());
+        assertEquals("orth", operands.get(0).at("/wrap/layer").asText());
+        assertEquals("match:eq", operands.get(0).at("/wrap/match").asText());
+        assertEquals("flags:caseInsensitive",
+                operands.get(0).at("/wrap/flags/0").asText());
+        assertEquals("koral:token", operands.get(1).at("/@type").asText());
+        assertEquals("Bundestag", operands.get(1).at("/wrap/key").asText());
+        assertEquals("orth", operands.get(1).at("/wrap/layer").asText());
+        assertEquals("match:eq", operands.get(1).at("/wrap/match").asText());
+        assertTrue(operands.get(1).at("/wrap/flags/0").isMissingNode());
+
+        query = "\"deutscher\"%lc \"Bundestag\"";
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:sequence",
+                result.at("/query/operation").asText());
+        operands = Lists.newArrayList(result.at("/query/operands").elements());
+        assertEquals("koral:token", operands.get(0).at("/@type").asText());
+        assertEquals("deutscher", operands.get(0).at("/wrap/key").asText());
+        assertEquals("orth", operands.get(0).at("/wrap/layer").asText());
+        assertEquals("match:eq", operands.get(0).at("/wrap/match").asText());
+        assertEquals("flags:caseInsensitive",
+                operands.get(0).at("/wrap/flags/0").asText());
+        assertEquals("koral:token", operands.get(1).at("/@type").asText());
+        assertEquals("Bundestag", operands.get(1).at("/wrap/key").asText());
+        assertEquals("orth", operands.get(1).at("/wrap/layer").asText());
+        assertEquals("match:eq", operands.get(1).at("/wrap/match").asText());
+        assertTrue(operands.get(1).at("/wrap/flags:caseInsensitive")
+                .isMissingNode());
+    }
+
+
+    @Test
+    public void testMultipleFlags () throws JsonProcessingException {
+        query = "\"Der + Mann\"%lcd";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("Der + Mann", result.at("/query/wrap/key").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("type:string", result.at("/query/wrap/type").asText());
+        assertEquals("flags:caseInsensitive",
+                result.at("/query/wrap/flags/0").asText());
+        assertEquals("flags:diacriticsInsensitive",
+                result.at("/query/wrap/flags/1").asText());
+
+        query = "\"Der + Mann\"%cd";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("Der + Mann", result.at("/query/wrap/key").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("flags:caseInsensitive",
+                result.at("/query/wrap/flags/0").asText());
+        assertEquals("flags:diacriticsInsensitive",
+                result.at("/query/wrap/flags/1").asText());
+    }
+
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPMeetProcessorTest.java b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPMeetProcessorTest.java
new file mode 100644
index 0000000..5fe4391
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPMeetProcessorTest.java
@@ -0,0 +1,670 @@
+package de.ids_mannheim.korap.query.test.cqp;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.junit.Test;
+// import org.junit.Ignore;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+//import com.google.common.collect.Lists;
+
+//import de.ids_mannheim.korap.query.object.KoralFrame;
+import de.ids_mannheim.korap.query.serialize.QuerySerializer;
+
+
+public class CQPMeetProcessorTest {
+	String query;
+    ArrayList<JsonNode> operands;
+
+    QuerySerializer qs = new QuerySerializer();
+    ObjectMapper mapper = new ObjectMapper();
+    JsonNode res;
+    
+    @Test
+    public void testMeetVsFocusErrors () throws JsonProcessingException, IOException {
+       query = "MU(meet \"in\" \"due\" 0 -1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("The MeetUnion offsets cannot be 0!!", res.at("/errors/0/1").asText());
+  }
+    
+    
+    @Test
+    public void testFocusMeetDisjunction() throws JsonProcessingException, IOException {     
+    	
+        // ((color de | de color) pelo ) | (pelo (color de | de color))
+        query = "MU(meet(meet \"color\" \"de\" -1 1) \"pelo\"  -4 4);";
+    	   qs.setQuery(query, "CQP");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals(302, res.at("/errors/0/0").asInt());;
+        assertEquals("We did not implement recursive meet with different offsets!!", res.at("/errors/0/1").asText()); // any ideea for a better message here?
+        
+     
+        /*query = "focus('der' {'Baum'}) | focus ({'Baum'} 'der');";*/
+        query = "MU(meet \"Baum\" \"der\" -1 1);";
+    	   qs.setQuery(query, "CQP");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:reference", res.at("/query/@type").asText());
+        assertEquals("operation:focus", res.at("/query/operation").asText());
+        assertEquals(1, res.at("/query/classRef/0").asInt());
+        assertEquals("operation:disjunction", res.at("/query/operands/0/operation").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/0/operation").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+        assertEquals(1, res.at("/query/operands/0/operands/0/operands/0/classOut").asInt());
+        assertEquals("Baum", res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/type").asText());
+        assertEquals("der", res.at("/query/operands/0/operands/0/operands/1/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/0/operands/1/wrap/type").asText());
+        assertEquals("Baum", res.at("/query/operands/0/operands/1/operands/1/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/1/operands/0/wrap/type").asText());
+        assertEquals("der", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/wrap/type").asText());
+        assertEquals(1, res.at("/meta/highlight/0").asInt());
+        assertEquals(1, res.at("/meta/highlight/1").asInt());
+        
+        query = "MU(meet \"Baum\" \"der\" -2 1);";
+        qs.setQuery(query, "CQP");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:reference", res.at("/query/@type").asText());
+        assertEquals("operation:focus", res.at("/query/operation").asText());
+        assertEquals(1, res.at("/query/classRef/0").asInt());
+        assertEquals("operation:disjunction", res.at("/query/operands/0/operation").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/0/operation").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+        assertEquals(1, res.at("/query/operands/0/operands/0/operands/0/classOut").asInt());
+        assertEquals("Baum", res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/type").asText());
+        assertEquals("der", res.at("/query/operands/0/operands/0/operands/1/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/0/operands/1/wrap/type").asText());
+        assertEquals("Baum", res.at("/query/operands/0/operands/1/operands/1/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/1/operands/0/wrap/type").asText());
+        assertEquals("der", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/wrap/type").asText());
+        assertEquals(0, res.at("/query/operands/0/operands/1/distances/0/boundary/min").asInt());
+        assertEquals(1, res.at("/query/operands/0/operands/1/distances/0/boundary/max").asInt());
+        assertEquals(1, res.at("/meta/highlight/0").asInt());
+        assertEquals(1, res.at("/meta/highlight/1").asInt());
+        
+        
+        query = "MU(meet \"Baum\" \"der\" -2 3);";
+        qs.setQuery(query, "CQP");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:reference", res.at("/query/@type").asText());
+        assertEquals("operation:focus", res.at("/query/operation").asText());
+        assertEquals(1, res.at("/query/classRef/0").asInt());
+        assertEquals("operation:disjunction", res.at("/query/operands/0/operation").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/0/operation").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+        assertEquals(1, res.at("/query/operands/0/operands/0/operands/0/classOut").asInt());
+        assertEquals("Baum", res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/type").asText());
+        assertEquals("der", res.at("/query/operands/0/operands/0/operands/1/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/0/operands/1/wrap/type").asText());
+        assertEquals("Baum", res.at("/query/operands/0/operands/1/operands/1/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/1/operands/0/wrap/type").asText());
+        assertEquals("der", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/wrap/type").asText());
+        assertEquals(0, res.at("/query/operands/0/operands/0/distances/0/boundary/min").asInt());
+        assertEquals(2, res.at("/query/operands/0/operands/0/distances/0/boundary/max").asInt());
+        assertEquals(0, res.at("/query/operands/0/operands/1/distances/0/boundary/min").asInt());
+        assertEquals(1, res.at("/query/operands/0/operands/1/distances/0/boundary/max").asInt());
+        assertEquals(1, res.at("/meta/highlight/0").asInt());
+        assertEquals(1, res.at("/meta/highlight/1").asInt());
+      
+        
+        query = "MU(meet \"Baum\" \"der\" -1 2);";
+        qs.setQuery(query, "CQP");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:reference", res.at("/query/@type").asText());
+        assertEquals("operation:focus", res.at("/query/operation").asText());
+        assertEquals(1, res.at("/query/classRef/0").asInt());
+        assertEquals("operation:disjunction", res.at("/query/operands/0/operation").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/0/operation").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+        assertEquals(1, res.at("/query/operands/0/operands/0/operands/0/classOut").asInt());
+        assertEquals("Baum", res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/type").asText());
+        assertEquals("der", res.at("/query/operands/0/operands/0/operands/1/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/0/operands/1/wrap/type").asText());
+        assertEquals("Baum", res.at("/query/operands/0/operands/1/operands/1/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/1/operands/0/wrap/type").asText());
+        assertEquals("der", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/wrap/type").asText());
+        assertEquals(0, res.at("/query/operands/0/operands/0/distances/0/boundary/min").asInt());
+        assertEquals(1, res.at("/query/operands/0/operands/0/distances/0/boundary/max").asInt());  
+         
+   }
+  
+   @Test
+   public void testMeetSpan1 () throws JsonProcessingException, IOException { 
+	    query=  "MU(meet (meet \"cambios\" \"climáticos\" 2 2) (meet \"la\" \"piel\" 1 1) s)";
+        qs.setQuery(query, "CQP");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:reference", res.at("/query/@type").asText());
+        assertEquals("operation:focus", res.at("/query/operation").asText());
+        assertEquals("koral:group", res.at("/query/operands/0/@type").asText());
+        assertEquals("operation:position", res.at("/query/operands/0/operation").asText());
+        assertEquals("frames:isAround", res.at("/query/operands/0/frames/0").asText());
+        assertEquals("koral:span", res.at("/query/operands/0/operands/0/@type").asText());
+        assertEquals("s", res.at("/query/operands/0/operands/0/wrap/key").asText());
+        assertEquals("koral:term", res.at("/query/operands/0/operands/0/wrap/@type").asText());
+        assertEquals("koral:group", res.at("/query/operands/0/operands/1/@type").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+	    assertEquals("false", res.at("/query/operands/0/operands/1/inOrder").asText());
+	    assertEquals(0, res.at("/query/operands/0/operands/1/distances/0/boundary/min").asInt());
+	    assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operands/0/operation").asText());
+	    assertEquals(1, res.at("/query/operands/0/operands/1/operands/0/distances/0/boundary/min").asInt());
+	    assertEquals(1, res.at("/query/operands/0/operands/1/operands/0/distances/0/boundary/max").asInt());
+	    assertEquals("operation:class", res.at("/query/operands/0/operands/1/operands/0/operands/0/operation").asText());
+        assertEquals(1, res.at("/query/operands/0/operands/1/operands/0/operands/0/classOut").asInt());
+        assertEquals("cambios", res.at("/query/operands/0/operands/1/operands/0/operands/0/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/operands/0/operands/0/wrap/type").asText());
+        assertEquals("climáticos", res.at("/query/operands/0/operands/1/operands/0/operands/1/wrap/key").asText());
+	    assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/operands/1/wrap/type").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operands/1/operation").asText());
+	    assertEquals("la", res.at("/query/operands/0/operands/1/operands/1/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/1/operands/0/wrap/type").asText());
+	    assertEquals("piel", res.at("/query/operands/0/operands/1/operands/1/operands/1/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/1/operands/1/wrap/type").asText());
+   }
+   	
+   @Test
+   public void testMeetSpan2 () throws JsonProcessingException, IOException {
+   	    
+     //"in due" | "due in" []* time in a span s
+     
+     query = "MU(meet (meet \"in\" \"due\" -1 1) \"time\" s)";  
+     qs.setQuery(query, "CQP");
+     res = mapper.readTree(qs.toJSON());
+     assertEquals("koral:reference", res.at("/query/@type").asText());
+     assertEquals("operation:focus", res.at("/query/operation").asText());
+     assertEquals(1, res.at("/query/classRef/0").asInt());
+     assertEquals("operation:position", res.at("/query/operands/0/operation").asText());
+     assertEquals("frames:isAround", res.at("/query/operands/0/frames/0").asText());
+     assertEquals("koral:span", res.at("/query/operands/0/operands/0/@type").asText());
+     assertEquals("s", res.at("/query/operands/0/operands/0/wrap/key").asText());
+     assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+     assertEquals("operation:disjunction", res.at("/query/operands/0/operands/1/operands/0/operation").asText());
+     assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operands/0/operands/0/operation").asText());
+     assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operands/0/operands/1/operation").asText()); 
+     assertEquals(1, res.at("/query/operands/0/operands/1/operands/0/operands/0/operands/0/classOut").asInt());
+     assertEquals("in", res.at("/query/operands/0/operands/1/operands/0/operands/0/operands/0/operands/0/wrap/key").asText());
+     assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/operands/0/operands/0/operands/0/wrap/type").asText());
+     assertEquals("due", res.at("/query/operands/0/operands/1/operands/0/operands/0/operands/1/wrap/key").asText());
+     assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/operands/0/operands/1/wrap/type").asText());
+     assertEquals("due", res.at("/query/operands/0/operands/1/operands/0/operands/1/operands/0/wrap/key").asText());
+     assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/operands/1/operands/0/wrap/type").asText());
+     assertEquals(1, res.at("/query/operands/0/operands/1/operands/0/operands/1/operands/1/classOut").asInt());
+     assertEquals("in", res.at("/query/operands/0/operands/1/operands/0/operands/1/operands/1/operands/0/wrap/key").asText());
+     assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/operands/1/operands/1/operands/0/wrap/type").asText());
+     assertEquals(0, res.at("/query/operands/0/operands/1/distances/0/boundary/min").asInt());
+     assertEquals(0, res.at("/query/operands/0/operands/1/distances/0/boundary/max").asInt());
+     assertEquals("time", res.at("/query/operands/0/operands/1/operands/1/wrap/key").asText());
+     assertTrue(res.at("/query/operands/0/operands/1/distances/0/boundary/max").isMissingNode());
+     assertEquals(1, res.at("/meta/highlight/0").asInt());
+     assertEquals(1, res.at("/meta/highlight/1").asInt());
+     
+     
+     //"piel" []* "azul" in a span np,  folowed by "de" at any distance, in a span s
+         query = "MU(meet (meet \"piel\" \"azul\" np)  \"de\" s)";
+         qs.setQuery(query, "CQP");
+         res = mapper.readTree(qs.toJSON());
+         assertEquals("koral:reference", res.at("/query/@type").asText());
+	    assertEquals("operation:focus", res.at("/query/operation").asText());
+	    assertEquals("koral:group", res.at("/query/operands/0/@type").asText());
+	    assertEquals("operation:position", res.at("/query/operands/0/operation").asText());
+	    assertEquals("frames:isAround", res.at("/query/operands/0/frames/0").asText());
+	    assertEquals("koral:span", res.at("/query/operands/0/operands/0/@type").asText());
+	    assertEquals("s", res.at("/query/operands/0/operands/0/wrap/key").asText());
+         assertEquals("koral:group", res.at("/query/operands/0/operands/1/@type").asText());
+         assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+	    assertEquals("false", res.at("/query/operands/0/operands/1/inOrder").asText());
+	    assertEquals(0, res.at("/query/operands/0/operands/1/distances/0/boundary/min").asInt());
+         assertTrue(res.at("/query/operands/0/operands/1/distances/0/boundary/max").isMissingNode());
+         assertEquals("operation:position", res.at("/query/operands/0/operands/1/operands/0/operation").asText());
+         assertEquals("frames:isAround", res.at("/query/operands/0/operands/1/operands/0/frames/0").asText());
+         assertEquals("koral:span", res.at("/query/operands/0/operands/1/operands/0/operands/0/@type").asText());
+	    assertEquals("np", res.at("/query/operands/0/operands/1/operands/0/operands/0/wrap/key").asText());
+         assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operands/0/operands/1/operation").asText());
+         assertEquals(1, res.at("/query/operands/0/operands/1/operands/0/operands/1/operands/0/classOut").asInt());
+	    assertEquals("piel", res.at("/query/operands/0/operands/1/operands/0/operands/1/operands/0/operands/0/wrap/key").asText());
+         assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/operands/1/operands/0/operands/0/wrap/type").asText()); 
+         assertEquals("azul", res.at("/query/operands/0/operands/1/operands/0/operands/1/operands/1/wrap/key").asText());
+         assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/operands/1/operands/1/wrap/type").asText()); 
+	    assertEquals("de", res.at("/query/operands/0/operands/1/operands/1/wrap/key").asText());
+         assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/1/wrap/type").asText());
+
+         query=  "MU(meet \"de\" \"piel\" <base/s=s>)";
+	    qs.setQuery(query, "CQP");
+	    res = mapper.readTree(qs.toJSON());
+	    assertEquals("koral:reference", res.at("/query/@type").asText());
+	    assertEquals("operation:focus", res.at("/query/operation").asText());
+	    assertEquals("koral:group", res.at("/query/operands/0/@type").asText());
+	    assertEquals("operation:position", res.at("/query/operands/0/operation").asText());
+	    assertEquals("frames:isAround", res.at("/query/operands/0/frames/0").asText());
+	    assertEquals("koral:span", res.at("/query/operands/0/operands/0/@type").asText());
+	    assertEquals("s", res.at("/query/operands/0/operands/0/wrap/key").asText());
+	    assertEquals("s", res.at("/query/operands/0/operands/0/wrap/layer").asText());
+	    assertEquals("base", res.at("/query/operands/0/operands/0/wrap/foundry").asText());
+	    assertEquals("koral:group", res.at("/query/operands/0/operands/1/@type").asText());
+	    assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+	    assertEquals("false", res.at("/query/operands/0/operands/1/inOrder").asText());
+	    assertEquals(0, res.at("/query/operands/0/operands/1/distances/0/boundary/min").asInt());
+	    assertEquals("operation:class", res.at("/query/operands/0/operands/1/operands/0/operation").asText());
+	    assertEquals(1, res.at("/query/operands/0/operands/1/operands/0/classOut").asInt());
+	    assertEquals("de", res.at("/query/operands/0/operands/1/operands/0/operands/0/wrap/key").asText());
+         assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/operands/0/wrap/type").asText()); 
+	    assertEquals("piel", res.at("/query/operands/0/operands/1/operands/1/wrap/key").asText());
+         assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/1/wrap/type").asText());
+
+
+         query=  "MU(meet \"de\"%l \"piel\"%l <base/s=s>)";
+	    qs.setQuery(query, "CQP");
+	    res = mapper.readTree(qs.toJSON());
+	    assertEquals("koral:reference", res.at("/query/@type").asText());
+	    assertEquals("operation:focus", res.at("/query/operation").asText());
+	    assertEquals("koral:group", res.at("/query/operands/0/@type").asText());
+	    assertEquals("operation:position", res.at("/query/operands/0/operation").asText());
+	    assertEquals("frames:isAround", res.at("/query/operands/0/frames/0").asText());
+	    assertEquals("koral:span", res.at("/query/operands/0/operands/0/@type").asText());
+	    assertEquals("s", res.at("/query/operands/0/operands/0/wrap/key").asText());
+	    assertEquals("s", res.at("/query/operands/0/operands/0/wrap/layer").asText());
+	    assertEquals("base", res.at("/query/operands/0/operands/0/wrap/foundry").asText());
+	    assertEquals("koral:group", res.at("/query/operands/0/operands/1/@type").asText());
+	    assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+	    assertEquals("false", res.at("/query/operands/0/operands/1/inOrder").asText());
+	    assertEquals(0, res.at("/query/operands/0/operands/1/distances/0/boundary/min").asInt());
+	    assertEquals("operation:class", res.at("/query/operands/0/operands/1/operands/0/operation").asText());
+	    assertEquals(1, res.at("/query/operands/0/operands/1/operands/0/classOut").asInt());
+	    assertEquals("de", res.at("/query/operands/0/operands/1/operands/0/operands/0/wrap/key").asText()); 
+         assertEquals("type:string", res.at("/query/operands/0/operands/1/operands/0/operands/0/wrap/type").asText());
+	    assertEquals("piel", res.at("/query/operands/0/operands/1/operands/1/wrap/key").asText());
+         assertEquals("type:string", res.at("/query/operands/0/operands/1/operands/1/wrap/type").asText());
+	   
+   }
+
+   // looking for 2 words with different/variable distances in between
+   @Test
+   public void testMeetVsFocus10 () throws JsonProcessingException, IOException {
+ 
+       // 'due' []{0,2} 'in'; focus on "in";
+       query = "MU(meet \"in\" \"due\" -3 -1);";
+       qs.setQuery(query, "CQP");
+       res = mapper.readTree(qs.toJSON());
+       assertEquals("koral:reference", res.at("/query/@type").asText());
+       assertEquals("operation:focus", res.at("/query/operation").asText());
+       assertEquals(1, res.at("/query/classRef/0").asInt());
+       assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+       assertEquals("due", res.at("/query/operands/0/operands/0/wrap/key").asText());
+       assertEquals("operation:class", res.at("/query/operands/0/operands/1/operation").asText());
+       assertEquals(1, res.at("/query/operands/0/operands/1/classOut").asInt());
+       assertEquals("in", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+       assertEquals(0, res.at("/query/operands/0/distances/0/boundary/min").asInt());
+       assertEquals(2, res.at("/query/operands/0/distances/0/boundary/max").asInt());
+       assertEquals(1, res.at("/meta/highlight/0").asInt());
+     
+        // 'in' []{0,2} 'due'; focus on "in";
+         query = "MU(meet \"in\" \"due\" 1 3);";
+      qs.setQuery(query, "CQP");
+       res = mapper.readTree(qs.toJSON());
+       assertEquals("koral:reference", res.at("/query/@type").asText());
+       assertEquals("operation:focus", res.at("/query/operation").asText());
+       assertEquals(1, res.at("/query/classRef/0").asInt());
+       assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+       assertEquals("operation:class", res.at("/query/operands/0/operands/0/operation").asText());
+       assertEquals(1, res.at("/query/operands/0/operands/0/classOut").asInt());
+       assertEquals("in", res.at("/query/operands/0/operands/0/operands/0/wrap/key").asText());
+       assertEquals("due", res.at("/query/operands/0/operands/1/wrap/key").asText());
+       assertEquals(0, res.at("/query/operands/0/distances/0/boundary/min").asInt());
+       assertEquals(2, res.at("/query/operands/0/distances/0/boundary/max").asInt());
+       assertEquals(1, res.at("/meta/highlight/0").asInt());
+       
+       // 'due' []{1,4} 'in'; focus on "in";
+       query = "MU(meet \"in\" \"due\" -5 -2);";
+           qs.setQuery(query, "CQP");
+       res = mapper.readTree(qs.toJSON());
+       assertEquals("koral:reference", res.at("/query/@type").asText());
+       assertEquals("operation:focus", res.at("/query/operation").asText());
+       assertEquals(1, res.at("/query/classRef/0").asInt());
+       assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+       assertEquals(1, res.at("/query/operands/0/operands/1/classOut").asInt());
+       assertEquals("due", res.at("/query/operands/0/operands/0/wrap/key").asText());
+       assertEquals("operation:class", res.at("/query/operands/0/operands/1/operation").asText());
+       assertEquals("in", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+       assertEquals(1, res.at("/query/operands/0/distances/0/boundary/min").asInt());
+       assertEquals(4, res.at("/query/operands/0/distances/0/boundary/max").asInt());
+       assertEquals(1, res.at("/meta/highlight/0").asInt());
+       
+       // 'in' []{1,4} 'due'; focus on "in";
+       query = "MU(meet \"in\" \"due\" 2 5);";
+           qs.setQuery(query, "CQP");
+       res = mapper.readTree(qs.toJSON());
+       assertEquals("koral:reference", res.at("/query/@type").asText());
+       assertEquals("operation:focus", res.at("/query/operation").asText());
+       assertEquals(1, res.at("/query/classRef/0").asInt());
+       assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+       assertEquals("operation:class", res.at("/query/operands/0/operands/0/operation").asText());
+       assertEquals(1, res.at("/query/operands/0/operands/0/classOut").asInt());
+       assertEquals("in", res.at("/query/operands/0/operands/0/operands/0/wrap/key").asText());
+       assertEquals("due", res.at("/query/operands/0/operands/1/wrap/key").asText());
+       assertEquals(1, res.at("/query/operands/0/distances/0/boundary/min").asInt());
+       assertEquals(4, res.at("/query/operands/0/distances/0/boundary/max").asInt());
+       assertEquals(1, res.at("/meta/highlight/0").asInt());
+    }  
+// 3 word sequences
+  
+
+@Test
+public void testMeetVsFocus11 () throws JsonProcessingException, IOException {
+   // 'color' 'de' 'piel'; focus on 'color';
+    query =  "MU(meet (meet \"color\" \"de\" 1 1) \"piel\" 2 2);";
+    qs.setQuery(query, "CQP");
+    res = mapper.readTree(qs.toJSON());
+    assertEquals("koral:reference", res.at("/query/@type").asText());
+    assertEquals("operation:focus", res.at("/query/operation").asText());
+    assertEquals(1, res.at("/query/classRef/0").asInt());
+    assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+    assertEquals("operation:sequence", res.at("/query/operands/0/operands/0/operation").asText());
+    assertEquals("operation:class", res.at("/query/operands/0/operands/0/operands/0/operation").asText());
+    assertEquals(1, res.at("/query/operands/0/operands/0/operands/0/classOut").asInt());
+    assertEquals("color", res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/key").asText());
+    assertEquals("de",res.at("/query/operands/0/operands/0/operands/1/wrap/key").asText());
+    assertEquals("piel", res.at("/query/operands/0/operands/1/wrap/key").asText());
+    assertEquals(1, res.at("/meta/highlight/0").asInt());
+}
+
+@Test
+public void testMeetVsFocus12 () throws JsonProcessingException, IOException {
+    
+    // 'color' 'de' 'piel'; focus on 'color'
+     query =  "MU(meet \"color\" (meet \"de\" \"piel\" 1 1) 1 1);";
+     qs.setQuery(query, "CQP");
+     res = mapper.readTree(qs.toJSON());
+     assertEquals("koral:reference", res.at("/query/@type").asText());
+     assertEquals("operation:focus", res.at("/query/operation").asText());
+     assertEquals(1, res.at("/query/classRef/0").asInt());
+     assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+     assertEquals("operation:class",res.at("/query/operands/0/operands/0/operation").asText());
+     assertEquals(1, res.at("/query/operands/0/operands/0/classOut").asInt());
+     assertEquals("color", res.at("/query/operands/0/operands/0/operands/0/wrap/key").asText());
+     assertEquals("de", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+     assertEquals("piel", res.at("/query/operands/0/operands/1/operands/1/wrap/key").asText());
+     assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+     assertEquals(1, res.at("/meta/highlight/0").asInt());
+}
+      
+
+@Test        
+public void testMeetVsFocus14 () throws JsonProcessingException, IOException {
+ 
+    // color [] de piel; the focus is on "piel"
+    query =  "MU(meet \"piel\" (meet \"color\" \"de\" 2 2) -3 -3);";
+    qs.setQuery(query, "CQP");
+    res = mapper.readTree(qs.toJSON());
+    assertEquals("koral:reference", res.at("/query/@type").asText());
+    assertEquals("operation:focus", res.at("/query/operation").asText());
+    assertEquals(1, res.at("/query/classRef/0").asInt());
+    assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+    assertEquals("operation:sequence", res.at("/query/operands/0/operands/0/operation").asText());
+    assertEquals("color", res.at("/query/operands/0/operands/0/operands/0/wrap/key").asText());
+    assertEquals(1, res.at("/query/operands/0/operands/0/distances/0/boundary/min").asInt());
+    assertEquals(1, res.at("/query/operands/0/operands/0/distances/0/boundary/max").asInt());
+    assertEquals("de", res.at("/query/operands/0/operands/0/operands/1/wrap/key").asText());
+    assertEquals("piel", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+    assertEquals("operation:class", res.at("/query/operands/0/operands/1/operation").asText());
+    assertEquals(1, res.at("/query/operands/0/operands/1/classOut").asInt());
+    assertEquals(1, res.at("/meta/highlight/0").asInt());
+}
+  
+@Test
+public void testMeetVsFocus15 () throws JsonProcessingException, IOException {
+   // color de piel; the focus is on "piel";
+     query =  "MU(meet \"piel\" (meet \"color\" \"de\" 1 1) -2 -2);";
+     qs.setQuery(query, "CQP");
+     res = mapper.readTree(qs.toJSON());
+     assertEquals("koral:reference", res.at("/query/@type").asText());
+     assertEquals("operation:focus", res.at("/query/operation").asText());
+     assertEquals(1, res.at("/query/classRef/0").asInt());
+     assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+     assertEquals("operation:sequence", res.at("/query/operands/0/operands/0/operation").asText());
+     assertEquals("color", res.at("/query/operands/0/operands/0/operands/0/wrap/key").asText());
+     assertEquals("de",res.at("/query/operands/0/operands/0/operands/1/wrap/key").asText());
+     assertEquals("piel", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+     assertEquals("operation:class", res.at("/query/operands/0/operands/1/operation").asText());
+     assertEquals(1, res.at("/query/operands/0/operands/1/classOut").asInt());
+     assertEquals(1, res.at("/meta/highlight/0").asInt());
+}
+@Test
+public void testMeetVsFocus9 () throws JsonProcessingException, IOException {
+     // color de piel, focus on "piel";
+     query = "MU(meet (meet \"piel\" \"de\" -1 -1) \"color\" -2 -2);";
+     qs.setQuery(query, "CQP");
+     res = mapper.readTree(qs.toJSON());
+     assertEquals("koral:reference", res.at("/query/@type").asText());
+     assertEquals("operation:focus", res.at("/query/operation").asText());
+     assertEquals(1, res.at("/query/classRef/0").asInt());
+     assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+     assertEquals("color", res.at("/query/operands/0/operands/0/wrap/key").asText());
+     assertEquals("de", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+     assertEquals("piel", res.at("/query/operands/0/operands/1/operands/1/operands/0/wrap/key").asText());
+     assertEquals("operation:class",res.at("/query/operands/0/operands/1/operands/1/operation").asText());
+     assertEquals(1, res.at("/query/operands/0/operands/1/operands/1/classOut").asInt());
+     assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+     assertEquals(1, res.at("/meta/highlight/0").asInt());
+}
+ 
+
+   // looking for long sequences of 4 words with focus on different positions
+
+     @Test
+     public void TestMeetFocusPos4 () throws JsonProcessingException, IOException {
+       // color de piel oscuro; focus on "oscuro";
+	    
+	   query = "MU(meet (meet 'oscuro' 'piel' -1 -1) (meet \"color\" \"de\" 1 1) -3 -3)";
+	   qs.setQuery(query, "CQP");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:reference", res.at("/query/@type").asText());
+        assertEquals("operation:focus", res.at("/query/operation").asText());
+        assertEquals("koral:group", res.at("/query/operands/0/@type").asText());
+        assertEquals("koral:group", res.at("/query/operands/0/operands/1/@type").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/0/operation").asText());  
+        assertEquals("color", res.at("/query/operands/0/operands/0/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/0/operands/0/wrap/type").asText());
+        assertEquals("de", res.at("/query/operands/0/operands/0/operands/1/wrap/key").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+        assertEquals("piel", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/operands/1/operands/0/wrap/type").asText());
+        assertEquals("operation:class", res.at("/query/operands/0/operands/1/operands/1/operation").asText());
+        assertEquals(1, res.at("/query/operands/0/operands/1/operands/1/classOut").asInt());
+        assertEquals("oscuro", res.at("/query/operands/0/operands/1/operands/1/operands/0/wrap/key").asText()); 
+	   
+   }
+   
+   @Test
+   public void testMeetFocusPos3 () throws JsonProcessingException, IOException {
+	     // color de piel oscuro; focus on "piel";
+	   query = "MU(meet (meet \"piel\" \"oscuro\" 1 1) (meet \"color\" \"de\" 1 1) -2 -2)";
+	   qs.setQuery(query, "CQP");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:reference", res.at("/query/@type").asText());
+        assertEquals("operation:focus", res.at("/query/operation").asText());
+        assertEquals("koral:group", res.at("/query/operands/0/@type").asText());
+        assertEquals("koral:group", res.at("/query/operands/0/operands/1/@type").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/0/operation").asText());  
+        assertEquals("color", res.at("/query/operands/0/operands/0/operands/0/wrap/key").asText());
+        assertEquals("de", res.at("/query/operands/0/operands/0/operands/1/wrap/key").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+        assertEquals("piel", res.at("/query/operands/0/operands/1/operands/0/operands/0/wrap/key").asText());
+        assertEquals("operation:class", res.at("/query/operands/0/operands/1/operands/0/operation").asText());
+        assertEquals(1, res.at("/query/operands/0/operands/1/operands/0/classOut").asInt());
+        assertEquals("oscuro", res.at("/query/operands/0/operands/1/operands/1/wrap/key").asText()); 
+   }
+   
+   @Test
+   public void testMeetFocusPos2 () throws JsonProcessingException, IOException {
+	   
+	   // color de piel oscuro; focus on "de";
+	   query = "MU(meet (meet \"de\" \"color\" -1 -1) (meet \"piel\" \"oscuro\" 1 1) 1 1)";
+        qs.setQuery(query, "CQP");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:reference", res.at("/query/@type").asText());
+        assertEquals("operation:focus", res.at("/query/operation").asText());
+        assertEquals("koral:group", res.at("/query/operands/0/@type").asText());
+        assertEquals("koral:group", res.at("/query/operands/0/operands/1/@type").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/0/operation").asText());
+        assertEquals("operation:class", res.at("/query/operands/0/operands/0/operands/1/operation").asText());
+        assertEquals(1, res.at("/query/operands/0/operands/0/operands/1/classOut").asInt());
+        assertEquals("color", res.at("/query/operands/0/operands/0/operands/0/wrap/key").asText());
+        assertEquals("de", res.at("/query/operands/0/operands/0/operands/1/operands/0/wrap/key").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+        assertEquals("piel", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+        assertEquals("oscuro", res.at("/query/operands/0/operands/1/operands/1/wrap/key").asText()); 
+   }
+
+   @Test
+   public void testMeetFocusPos1 () throws JsonProcessingException, IOException {
+     // color de piel oscuro; focus on "color"
+          query = "MU(meet (meet \"color\" \"de\" 1 1) (meet \"piel\" \"oscuro\" 1 1) 2 2)";
+          qs.setQuery(query, "CQP");
+          res = mapper.readTree(qs.toJSON());
+          assertEquals("koral:reference", res.at("/query/@type").asText());
+          assertEquals("operation:focus", res.at("/query/operation").asText());
+          assertEquals("koral:group", res.at("/query/operands/0/@type").asText());
+          assertEquals("koral:group", res.at("/query/operands/0/operands/1/@type").asText());
+          assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+          assertEquals("operation:sequence", res.at("/query/operands/0/operands/0/operation").asText());
+          assertEquals("operation:class", res.at("/query/operands/0/operands/0/operands/0/operation").asText());
+          assertEquals(1, res.at("/query/operands/0/operands/0/operands/0/classOut").asInt());
+          assertEquals("color", res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/key").asText());
+          assertEquals("de", res.at("/query/operands/0/operands/0/operands/1/wrap/key").asText());
+          assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+          assertEquals("piel", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+          assertEquals("oscuro", res.at("/query/operands/0/operands/1/operands/1/wrap/key").asText());     
+}   
+   
+   @Test
+   public void testMeetFocusDistPos1 () throws JsonProcessingException, IOException {
+	   	
+       	 // color de []{2} piel oscuro; focus on "color";
+        query = "MU(meet (meet \"color\" \"de\" 1 1) (meet \"piel\" \"oscuro\" 1 1) 4 4)";
+	   qs.setQuery(query, "CQP");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:reference", res.at("/query/@type").asText());
+        assertEquals("operation:focus", res.at("/query/operation").asText());
+        assertEquals("koral:group", res.at("/query/operands/0/@type").asText());
+        assertEquals("koral:group", res.at("/query/operands/0/operands/1/@type").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/0/operation").asText());
+        assertEquals("operation:class", res.at("/query/operands/0/operands/0/operands/0/operation").asText());
+        assertEquals(1, res.at("/query/operands/0/operands/0/operands/0/classOut").asInt());
+        assertEquals("color", res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/key").asText());
+        assertEquals("de", res.at("/query/operands/0/operands/0/operands/1/wrap/key").asText());
+        assertEquals("2", res.at("/query/operands/0/distances/0/boundary/min").asText());
+        assertEquals("2", res.at("/query/operands/0/distances/0/boundary/max").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operands/1/operation").asText());
+        assertEquals("piel", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+        assertEquals("oscuro", res.at("/query/operands/0/operands/1/operands/1/wrap/key").asText()); 
+   }   
+	    
+
+        // // equal offsets mean exact distance
+   @Test
+   public void testMeetEqOffs () throws JsonProcessingException, IOException {
+ 	  	
+   	// 'in' 'due'; focus on 'in'
+        query = "MU(meet \"in\" \"due\" 1 1);";
+        qs.setQuery(query, "CQP");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:reference", res.at("/query/@type").asText());
+        assertEquals("operation:focus", res.at("/query/operation").asText());
+        assertEquals(1, res.at("/query/classRef/0").asInt());
+        assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+        assertEquals("operation:class", res.at("/query/operands/0/operands/0/operation").asText());
+        assertEquals(1, res.at("/query/operands/0/operands/0/classOut").asInt());
+        assertEquals("in", res.at("/query/operands/0/operands/0/operands/0/wrap/key").asText());
+        assertEquals("due", res.at("/query/operands/0/operands/1/wrap/key").asText());
+        assertEquals(1, res.at("/meta/highlight/0").asInt());
+   	
+        // 'due' 'in'; focus on 'in';
+        query = "MU(meet \"in\" \"due\" -1 -1);";
+      	qs.setQuery(query, "CQP");
+      	res = mapper.readTree(qs.toJSON());
+      	assertEquals("koral:reference", res.at("/query/@type").asText());
+      	assertEquals("operation:focus", res.at("/query/operation").asText());
+      	assertEquals(1, res.at("/query/classRef/0").asInt());
+      	assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+      	assertEquals("due", res.at("/query/operands/0/operands/0/wrap/key").asText());
+      	assertEquals("operation:class", res.at("/query/operands/0/operands/1/operation").asText());
+      	assertEquals(1, res.at("/query/operands/0/operands/1/classOut").asInt());
+      	assertEquals("in", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+      	assertEquals(1, res.at("/meta/highlight/0").asInt());
+          
+      	// 'in' [] 'due'; focus on 'in';
+      	query = "MU(meet \"in\" \"due\" 2 2);";
+      	qs.setQuery(query, "CQP");
+      	res = mapper.readTree(qs.toJSON());
+      	assertEquals("koral:reference", res.at("/query/@type").asText());
+      	assertEquals("operation:focus", res.at("/query/operation").asText());
+      	assertEquals(1, res.at("/query/classRef/0").asInt());
+      	assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+      	assertEquals("operation:class", res.at("/query/operands/0/operands/0/operation").asText());
+      	assertEquals(1, res.at("/query/operands/0/operands/0/classOut").asInt());
+      	assertEquals("in", res.at("/query/operands/0/operands/0/operands/0/wrap/key").asText());
+      	assertEquals("due", res.at("/query/operands/0/operands/1/wrap/key").asText());
+      	assertEquals(1, res.at("/query/operands/0/distances/0/boundary/min").asInt());
+      	assertEquals(1, res.at("/query/operands/0/distances/0/boundary/max").asInt());
+      	assertEquals(1, res.at("/meta/highlight/0").asInt());
+       
+      	// 'due' []{2} 'in'; focus on in;
+      	query = "MU(meet \"in\" \"due\" -3 -3);";
+      	qs.setQuery(query, "CQP");
+      	res = mapper.readTree(qs.toJSON());
+      	assertEquals("koral:reference", res.at("/query/@type").asText());
+      	assertEquals("operation:focus", res.at("/query/operation").asText());
+      	assertEquals(1, res.at("/query/classRef/0").asInt());
+      	assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+      	assertEquals("due", res.at("/query/operands/0/operands/0/wrap/key").asText());
+    	     assertEquals("operation:class", res.at("/query/operands/0/operands/1/operation").asText());
+      	assertEquals(1, res.at("/query/operands/0/operands/1/classOut").asInt());
+      	assertEquals("in", res.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+      	assertEquals(2, res.at("/query/operands/0/distances/0/boundary/min").asInt());
+      	assertEquals(2, res.at("/query/operands/0/distances/0/boundary/max").asInt());
+      	assertEquals(1, res.at("/meta/highlight/0").asInt());
+	
+}
+   
+
+        @Test
+     public void testMeetPos () throws JsonProcessingException, IOException {
+          // [pos="NN.*"] of virtue; the focus is on [pos="NN.*"]
+          query=  "MU(meet (meet [pos=\"NN.*\"] \"of\" 1 1) \"virtue\" 2 2);";
+          qs.setQuery(query, "CQP");
+          res = mapper.readTree(qs.toJSON());
+          assertEquals("koral:reference", res.at("/query/@type").asText());
+      	assertEquals("operation:focus", res.at("/query/operation").asText());
+      	assertEquals(1, res.at("/query/classRef/0").asInt());
+      	assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());
+      	assertEquals("operation:sequence", res.at("/query/operands/0/operands/0/operation").asText());
+      	assertEquals("operation:class", res.at("/query/operands/0/operands/0/operands/0/operation").asText());
+      	assertEquals(1, res.at("/query/operands/0/operands/0/operands/0/classOut").asInt());
+      	assertEquals("NN.*", res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/key").asText());
+      	assertEquals("pos", res.at("/query/operands/0/operands/0/operands/0/operands/0/wrap/layer").asText());
+      	assertEquals("of",res.at("/query/operands/0/operands/0/operands/1/wrap/key").asText());
+      	assertEquals("virtue", res.at("/query/operands/0/operands/1/wrap/key").asText());
+      	assertEquals(1, res.at("/meta/highlight/0").asInt());
+        }   	    	
+        
+       
+  
+
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPPositionTest.java b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPPositionTest.java
new file mode 100644
index 0000000..f20ba7a
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPPositionTest.java
@@ -0,0 +1,506 @@
+package de.ids_mannheim.korap.query.test.cqp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.query.test.BaseQueryTest;
+
+public class CQPPositionTest extends BaseQueryTest{
+
+    private JsonNode result;
+
+    public CQPPositionTest () {
+        super("CQP");
+    }
+    
+    @Test
+    public void testSequenceStartsWithStartSentence () throws JsonProcessingException {
+       
+         // throws error because of the FailedPredicateException; 
+        query =" <s> <np> \"copil\" </np> ;"; 
+        result = runQuery(query);
+        assertEquals("unmatched span tags!", result.at("/errors/0/1").asText());
+        assertEquals(302, result.at("/errors/1/0").asInt());
+
+        query = "<s> []+ (\"der\")* []+ </z>";
+        result = runQuery(query);
+        assertEquals("unmatched span tags!", result.at("/errors/0/1").asText());
+
+        query =" <base/s=s> \"copil\" ;"; //KoralFrame.STARTS_WITH
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:startsWith", result.at("/query/frames/0").asText());
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("base", result.at("/query/operands/0/wrap/foundry").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/layer").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/@type").asText());
+        assertEquals("copil", result.at("/query/operands/1/wrap/key").asText());
+
+        // it is interpreted as a sentence starting/ended with a np, followed/preceded by "copil"; 
+        // np is a segment in the sequence, not a position operator, like s
+        query =" <s> <np> \"copil\" ;"; //KoralFrame.STARTS_WITH
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        query ="\"copil\" </np> </s>;"; //KoralFrame.ENDS_WITH
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        
+       
+    //    assertEquals("koral:group", result.at("/query/@type").asText());
+       // matches np imbricated in sentence; imbrication works for correct qstructures
+        query =" <s> <np> \"copil\" </np> \"mic\"</s>;"; 
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+
+       
+    }
+    
+  
+    @Test
+    public void testSequenceStartsWithEndSentence () throws JsonProcessingException {
+        
+        // parsed as a sequence of span s and token copil; focus on the token;
+        query =" </base/s=s> \"copil\" ;"; 
+        result = runQuery(query);
+        assertEquals("koral:reference", result.at("/query/@type").asText());
+        assertEquals("operation:focus", result.at("/query/operation").asText());
+        assertEquals("koral:group", result.at("/query/operands/0/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operands/0/operation").asText());
+        assertEquals("koral:span", result.at("/query/operands/0/operands/0/@type").asText());
+        assertEquals("koral:group", result.at("/query/operands/0/operands/1/@type").asText());
+        assertEquals("operation:class", result.at("/query/operands/0/operands/1/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/0/operands/1/operands/0/@type").asText());
+        assertEquals("type:regex", result.at("/query/operands/0/operands/1/operands/0/wrap/type").asText());
+        assertEquals("copil", result.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+        
+        
+        // (span token token) sequence; focus on both tokens
+        query =" </base/s=s> \"copil\" \"cuminte\";";
+        result = runQuery(query);
+        assertEquals("koral:reference", result.at("/query/@type").asText());
+        assertEquals("operation:focus", result.at("/query/operation").asText());
+        assertEquals("koral:group", result.at("/query/operands/0/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operands/0/operation").asText());
+        assertEquals("koral:span", result.at("/query/operands/0/operands/0/@type").asText());
+        assertEquals("koral:group", result.at("/query/operands/0/operands/1/@type").asText());
+        assertEquals("operation:class", result.at("/query/operands/0/operands/1/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/0/operands/1/operands/0/@type").asText());
+        assertEquals("type:regex", result.at("/query/operands/0/operands/1/operands/0/wrap/type").asText());
+        assertEquals("copil", result.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+        assertEquals("koral:group", result.at("/query/operands/0/operands/2/@type").asText());
+        assertEquals("operation:class", result.at("/query/operands/0/operands/2/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/0/operands/2/operands/0/@type").asText());
+        assertEquals("type:regex", result.at("/query/operands/0/operands/2/operands/0/wrap/type").asText());
+        assertEquals("cuminte", result.at("/query/operands/0/operands/2/operands/0/wrap/key").asText());
+    }
+
+
+    @Test
+    public void testSequenceEndsWithEndSentence () throws JsonProcessingException{
+        query ="\"copil\" </base/s=s>;"; //KoralFrame.ENDS_WITH
+        result = runQuery(query);
+        
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:endsWith", result.at("/query/frames/0").asText());
+        assertEquals("frames:matches", result.at("/query/frames/1").asText());
+        assertEquals(2, result.at("/query/frames").size());
+        assertEquals(2, result.at("/query/operands").size());
+        
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("base", result.at("/query/operands/0/wrap/foundry").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/layer").asText());
+        
+        assertEquals("koral:token", result.at("/query/operands/1/@type").asText());
+        assertEquals("type:regex", result.at("/query/operands/1/wrap/type").asText());
+        assertEquals("copil", result.at("/query/operands/1/wrap/key").asText());
+
+    }
+    
+    @Test
+    public void testLongerSequenceEndsWithEndSentence () throws JsonProcessingException{
+        query ="\"copil\" \"cuminte\" </base/s=s>;"; //KoralFrame.ENDS_WITH
+        result = runQuery(query);
+        
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals(2, result.at("/query/operands").size());
+        assertEquals(2, result.at("/query/frames").size());
+        assertEquals("frames:endsWith", result.at("/query/frames/0").asText());
+        assertEquals("frames:matches", result.at("/query/frames/1").asText());
+ 
+        
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("base", result.at("/query/operands/0/wrap/foundry").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/layer").asText());
+        
+        assertEquals("koral:reference", result.at("/query/operands/1/@type").asText());
+        assertEquals("operation:focus", result.at("/query/operands/1/operation").asText());
+  
+        assertEquals("koral:group", result.at("/query/operands/1/operands/0/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operands/1/operands/0/operation").asText());
+  
+        
+        assertEquals("koral:group", result.at("/query/operands/1/operands/0/operands/0/@type").asText());
+        assertEquals("operation:class", result.at("/query/operands/1/operands/0/operands/0/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/operands/0/operands/0/operands/0/@type").asText());
+        assertEquals("copil", result.at("/query/operands/1/operands/0/operands/0/operands/0/wrap/key").asText());
+   
+        
+        assertEquals("koral:group", result.at("/query/operands/1/operands/0/operands/1/@type").asText());
+        assertEquals("operation:class", result.at("/query/operands/1/operands/0/operands/1/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/operands/0/operands/1/operands/0/@type").asText());
+        assertEquals("cuminte", result.at("/query/operands/1/operands/0/operands/1/operands/0/wrap/key").asText());
+    }
+    
+ 
+    @Test
+    public void testSequenceEndsWithStartSentence () throws JsonProcessingException{
+        query ="\"copil\" <base/s=s>;"; 
+        result = runQuery(query);     
+        assertEquals("koral:reference", result.at("/query/@type").asText());
+        assertEquals("operation:focus", result.at("/query/operation").asText());
+        assertEquals("koral:group", result.at("/query/operands/0/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operands/0/operation").asText());
+        assertEquals("koral:span", result.at("/query/operands/0/operands/1/@type").asText());
+        assertEquals("koral:group", result.at("/query/operands/0/operands/0/@type").asText());
+        assertEquals("operation:class", result.at("/query/operands/0/operands/0/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/0/operands/0/operands/0/@type").asText());
+        assertEquals("type:regex", result.at("/query/operands/0/operands/0/operands/0/wrap/type").asText());
+        assertEquals("copil", result.at("/query/operands/0/operands/0/operands/0/wrap/key").asText());
+    }
+    
+   
+    @Test
+    public void testLongerSequenceEndsWithStartSentence () throws JsonProcessingException{
+        query ="\"copil\" \"cuminte\" <base/s=s>;"; 
+        result = runQuery(query);
+        assertEquals("koral:reference", result.at("/query/@type").asText());
+        assertEquals("operation:focus", result.at("/query/operation").asText());
+        assertEquals("koral:group", result.at("/query/operands/0/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operands/0/operation").asText());  
+        assertEquals("koral:group", result.at("/query/operands/0/operands/0/@type").asText());
+        assertEquals("operation:class", result.at("/query/operands/0/operands/0/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/0/operands/0/operands/0/@type").asText());
+        assertEquals("type:regex", result.at("/query/operands/0/operands/0/operands/0/wrap/type").asText());
+        assertEquals("copil", result.at("/query/operands/0/operands/0/operands/0/wrap/key").asText());
+        assertEquals("koral:group", result.at("/query/operands/0/operands/1/@type").asText());
+        assertEquals("operation:class", result.at("/query/operands/0/operands/1/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/0/operands/1/operands/0/@type").asText());
+        assertEquals("type:regex", result.at("/query/operands/0/operands/1/operands/0/wrap/type").asText());
+        assertEquals("cuminte", result.at("/query/operands/0/operands/1/operands/0/wrap/key").asText());
+        
+        assertEquals("koral:span", result.at("/query/operands/0/operands/2/@type").asText());
+   }
+
+    @Test
+    public void testSingleTokenInSentence () throws JsonProcessingException {
+        query =" <base/s=s> \"copil\" </base/s=s>;";  // KoralFrame.MATCHES
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:matches", result.at("/query/frames/0").asText());
+        assertTrue(result.at("/query/frames/1").isMissingNode());
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("base", result.at("/query/operands/0/wrap/foundry").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/layer").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/@type").asText());
+        assertEquals("copil", result.at("/query/operands/1/wrap/key").asText());
+    }
+    
+    
+    @Test
+    public void testSequenceInSentence () throws JsonProcessingException {
+        query = "<base/s=s> []* \"copil\" []* </base/s=s>;";
+        result = runQuery(query);
+        
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:matches", result.at("/query/frames/0").asText());
+        assertEquals(1, result.at("/query/frames").size());
+        assertEquals(2, result.at("/query/operands").size());
+        
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        
+        assertEquals("koral:group", result.at("/query/operands/1/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operands/1/operation").asText());
+        assertEquals(3, result.at("/query/operands/1/operands").size());
+        
+        assertEquals("koral:group", result.at("/query/operands/1/operands/0/@type").asText());
+        assertEquals("operation:repetition", result.at("/query/operands/1/operands/0/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/operands/0/operands/0/@type").asText());
+        
+        assertEquals("copil", result.at("/query/operands/1/operands/1/wrap/key").asText());
+      
+        assertEquals("koral:group", result.at("/query/operands/1/operands/2/@type").asText());
+        assertEquals("operation:repetition", result.at("/query/operands/1/operands/2/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/operands/2/operands/0/@type").asText());
+        
+    }
+    
+    @Test
+    public void testSequenceInSentence2 () throws JsonProcessingException{
+        query = "<base/s=s> []* \"copil\" [] \"cuminte\" []* </base/s=s>";
+        result = runQuery(query);
+        
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+      //  assertEquals("frames:isAround", res.at("/query/frames/0").asText());
+      //  assertEquals("frames:startsWith", res.at("/query/frames/1").asText());
+     //   assertEquals("frames:endsWith", res.at("/query/frames/2").asText());
+        assertEquals(1, result.at("/query/frames").size());
+        assertEquals("frames:matches", result.at("/query/frames/0").asText());
+        assertEquals(2, result.at("/query/operands").size());
+        
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        
+        assertEquals("koral:group", result.at("/query/operands/1/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operands/1/operation").asText());
+        
+        result = result.at("/query/operands/1");
+        assertEquals("koral:group", result.at("/@type").asText());
+        assertEquals("operation:sequence", result.at("/operation").asText());
+        assertEquals(5, result.at("/operands").size());
+
+        // []*
+        assertEquals("koral:group", result.at("/operands/0/@type").asText());
+        assertEquals("operation:repetition", result.at("/operands/0/operation").asText());
+        assertEquals("koral:token", result.at("/operands/0/operands/0/@type").asText());
+        
+        assertEquals(0, result.at("/operands/0/boundary/min").asInt());
+        assertTrue(result.at("/operands/0/boundary/max").isMissingNode());
+        
+        // copil
+        assertEquals("koral:token", result.at("/operands/1/@type").asText());
+        assertEquals("koral:term", result.at("/operands/1/wrap/@type").asText());
+        assertEquals("match:eq", result.at("/operands/1/wrap/match").asText());
+        assertEquals("type:regex", result.at("/operands/1/wrap/type").asText());
+        assertEquals("orth", result.at("/operands/1/wrap/layer").asText());
+        assertEquals("copil", result.at("/operands/1/wrap/key").asText());
+        
+        // []
+        assertEquals("koral:token", result.at("/operands/2/@type").asText());
+        assertTrue(result.at("/operands/2/wrap/key").isMissingNode());
+        
+        // cuminte
+        assertEquals("koral:token", result.at("/operands/3/@type").asText());
+        assertEquals("koral:term", result.at("/operands/3/wrap/@type").asText());
+        assertEquals("match:eq", result.at("/operands/3/wrap/match").asText());
+        assertEquals("type:regex", result.at("/operands/3/wrap/type").asText());
+        assertEquals("orth", result.at("/operands/3/wrap/layer").asText());
+        assertEquals("cuminte", result.at("/operands/3/wrap/key").asText());
+        
+        //[]*
+        assertEquals("koral:group", result.at("/operands/4/@type").asText());
+        assertEquals("operation:repetition", result.at("/operands/4/operation").asText());
+        assertEquals("koral:token", result.at("/operands/4/operands/0/@type").asText());
+        assertEquals(0, result.at("/operands/4/boundary/min").asInt());
+        assertTrue(result.at("/operands/4/boundary/max").isMissingNode());
+    }
+    
+    @Test
+    public void testSequenceWithinSentence () throws JsonProcessingException {
+        // same as  "contains(<base/s=s>, \"copil\" \"cuminte\")"; in PQ+, checked!
+        // only if []+ is paired and emmbeding the searched string/sequence: []+ ... []+
+        
+        query = "<base/s=s> []+ \"copil\" \"cuminte\" []+ </base/s=s>;";
+        result = runQuery(query);
+        
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:isAround", result.at("/query/frames/0").asText());
+        assertEquals(1, result.at("/query/frames").size());
+        
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        
+        assertEquals("koral:group", result.at("/query/operands/1/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operands/1/operation").asText());
+    }
+    
+    /**
+     * @throws JsonProcessingException
+     */
+    @Test
+    public void testSequenceAtConstituentEnd () throws JsonProcessingException {
+        //if []+ is not embedding the rest of the expression, it is treated as token repetition;
+        query = "<np> []+ ([pos=\"JJ.*\"] []+){3,} </np>;"; //KoralFrame.MATCHES
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:matches", result.at("/query/frames/0").asText());
+        assertTrue(result.at("/query/frames/1").isMissingNode());
+        assertEquals(2, result.at("/query/operands").size());
+        
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("np", result.at("/query/operands/0/wrap/key").asText());
+        
+        assertEquals("koral:group", result.at("/query/operands/1/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operands/1/operation").asText());
+        assertEquals(2, result.at("/query/operands/1/operands").size());
+        
+        JsonNode node = result.at("/query/operands/1");
+        // []+
+        assertEquals("koral:group", node.at("/operands/1/@type").asText());
+        assertEquals("operation:repetition", node.at("/operands/0/operation").asText());
+        assertEquals("koral:token", node.at("/operands/0/operands/0/@type").asText());
+        assertEquals(1, node.at("/operands/0/boundary/min").asInt());
+        
+        // ([pos=\"JJ.*\"] []+){3,}
+        assertEquals("operation:repetition", node.at("/operands/1/operation").asText());
+        assertEquals(3, node.at("/operands/1/boundary/min").asInt());
+        assertEquals(1, node.at("/operands/1/operands").size());        
+        
+        // ([pos=\"JJ.*\"] []+)
+        node = node.at("/operands/1/operands/0");
+        assertEquals("operation:sequence", node.at("/operation").asText());
+        assertEquals(2, node.at("/operands").size());     
+        
+        // [pos=\"JJ.*\"]
+        assertEquals("koral:token", node.at("/operands/0/@type").asText());
+        assertEquals("pos", node.at("/operands/0/wrap/layer").asText());
+        assertEquals("JJ.*", node.at("/operands/0/wrap/key").asText());
+        
+        // []+ 
+        assertEquals("operation:repetition", node.at("/operands/1/operation").asText());
+        assertEquals("1", node.at("/operands/1/boundary/min").asText());
+        assertEquals("koral:token", node.at("/operands/1/operands/0/@type").asText());
+
+    }
+    @Test
+    public void testEmbeddedStruct () throws JsonProcessingException {
+        query = "<s><np>[]*</np> []* <np1>[]*</np1></s>"; 
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:matches", result.at("/query/frames/0").asText());
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("koral:group", result.at("/query/operands/1/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operands/1/operation").asText());
+        
+        assertEquals("koral:group", result.at("/query/operands/1/operands/0/@type").asText());
+        assertEquals("operation:position", result.at("/query/operands/1/operands/0/operation").asText());
+        assertEquals("frames:matches", result.at("/query/operands/1/operands/0/frames/0").asText());
+        assertEquals("koral:span", result.at("/query/operands/1/operands/0/operands/0/@type").asText());
+        assertEquals("np", result.at("/query/operands/1/operands/0/operands/0/wrap/key").asText());
+        assertEquals("koral:group", result.at("/query/operands/1/operands/0/operands/1/@type").asText());
+        assertEquals("operation:repetition", result.at("/query/operands/1/operands/0/operands/1/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/operands/0/operands/1/operands/0/@type").asText());
+        assertEquals("0", result.at("/query/operands/1/operands/0/operands/1/boundary/min").asText());
+
+        assertEquals("koral:group", result.at("/query/operands/1/operands/2/@type").asText());
+        assertEquals("operation:position", result.at("/query/operands/1/operands/2/operation").asText());
+        assertEquals("frames:matches", result.at("/query/operands/1/operands/2/frames/0").asText());
+        assertEquals("np1", result.at("/query/operands/1/operands/2/operands/0/wrap/key").asText());
+        assertEquals("koral:group", result.at("/query/operands/1/operands/2/operands/1/@type").asText());
+        assertEquals("operation:repetition", result.at("/query/operands/1/operands/2/operands/1/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/operands/2/operands/1/operands/0/@type").asText());
+        assertEquals("0", result.at("/query/operands/1/operands/2/operands/1/boundary/min").asText());
+        
+  
+        
+    }  
+
+    @Test
+    public void testSequenceWithinConstituent () throws JsonProcessingException {
+        // EM: comparable to PQ+ query: contains (NP, sequence)
+        query =" <np> []+ ([pos=\"JJ.*\"]){3,} []+ </np>;"; //KoralFrame.IS_AROUND
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:isAround", result.at("/query/frames/0").asText());
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("np", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("koral:group", result.at("/query/operands/1/@type").asText());
+        assertEquals("operation:repetition", result.at("/query/operands/1/operation").asText());
+        assertEquals("JJ.*", result.at("/query/operands/1/operands/0/wrap/key").asText());
+        assertEquals(3, result.at("/query/operands/1/boundary/min").asInt());
+        // the []+ segments are not serialesd;
+        assertTrue(result.at("/query/operands/2").isMissingNode());
+        
+    }
+    
+    // EM: rbound: last token in the region
+    // match token at the end of a sentence
+    @Test
+    public void testRbound ()  throws JsonProcessingException {
+        query ="[\"copil\"  & rbound(<s>)];"; 
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        
+        query ="[\"copil\"  & rbound(s)];"; 
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+
+        query ="[\"copil\"  & rbound(<base/s=s>)];"; 
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:endsWith", result.at("/query/frames/0").asText());
+        assertEquals("frames:matches", result.at("/query/frames/1").asText());
+        //assertTrue(res.at("/query/frames/1").isMissingNode());
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("base", result.at("/query/operands/0/wrap/foundry").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/layer").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/@type").asText());
+        assertEquals("copil", result.at("/query/operands/1/wrap/key").asText());
+        
+    }
+    
+    // EM: lbound: first token in the region
+    // match token at the start of a sentence
+    @Test
+    public void testLBound () throws JsonProcessingException {
+        query ="[(base=\"copil\") & lbound(<base/s=s>)];"; 
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:startsWith", result.at("/query/frames/0").asText());
+        assertEquals("frames:matches", result.at("/query/frames/1").asText());
+        //assertTrue(res.at("/query/frames/1").isMissingNode());
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("base", result.at("/query/operands/0/wrap/foundry").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/layer").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/@type").asText());
+        assertEquals("copil", result.at("/query/operands/1/wrap/key").asText());
+        assertEquals("lemma", result.at("/query/operands/1/wrap/layer").asText());
+    }
+    
+    @Test
+    public void testRBoundSequence () throws JsonProcessingException {
+        
+        query ="[word = \"acest\"][\"copil\" & rbound(<base/s=s>)];"; 
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/0/@type").asText());
+        assertEquals("acest", result.at("/query/operands/0/wrap/key").asText());    
+        assertEquals("koral:group", result.at("/query/operands/1/@type").asText());
+        assertEquals("operation:position", result.at("/query/operands/1/operation").asText());
+        assertEquals("frames:endsWith", result.at("/query/operands/1/frames/0").asText());
+        assertEquals("frames:matches", result.at("/query/operands/1/frames/1").asText());
+        //assertTrue(res.at("/query/frames/1").isMissingNode());
+        assertEquals("koral:span", result.at("/query/operands/1/operands/0/@type").asText());
+        assertEquals("base", result.at("/query/operands/1/operands/0/wrap/foundry").asText());
+        assertEquals("s", result.at("/query/operands/1/operands/0/wrap/layer").asText());
+        assertEquals("s", result.at("/query/operands/1/operands/0/wrap/key").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/operands/1/@type").asText());
+        assertEquals("copil", result.at("/query/operands/1/operands/1/wrap/key").asText()); 
+    }
+
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPQueryProcessorTest.java b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPQueryProcessorTest.java
new file mode 100644
index 0000000..e11233b
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPQueryProcessorTest.java
@@ -0,0 +1,1122 @@
+package de.ids_mannheim.korap.query.test.cqp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.Lists;
+
+//import de.ids_mannheim.korap.query.object.KoralFrame;
+import de.ids_mannheim.korap.query.serialize.QuerySerializer;
+import de.ids_mannheim.korap.query.test.BaseQueryTest;
+
+/**
+ * Tests for JSON-LD serialization of CQP queries.
+ * 
+ * @author Joachim Bingel (bingel@ids-mannheim.de)
+ * @author Elena Irimia (elena@racai.ro) based on PoliqarpPlusQueryProcessorTest
+ * @author Eliza Margaretha 
+ */
+public class CQPQueryProcessorTest extends BaseQueryTest {
+
+    private String query;
+    private ArrayList<JsonNode> operands;
+
+    private QuerySerializer qs = new QuerySerializer();
+    private ObjectMapper mapper = new ObjectMapper();
+    private JsonNode result;
+
+    public CQPQueryProcessorTest () {
+        super("CQP");
+    }
+
+    @Test
+    public void testUnknownLayer () throws IOException {
+        result = runQuery("[mate/b=\"Baum\"]");
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("Baum", result.at("/query/wrap/key").asText());
+        assertEquals("b", result.at("/query/wrap/layer").asText());
+        
+        result = runQuery("[mate/bi='Baum']");
+        assertEquals("bi", result.at("/query/wrap/layer").asText());
+    }
+
+    @Test
+    public void testLayerWithFlag () throws IOException {
+        result = runQuery("[base='Baum'%c]");
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("flags:caseInsensitive", result.at("/query/wrap/flags/0").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("Baum", result.at("/query/wrap/key").asText());
+        assertEquals("lemma", result.at("/query/wrap/layer").asText());
+
+        result = runQuery("[mate/x=\"Baum\"%c]");   query = "[mate/x=Baum/i]";
+        assertEquals("mate", result.at("/query/wrap/foundry").asText());
+        assertEquals("x", result.at("/query/wrap/layer").asText());
+        assertEquals("flags:caseInsensitive", result.at("/query/wrap/flags/0").asText());
+
+        result = runQuery("[mate/x=\"\"%c]");
+        assertEquals("mate", result.at("/query/wrap/foundry").asText());
+        assertEquals("x", result.at("/query/wrap/layer").asText());
+        assertEquals("flags:caseInsensitive", result.at("/query/wrap/flags/0").asText());
+    }
+
+    
+    @Test
+    public void testContext () throws JsonProcessingException {
+        String contextString = "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld";
+        result = runQuery("\"foo\"");
+        assertEquals(contextString, result.get("@context").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("foo", result.at("/query/wrap/key").asText());
+    }
+
+    @Test
+    public void testUnbalancedBrackets () throws JsonProcessingException {
+        query = "[base=Mann";
+        qs.setQuery(query, "CQP");
+        assertTrue(qs.hasErrors());
+        result = mapper.readTree(qs.toJSON());
+        assertEquals(302, result.at("/errors/0/0").asInt());
+        assertEquals(302, result.at("/errors/1/0").asInt());
+	}
+
+
+    @Test
+    public void testSingleTokens () throws JsonProcessingException {
+        query = "[base=\"Mann\"]";
+        qs.setQuery(query, "CQP");
+        assertFalse(qs.hasErrors());
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("Mann", result.at("/query/wrap/key").asText());
+        assertEquals("lemma", result.at("/query/wrap/layer").asText());
+    }
+    
+
+    @Test
+    public void testSingleTokenNegation () throws JsonProcessingException {
+
+        query = "[!base=\"Mann\"]";
+        qs.setQuery(query, "CQP");
+		assertFalse(qs.hasErrors());
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("Mann", result.at("/query/wrap/key").asText());
+        assertEquals("lemma", result.at("/query/wrap/layer").asText());
+        assertEquals("match:ne", result.at("/query/wrap/match").asText());
+        
+        query = "![base=\"Mann\"]";
+        qs.setQuery(query, "CQP");
+		assertFalse(qs.hasErrors());
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("Mann", result.at("/query/wrap/key").asText());
+        assertEquals("lemma", result.at("/query/wrap/layer").asText());
+        assertEquals("match:ne", result.at("/query/wrap/match").asText());
+
+        query = "[orth!=\"Frau\"]";
+        qs.setQuery(query, "CQP");
+        assertFalse(qs.hasErrors());
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("Frau", result.at("/query/wrap/key").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:ne", result.at("/query/wrap/match").asText());
+
+        query = "[p!=\"NN\"]";
+        qs.setQuery(query, "CQP");
+        assertFalse(qs.hasErrors());
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("NN", result.at("/query/wrap/key").asText());
+        assertEquals("p", result.at("/query/wrap/layer").asText());
+        assertEquals("match:ne", result.at("/query/wrap/match").asText());
+    }
+    
+    @Test
+    public void testSingleTokenDoubleNegation () throws JsonProcessingException {
+        query = "[!p!=\"NN\"]";
+        qs.setQuery(query, "CQP");
+        assertFalse(qs.hasErrors());
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("NN", result.at("/query/wrap/key").asText());
+        assertEquals("p", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+
+        query = "![p!=\"NN\"]";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertTrue(result.at("/errors").isMissingNode());
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("NN", result.at("/query/wrap/key").asText());
+        assertEquals("p", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+    }
+
+    @Test
+    public void testNegatedTerms () throws JsonProcessingException{
+    	query = "[!(word=\"ido\"%c)]";
+        result = runQuery(query);
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("match:ne", result.at("/query/wrap/match").asText());
+        assertEquals("flags:caseInsensitive", result.at("/query/wrap/flags/0").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("ido", result.at("/query/wrap/key").asText());
+        assertEquals("word", result.at("/query/wrap/layer").asText());
+    }
+    
+
+    @Test
+    public void testNegatedConjunctionAndDisjunction ()
+            throws JsonProcessingException {
+
+        // negating conjunction and disjunction testing
+        // relation or shoud become and because of negation! 
+        // see De Morgan's laws;  implemented correctly in PQ+ too...
+        query = "[(lemma=\"ir\") & !(word=\"ido\"%c | word=\"voy\"%c)];";
+        result = runQuery(query);
+        assertEquals("koral:termGroup",
+                result.at("/query/wrap/@type").asText());
+        assertEquals("relation:and",
+                result.at("/query/wrap/relation").asText());
+        operands = Lists
+                .newArrayList(result.at("/query/wrap/operands").elements());
+        assertEquals("koral:term", operands.get(0).at("/@type").asText());
+        assertEquals("ir", operands.get(0).at("/key").asText());
+        assertEquals("lemma", operands.get(0).at("/layer").asText());
+        assertEquals("match:eq", operands.get(0).at("/match").asText());
+        
+        assertEquals("koral:termGroup", operands.get(1).at("/@type").asText());
+        assertEquals("relation:and", operands.get(1).at("/relation").asText());
+        
+        assertEquals("koral:term",
+                operands.get(1).at("/operands/0/@type").asText());
+        assertEquals("koral:term",
+                operands.get(1).at("/operands/1/@type").asText());
+        assertEquals("ido", operands.get(1).at("/operands/0/key").asText());
+        assertEquals("word", operands.get(1).at("/operands/0/layer").asText());
+        assertEquals("match:ne",
+                operands.get(1).at("/operands/0/match").asText());
+        
+        assertEquals("voy", operands.get(1).at("/operands/1/key").asText());
+        assertEquals("word", operands.get(1).at("/operands/1/layer").asText());
+        assertEquals("match:ne",
+                operands.get(1).at("/operands/1/match").asText());
+    }
+    
+    @Test
+    public void testNegatedConjunctionAndDisjunction2 ()
+            throws JsonProcessingException {
+        // !(a&!b) = !a | !!b = !a | b
+        query = "![(lemma=\"ir\") & !(word=\"ido\"%c | word=\"voy\"%c)];";
+        result = runQuery(query);
+
+        assertEquals("koral:termGroup",
+                result.at("/query/wrap/@type").asText());
+        assertEquals("relation:or", result.at("/query/wrap/relation").asText());
+        operands = Lists
+                .newArrayList(result.at("/query/wrap/operands").elements());
+        assertEquals("koral:term", operands.get(0).at("/@type").asText());
+        assertEquals("ir", operands.get(0).at("/key").asText());
+        assertEquals("lemma", operands.get(0).at("/layer").asText());
+        assertEquals("match:ne", operands.get(0).at("/match").asText());
+        
+        assertEquals("koral:termGroup", operands.get(1).at("/@type").asText());
+        assertEquals("relation:or", operands.get(1).at("/relation").asText());
+        
+        assertEquals("koral:term",
+                operands.get(1).at("/operands/0/@type").asText());
+        assertEquals("koral:term",
+                operands.get(1).at("/operands/1/@type").asText());
+        assertEquals("ido", operands.get(1).at("/operands/0/key").asText());
+        assertEquals("word", operands.get(1).at("/operands/0/layer").asText());
+        assertEquals("match:eq",
+                operands.get(1).at("/operands/0/match").asText());
+        assertEquals("voy", operands.get(1).at("/operands/1/key").asText());
+        assertEquals("word", operands.get(1).at("/operands/1/layer").asText());
+        assertEquals("match:eq",
+                operands.get(1).at("/operands/1/match").asText());
+
+    }
+
+
+    @Test
+    public void testValue () throws JsonProcessingException, IOException {
+        query = "[mate/m='temp':'pres']";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("temp", result.at("/query/wrap/key").asText());
+        assertEquals("pres", result.at("/query/wrap/value").asText());
+        assertEquals("m", result.at("/query/wrap/layer").asText());
+        assertEquals("mate", result.at("/query/wrap/foundry").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+
+        query = "[mate/m=\"number\":\"pl\"]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("number", result.at("/query/wrap/key").asText());
+        assertEquals("pl", result.at("/query/wrap/value").asText());
+        assertEquals("m", result.at("/query/wrap/layer").asText());
+        assertEquals("mate", result.at("/query/wrap/foundry").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+    }
+
+    @Test
+    public void testPunct () throws JsonProcessingException, IOException {
+        query = "[punct=\".\"]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals(".", result.at("/query/wrap/key").asText());
+        assertEquals("type:punct", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+
+        query = "[punct=\"\\.\"]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("\\.", result.at("/query/wrap/key").asText());
+        assertEquals("type:punct", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+    }
+
+   
+    @Test
+    public void testCoordinatedFields () throws JsonProcessingException,
+            IOException {
+        query = "[base='Mann'&(cas='N'|cas='A')]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("relation:and", result.at("/query/wrap/relation").asText());
+        assertEquals("Mann", result.at("/query/wrap/operands/0/key").asText());
+        assertEquals("lemma", result.at("/query/wrap/operands/0/layer").asText());
+        assertEquals("koral:termGroup", result.at("/query/wrap/operands/1/@type")
+                .asText());
+        assertEquals("relation:or", result.at("/query/wrap/operands/1/relation")
+                .asText());
+        assertEquals("N", result.at("/query/wrap/operands/1/operands/0/key")
+                .asText());
+        assertEquals("cas", result.at("/query/wrap/operands/1/operands/0/layer")
+                .asText());
+        assertEquals("A", result.at("/query/wrap/operands/1/operands/1/key")
+                .asText());
+        assertEquals("cas", result.at("/query/wrap/operands/1/operands/1/layer")
+                .asText());
+
+        query = "[base=\"Mann\"&cas=\"N\"&gen=\"m\"]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("relation:and", result.at("/query/wrap/relation").asText());
+        assertEquals("Mann", result.at("/query/wrap/operands/0/key").asText());
+        assertEquals("lemma", result.at("/query/wrap/operands/0/layer").asText());
+        assertEquals("koral:termGroup", result.at("/query/wrap/operands/1/@type")
+                .asText());
+        assertEquals("relation:and", result.at("/query/wrap/operands/1/relation")
+                .asText());
+        assertEquals("N", result.at("/query/wrap/operands/1/operands/0/key")
+                .asText());
+        assertEquals("cas", result.at("/query/wrap/operands/1/operands/0/layer")
+                .asText());
+        assertEquals("m", result.at("/query/wrap/operands/1/operands/1/key")
+                .asText());
+        assertEquals("gen", result.at("/query/wrap/operands/1/operands/1/layer")
+                .asText());
+
+		query = "[(cas=\"N\"|cas=\"A\")&base='Mann']";
+		result = runQuery(query);	
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("relation:and", result.at("/query/wrap/relation").asText());
+        assertEquals("koral:termGroup", result.at("/query/wrap/operands/0/@type")
+                .asText());
+        assertEquals("relation:or", result.at("/query/wrap/operands/0/relation")
+                .asText());
+        assertEquals("N", result.at("/query/wrap/operands/0/operands/0/key")
+                .asText());
+        assertEquals("cas", result.at("/query/wrap/operands/0/operands/0/layer")
+                .asText());
+        assertEquals("A", result.at("/query/wrap/operands/0/operands/1/key")
+                .asText());
+        assertEquals("cas", result.at("/query/wrap/operands/0/operands/1/layer")
+                .asText());
+		assertEquals("Mann", result.at("/query/wrap/operands/1/key").asText());
+        assertEquals("lemma", result.at("/query/wrap/operands/1/layer").asText());
+	}
+
+	
+    @Test
+    public void testUnnecessaryParentheses () throws JsonProcessingException,
+            IOException {
+        query = "[(base='Mann')]";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("Mann", result.at("/query/wrap/key").asText());
+        assertEquals("lemma", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+
+        query = "[(((base='Mann')))]";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("Mann", result.at("/query/wrap/key").asText());
+        assertEquals("lemma", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+
+        query = "[(base=\"Mann\"&cas=\"N\")];";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:termGroup", result.at("/query/wrap/@type")
+                .asText());
+        assertEquals("relation:and", result.at("/query/wrap/relation")
+                     .asText());
+        assertEquals("Mann", result.at("/query/wrap/operands/0/key")
+                     .asText());
+        assertEquals("lemma", result.at("/query/wrap/operands/0/layer")
+                     .asText());
+        assertEquals("N", result.at("/query/wrap/operands/1/key")
+                     .asText());
+        assertEquals("cas", result.at("/query/wrap/operands/1/layer")
+                     .asText());
+
+
+        query = "[(((base='Mann'&cas='N')))]";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:termGroup", result.at("/query/wrap/@type")
+                .asText());
+        assertEquals("relation:and", result.at("/query/wrap/relation")
+                     .asText());
+        assertEquals("Mann", result.at("/query/wrap/operands/0/key")
+                     .asText());
+        assertEquals("lemma", result.at("/query/wrap/operands/0/layer")
+                     .asText());
+        assertEquals("N", result.at("/query/wrap/operands/1/key")
+                     .asText());
+        assertEquals("cas", result.at("/query/wrap/operands/1/layer")
+                     .asText());
+
+
+        query = "[(((base='Mann'&((cas='N')))))]";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:termGroup", result.at("/query/wrap/@type")
+                .asText());
+        assertEquals("relation:and", result.at("/query/wrap/relation")
+                     .asText());
+        assertEquals("Mann", result.at("/query/wrap/operands/0/key")
+                     .asText());
+        assertEquals("lemma", result.at("/query/wrap/operands/0/layer")
+                     .asText());
+        assertEquals("N", result.at("/query/wrap/operands/1/key")
+                     .asText());
+        assertEquals("cas", result.at("/query/wrap/operands/1/layer")
+                     .asText());
+
+		query = "[((cas='N'|cas='A'))&base='Mann']";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());		
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("relation:and", result.at("/query/wrap/relation").asText());
+        assertEquals("koral:termGroup", result.at("/query/wrap/operands/0/@type")
+                .asText());
+        assertEquals("relation:or", result.at("/query/wrap/operands/0/relation")
+                .asText());
+        assertEquals("N", result.at("/query/wrap/operands/0/operands/0/key")
+                .asText());
+        assertEquals("cas", result.at("/query/wrap/operands/0/operands/0/layer")
+                .asText());
+        assertEquals("A", result.at("/query/wrap/operands/0/operands/1/key")
+                .asText());
+        assertEquals("cas", result.at("/query/wrap/operands/0/operands/1/layer")
+                .asText());
+		assertEquals("Mann", result.at("/query/wrap/operands/1/key").asText());
+        assertEquals("lemma", result.at("/query/wrap/operands/1/layer").asText());
+    };
+
+    @Test
+    public void testTokenSequence () throws JsonProcessingException,
+            IOException {
+        query = "[base=\"Mann\"][orth=\"Frau\"];";
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operation").asText());
+        assertEquals("Mann", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("lemma", result.at("/query/operands/0/wrap/layer").asText());
+        assertEquals("Frau", result.at("/query/operands/1/wrap/key").asText());
+        assertEquals("orth", result.at("/query/operands/1/wrap/layer").asText());
+
+        query = "[base=\"Mann\"][orth=\"Frau\"][p=\"NN\"];";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("NN", result.at("/query/operands/2/wrap/key").asText());
+        assertEquals("p", result.at("/query/operands/2/wrap/layer").asText());
+
+        query = "[base=\"Mann\"][orth=\"Frau\"][p=\"NN\"][foo=\"bar\"];";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("bar", result.at("/query/operands/3/wrap/key").asText());
+        assertEquals("foo", result.at("/query/operands/3/wrap/layer").asText());
+    }
+
+
+    @Test
+    public void testDisjSegments () throws JsonProcessingException, IOException {
+        query = "[base=\"der\"]|[base=\"das\"]";
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:disjunction", result.at("/query/operation")
+                .asText());
+        assertEquals("koral:token", result.at("/query/operands/0/@type").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/@type").asText());
+        assertEquals("der", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("lemma", result.at("/query/operands/0/wrap/layer").asText());
+        assertEquals("das", result.at("/query/operands/1/wrap/key").asText());
+        assertEquals("lemma", result.at("/query/operands/1/wrap/layer").asText());
+
+        query = "([base='der']|[base='das'])[base='Schild']";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operation").asText());
+        assertEquals("Schild", result.at("/query/operands/1/wrap/key").asText());
+        assertEquals("koral:group", result.at("/query/operands/0/@type").asText());
+        assertEquals("operation:disjunction",
+                result.at("/query/operands/0/operation").asText());
+
+        query = "[base='Schild']([base='der']|[base='das'])";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("Schild", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("koral:group", result.at("/query/operands/1/@type").asText());
+        assertEquals("operation:disjunction",
+                result.at("/query/operands/1/operation").asText());
+
+        query = "([orth=\"der\"][base=\"katze\"])|([orth=\"eine\"][base=\"baum\"])";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:disjunction", result.at("/query/operation")
+                .asText());
+        assertEquals("koral:group", result.at("/query/operands/0/@type").asText());
+        assertEquals("operation:sequence", result
+                .at("/query/operands/0/operation").asText());
+        assertEquals("koral:token", result
+                .at("/query/operands/0/operands/0/@type").asText());
+        assertEquals("der", result.at("/query/operands/0/operands/0/wrap/key")
+                .asText());
+        assertEquals("katze", result.at("/query/operands/0/operands/1/wrap/key")
+                .asText());
+        assertEquals("eine", result.at("/query/operands/1/operands/0/wrap/key")
+                .asText());
+        assertEquals("baum", result.at("/query/operands/1/operands/1/wrap/key")
+                .asText());
+
+        query = "[orth='der'][base=\"katze\"]|[orth='eine'][base=\"baum\"]";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:disjunction", result.at("/query/operation")
+                .asText());
+        assertEquals("koral:group", result.at("/query/operands/0/@type").asText());
+        assertEquals("operation:sequence", result
+                .at("/query/operands/0/operation").asText());
+        assertEquals("koral:token", result
+                .at("/query/operands/0/operands/0/@type").asText());
+        assertEquals("der", result.at("/query/operands/0/operands/0/wrap/key")
+                .asText());
+        assertEquals("katze", result.at("/query/operands/0/operands/1/wrap/key")
+                .asText());
+        assertEquals("eine", result.at("/query/operands/1/operands/0/wrap/key")
+                .asText());
+        assertEquals("baum", result.at("/query/operands/1/operands/1/wrap/key")
+                .asText());
+
+        query = "[orth='der']([base='katze']|[orth='eine'])[base='baum']";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operation").asText());
+        assertEquals("koral:group", result.at("/query/operands/1/@type").asText());
+        assertEquals("operation:disjunction",
+                result.at("/query/operands/1/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/0/@type").asText());
+        assertEquals("koral:token", result.at("/query/operands/2/@type").asText());
+        assertEquals("der", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("katze", result.at("/query/operands/1/operands/0/wrap/key")
+                .asText());
+        assertEquals("eine", result.at("/query/operands/1/operands/1/wrap/key")
+                .asText());
+        assertEquals("baum", result.at("/query/operands/2/wrap/key").asText());
+
+        query = "[orth='der'][base='katze']|[orth='der'][base='hund']|[orth='der'][base='baum']";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("der", result.at("/query/operands/2/operands/0/wrap/key")
+                .asText());
+        assertEquals("baum", result.at("/query/operands/2/operands/1/wrap/key")
+                .asText());
+
+        query = "[orth='der']([base='katze']|[base='hund']|[base='baum'])";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operation").asText());
+        assertEquals("koral:group", result.at("/query/operands/1/@type").asText());
+        assertEquals("operation:disjunction",
+                result.at("/query/operands/1/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/0/@type").asText());
+        assertEquals("koral:token", result
+                .at("/query/operands/1/operands/0/@type").asText());
+        assertEquals("koral:token", result
+                .at("/query/operands/1/operands/1/@type").asText());
+        assertEquals("koral:token", result
+                .at("/query/operands/1/operands/2/@type").asText());
+        assertEquals("katze", result.at("/query/operands/1/operands/0/wrap/key")
+                .asText());
+        assertEquals("hund", result.at("/query/operands/1/operands/1/wrap/key")
+                .asText());
+        assertEquals("baum", result.at("/query/operands/1/operands/2/wrap/key")
+                .asText());
+    }
+
+
+    @Test
+    public void testClasses () throws JsonProcessingException, IOException {
+        // equivalent with  query = "{[base=Mann]}" in PQ+
+    	query = "@[base='Mann']";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:class", result.at("/query/operation").asText());
+        assertEquals(1, result.at("/query/classOut").asInt());
+        assertTrue(result.at("/query/classIn").isMissingNode());
+        assertEquals("Mann", result.at("/query/operands/0/wrap/key").asText());
+    	
+        query = "@([base='Mann'][orth='Frau'])";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:class", result.at("/query/operation").asText());
+        assertEquals(1, result.at("/query/classOut").asInt());
+        assertTrue(result.at("/query/classIn").isMissingNode());
+        assertEquals("Mann", result.at("/query/operands/0/operands/0/wrap/key")
+                .asText());
+        assertEquals("Frau", result.at("/query/operands/0/operands/1/wrap/key")
+                .asText());
+
+        
+        query = "@1(@[tt/p='ADJA'][mate/p='NN'])";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:class", result.at("/query/operation").asText());
+        assertEquals("operation:sequence", result
+                .at("/query/operands/0/operation").asText());
+        assertEquals(2, result.at("/query/classOut").asInt());
+        assertEquals("operation:class", result.at("/query/operands/0/operands/0/operation").asText());
+        assertEquals(1, result.at("/query/operands/0/operands/0/classOut").asInt());
+        assertEquals(2, result.at("/meta/highlight/0").asInt());
+        assertEquals(1, result.at("/meta/highlight/1").asInt());
+    	
+
+        query = "@[base='Mann']@1[orth='Frau']";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operation").asText());
+        assertEquals("operation:class", result.at("/query/operands/0/operation")
+                .asText());
+        assertEquals(1, result.at("/query/operands/0/classOut").asInt());
+        assertEquals("operation:class", result.at("/query/operands/1/operation")
+                .asText());
+        assertEquals(2, result.at("/query/operands/1/classOut").asInt());
+        assertTrue(result.at("/query/classIn").isMissingNode());
+        assertEquals("Mann", result.at("/query/operands/0/operands/0/wrap/key")
+                .asText());
+        assertEquals("Frau", result.at("/query/operands/1/operands/0/wrap/key")
+                .asText());
+        assertEquals(1, result.at("/meta/highlight/0").asInt());
+        assertEquals(2, result.at("/meta/highlight/1").asInt());
+        
+        
+        query = "[p='NN']@([base='Mann'][orth='Frau'])";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operation").asText());
+        assertEquals("koral:group", result.at("/query/operands/1/@type").asText());
+        assertEquals("operation:class", result.at("/query/operands/1/operation")
+                .asText());
+        assertEquals(1, result.at("/query/operands/1/classOut").asInt());
+        assertTrue(result.at("/query/operands/1/classIn").isMissingNode());
+        assertEquals("Mann",
+                result.at("/query/operands/1/operands/0/operands/0/wrap/key")
+                        .asText());
+        assertEquals("Frau",
+                result.at("/query/operands/1/operands/0/operands/1/wrap/key")
+                        .asText());
+
+        query = "@([base='Mann'][orth='Frau'])[p='NN']";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operation").asText());
+        assertEquals("koral:group", result.at("/query/operands/0/@type").asText());
+        assertEquals("operation:class", result.at("/query/operands/0/operation")
+                .asText());
+        assertEquals(1, result.at("/query/operands/0/classOut").asInt());
+        assertTrue(result.at("/query/operands/0/classIn").isMissingNode());
+        assertEquals("Mann",
+                result.at("/query/operands/0/operands/0/operands/0/wrap/key")
+                        .asText());
+        assertEquals("Frau",
+                result.at("/query/operands/0/operands/0/operands/1/wrap/key")
+                        .asText());
+        assertEquals(1, result.at("/meta/highlight/0").asInt());
+       
+ 
+    }
+
+    
+
+    @Ignore // CQP doesn't support relational queries like that
+    @Test
+    public void testRelations () throws JsonProcessingException, IOException {
+        query = "dominates(<s>,<np>)";  // span s dominates span np. ; 
+        // possible equivalent: <s>[]*<np>[]*</np>[]*</s>; or using within? -- operation relation is different;
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:relation", result.at("/query/operation").asText());
+        assertEquals("koral:relation", result.at("/query/relation/@type").asText());
+        assertEquals("c", result.at("/query/relation/wrap/layer").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("np", result.at("/query/operands/1/wrap/key").asText());
+
+        query = "relatesTo([base=\"Baum\"],<np>)"; // Baum dominates np;
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:relation", result.at("/query/operation").asText());
+        assertEquals("koral:relation", result.at("/query/relation/@type").asText());
+        assertEquals("Baum", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("np", result.at("/query/operands/1/wrap/key").asText());
+
+        query = "relatesTo(\"Baum\",<np>)";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("orth", result.at("/query/operands/0/wrap/layer").asText());
+        assertEquals("Baum", result.at("/query/operands/0/wrap/key").asText());
+
+        query = "relatesTo(mate/d=HEAD:<np>,[base=\"Baum\"])";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("lemma", result.at("/query/operands/1/wrap/layer").asText());
+        assertEquals("Baum", result.at("/query/operands/1/wrap/key").asText());
+        assertEquals("koral:relation", result.at("/query/relation/@type").asText());
+        assertEquals("mate", result.at("/query/relation/wrap/foundry").asText());
+        assertEquals("d", result.at("/query/relation/wrap/layer").asText());
+        assertEquals("HEAD", result.at("/query/relation/wrap/key").asText());
+
+        query = "dependency([base=\"fällen\"],[base=\"Baum\"])";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("lemma", result.at("/query/operands/0/wrap/layer").asText());
+        assertEquals("fällen", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("lemma", result.at("/query/operands/1/wrap/layer").asText());
+        assertEquals("Baum", result.at("/query/operands/1/wrap/key").asText());
+        assertEquals("koral:relation", result.at("/query/relation/@type").asText());
+        assertEquals("d", result.at("/query/relation/wrap/layer").asText());
+
+        query = "dominates(\"Baum\",<np>)";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("orth", result.at("/query/operands/0/wrap/layer").asText());
+        assertEquals("Baum", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("koral:relation", result.at("/query/relation/@type").asText());
+        assertEquals("c", result.at("/query/relation/wrap/layer").asText());
+
+        query = "dominates(cnx/c:<vp>,<np>)";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("cnx", result.at("/query/relation/wrap/foundry").asText());
+        assertEquals("c", result.at("/query/relation/wrap/layer").asText());
+
+        query = "dominates(cnx/c*:<vp>,<np>)";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("cnx", result.at("/query/relation/wrap/foundry").asText());
+        assertEquals("c", result.at("/query/relation/wrap/layer").asText());
+        assertEquals(0, result.at("/query/relation/boundary/min").asInt());
+        assertTrue(result.at("/query/relation/boundary/max")
+                .isMissingNode());
+
+        query = "dominates(cnx/c{1,5}:<vp>,<np>)";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals(1, result.at("/query/relation/boundary/min").asInt());
+        assertEquals(5, result.at("/query/relation/boundary/max").asInt());
+
+        query = "dominates(cnx/c{,5}:<vp>,<np>)";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals(0, result.at("/query/relation/boundary/min").asInt());
+        assertEquals(5, result.at("/query/relation/boundary/max").asInt());
+
+        query = "dominates(cnx/c{5}:<vp>,<np>)";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals(5, result.at("/query/relation/boundary/min").asInt());
+        assertEquals(5, result.at("/query/relation/boundary/max").asInt());
+    }
+
+    @Ignore
+    @Test
+    public void testAlign () throws JsonProcessingException, IOException {
+        query = "[orth=\"der\"]^[orth=\"Mann\"]";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("operation:sequence", result.at("/query/operation").asText());
+        assertEquals("der", result.at("/query/operands/0/operands/0/wrap/key")
+                .asText());
+        assertEquals(1, result.at("/query/operands/0/classOut").asInt());
+        assertEquals("Mann", result.at("/query/operands/1/operands/0/wrap/key")
+                .asText());
+        assertEquals("operation:class", result.at("/query/operands/1/operation")
+                .asText());
+        assertEquals(2, result.at("/query/operands/1/classOut").asInt());
+        assertEquals(1, result.at("/meta/alignment/0/0").asInt());
+        assertEquals(2, result.at("/meta/alignment/0/1").asInt());
+
+        query = "[orth='der']^[orth='große'][orth='Mann']";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("operation:sequence", result.at("/query/operation").asText());
+        assertEquals("der", result.at("/query/operands/0/operands/0/wrap/key")
+                .asText());
+        assertEquals(1, result.at("/query/operands/0/classOut").asInt());
+        assertEquals("große", result.at("/query/operands/1/operands/0/wrap/key")
+                .asText());
+        assertEquals("operation:class", result.at("/query/operands/1/operation")
+                .asText());
+        assertEquals(2, result.at("/query/operands/1/classOut").asInt());
+        assertEquals("Mann", result.at("/query/operands/2/wrap/key").asText());
+        assertEquals(1, result.at("/meta/alignment/0/0").asInt());
+        assertEquals(2, result.at("/meta/alignment/0/1").asInt());
+
+
+        query = "([base=\"a\"]^[base=\"b\"])|[base=\"c\"]";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("operation:disjunction", result.at("/query/operation")
+                .asText());
+        assertEquals("operation:sequence", result
+                .at("/query/operands/0/operation").asText());
+        assertEquals("operation:class",
+                result.at("/query/operands/0/operands/1/operation").asText());
+        assertEquals("a",
+                result.at("/query/operands/0/operands/0/operands/0/wrap/key")
+                        .asText());
+        assertEquals("b",
+                result.at("/query/operands/0/operands/1/operands/0/wrap/key")
+                        .asText());
+        assertEquals("c", result.at("/query/operands/1/wrap/key").asText());
+        assertEquals(1, result.at("/query/operands/0/operands/0/classOut").asInt());
+        assertEquals(2, result.at("/query/operands/0/operands/1/classOut").asInt());
+        assertEquals(1, result.at("/meta/alignment/0/0").asInt());
+        assertEquals(2, result.at("/meta/alignment/0/1").asInt());
+
+        query = "([base='a']^[base='b'][base='c'])|[base='d']";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("a",
+                result.at("/query/operands/0/operands/0/operands/0/wrap/key")
+                        .asText());
+        assertEquals("b",
+                result.at("/query/operands/0/operands/1/operands/0/wrap/key")
+                        .asText());
+        assertEquals("c", result.at("/query/operands/0/operands/2/wrap/key")
+                .asText());
+        assertEquals("d", result.at("/query/operands/1/wrap/key").asText());
+
+        query = "([base='a']^[base='b']^[base='c'])|[base='d']";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("a",
+                result.at("/query/operands/0/operands/0/operands/0/wrap/key")
+                        .asText());
+        assertEquals(1, result.at("/query/operands/0/operands/0/classOut").asInt());
+        assertEquals("b",
+                result.at("/query/operands/0/operands/1/operands/0/wrap/key")
+                        .asText());
+        assertEquals(2, result.at("/query/operands/0/operands/1/classOut").asInt());
+        assertEquals("c",
+                result.at("/query/operands/0/operands/2/operands/0/wrap/key")
+                        .asText());
+        assertEquals(3, result.at("/query/operands/0/operands/2/classOut").asInt());
+        assertEquals("d", result.at("/query/operands/1/wrap/key").asText());
+        assertEquals(1, result.at("/meta/alignment/0/0").asInt());
+        assertEquals(2, result.at("/meta/alignment/0/1").asInt());
+        assertEquals(2, result.at("/meta/alignment/1/0").asInt());
+        assertEquals(3, result.at("/meta/alignment/1/1").asInt());
+
+        query = "^ 'Mann'";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("Mann", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("operation:class", result.at("/query/operation").asText());
+        assertEquals(1, result.at("/query/classOut").asInt());
+        assertEquals(-1, result.at("/meta/alignment/0/0").asInt());
+        assertEquals(1, result.at("/meta/alignment/0/1").asInt());
+
+        query = "'Mann' ^";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("Mann", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("operation:class", result.at("/query/operation").asText());
+        assertEquals(1, result.at("/query/classOut").asInt());
+        assertEquals(1, result.at("/meta/alignment/0/0").asInt());
+        assertEquals(-1, result.at("/meta/alignment/0/1").asInt());
+    }
+
+
+    @Test
+    public void testSimpleQueries () throws JsonProcessingException,
+            IOException {
+        query = "\"Baum\"";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("Baum", result.at("/query/wrap/key").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+
+        query = "'Der' 'Baum'";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:sequence", result.at("/query/operation").asText());
+        assertEquals("koral:token", result.at("/query/operands/0/@type").asText());
+        assertEquals("koral:term", result.at("/query/operands/0/wrap/@type")
+                .asText());
+        assertEquals("Der", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("Baum", result.at("/query/operands/1/wrap/key").asText());
+        assertEquals("orth", result.at("/query/operands/0/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/operands/0/wrap/match")
+                .asText());
+        assertEquals("orth", result.at("/query/operands/1/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/operands/1/wrap/match")
+                .asText());
+
+        query = "\"Der\" \"große\" \"Baum\"";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("Der", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("große", result.at("/query/operands/1/wrap/key").asText());
+        assertEquals("Baum", result.at("/query/operands/2/wrap/key").asText());
+
+        query = "\"Der\" ('große'|'kleine') 'Baum'";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("Der", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("operation:disjunction",
+                result.at("/query/operands/1/operation").asText());
+        assertEquals("große", result.at("/query/operands/1/operands/0/wrap/key")
+                .asText());
+        assertEquals("kleine", result.at("/query/operands/1/operands/1/wrap/key")
+                .asText());
+        assertEquals("Baum", result.at("/query/operands/2/wrap/key").asText());
+
+        query = "'der' 'große' 'Baum' | 'der' 'kleine' 'Baum'";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("operation:disjunction", result.at("/query/operation")
+                .asText());
+        assertEquals("der", result.at("/query/operands/0/operands/0/wrap/key")
+                .asText());
+        assertEquals("große", result.at("/query/operands/0/operands/1/wrap/key")
+                .asText());
+        assertEquals("Baum", result.at("/query/operands/0/operands/2/wrap/key")
+                .asText());
+        assertEquals("der", result.at("/query/operands/1/operands/0/wrap/key")
+                .asText());
+        assertEquals("kleine", result.at("/query/operands/1/operands/1/wrap/key")
+                .asText());
+        assertEquals("Baum", result.at("/query/operands/1/operands/2/wrap/key")
+                .asText());
+
+        query = "\"Der\" [p=\"ADJA\"] \"Baum\"";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("Der", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("ADJA", result.at("/query/operands/1/wrap/key").asText());
+        assertEquals("p", result.at("/query/operands/1/wrap/layer").asText());
+        assertEquals("Baum", result.at("/query/operands/2/wrap/key").asText());
+    }
+
+
+    @Test
+    public void testWithin () throws JsonProcessingException, IOException {
+        
+        query= "[pos=\"NN\"] []* [pos=\"NN\"] within np;";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:isAround", result.at("/query/frames/0").asText());
+        assertEquals("np", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("NN", result.at("/query/operands/1/operands/0/wrap/key").asText());
+        assertEquals("operation:repetition", result.at("/query/operands/1/operands/1/operation").asText());
+        assertEquals("NN", result.at("/query/operands/1/operands/2/wrap/key").asText());
+        assertEquals("operation:sequence", result.at("/query/operands/1/operation").asText());
+    	
+    	query = "[p='VVFIN'] within s";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:isAround", result.at("/query/frames/0").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+     
+        assertEquals("VVFIN", result.at("/query/operands/1/wrap/key").asText());
+        
+        
+    }
+
+   @Test
+   public void testWithinElement () throws JsonProcessingException, IOException {
+	   // within implemented with span now!
+        query = "[p='VVFIN'] within <base/s=s>";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:isAround", result.at("/query/frames/0").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("VVFIN", result.at("/query/operands/1/wrap/key").asText());
+        }
+
+
+    @Test
+    public void testSpanSerialization () throws JsonProcessingException,
+            IOException {
+
+      //  query = "<s> []+ 'der' []+ </s>)"; and query = "'der' within s"; should serialize identically
+      
+        query = "<s> []+ 'der' []+ </s>)";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+
+        String result1 = result.toString();
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:isAround", result.at("/query/frames/0").asText());
+        assertEquals(1, result.at("/query/frames").size());
+        
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/@type").asText());
+
+
+        query = "'der' within s";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        String result2 = result.toString();
+        
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation").asText());
+        assertEquals("frames:isAround", result.at("/query/frames/0").asText());
+        assertEquals(1, result.at("/query/frames").size());
+        
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        assertEquals("koral:token", result.at("/query/operands/1/@type").asText());
+        
+        assertEquals(result1, result2);
+    }
+
+    @Ignore
+    @Test
+    public void testQueryReferences () throws JsonProcessingException, IOException {
+        query = "{#test}";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:queryRef", result.at("/query/@type").asText());
+        assertEquals("test", result.at("/query/ref").asText());
+
+        query = "{#admin/example}";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("koral:queryRef", result.at("/query/@type").asText());
+        assertEquals("admin/example", result.at("/query/ref").asText());
+
+        query = "Der {#admin/example} [orth=Baum]";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+
+        assertEquals("koral:token", result.at("/query/operands/0/@type").asText());
+        assertEquals("koral:queryRef", result.at("/query/operands/1/@type").asText());
+        assertEquals("admin/example", result.at("/query/operands/1/ref").asText());
+        assertEquals("koral:token", result.at("/query/operands/2/@type").asText());
+
+        query = "[orth=Der]{#admin/example}{1,}[orth=Baum]";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+
+        assertEquals("koral:token", result.at("/query/operands/0/@type").asText());
+        assertEquals("koral:group", result.at("/query/operands/1/@type").asText());
+        assertEquals("koral:queryRef", result.at("/query/operands/1/operands/0/@type").asText());
+        assertEquals("admin/example", result.at("/query/operands/1/operands/0/ref").asText());
+        assertEquals("koral:token", result.at("/query/operands/2/@type").asText());
+    }
+
+    @Test
+    public void testMeta () throws JsonProcessingException, IOException {
+        query = "'x' meta textClass='Sport'";
+        qs.setQuery(query, "CQP");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("x", result.at("/query/wrap/key").asText());
+        assertEquals("koral:doc", result.at("/collection/@type").asText());
+        assertEquals("textClass", result.at("/collection/key").asText());
+        assertEquals("Sport", result.at("/collection/value").asText());
+
+        query = "\"x\" meta textClass='Sport'";
+        qs.setQuery(query, "CQP");
+        qs.setCollection("author=Smith");
+        result = mapper.readTree(qs.toJSON());
+        assertEquals("x", result.at("/query/wrap/key").asText());
+        assertEquals("koral:docGroup", result.at("/collection/@type").asText());
+        assertEquals("operation:and", result.at("/collection/operation").asText());
+        assertEquals("textClass", result.at("/collection/operands/0/key").asText());
+        assertEquals("Sport", result.at("/collection/operands/0/value").asText());
+        assertEquals("author", result.at("/collection/operands/1/key").asText());
+        assertEquals("Smith", result.at("/collection/operands/1/value").asText());
+    }
+   
+ 
+}
\ No newline at end of file
diff --git a/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPRegexTest.java b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPRegexTest.java
new file mode 100644
index 0000000..fe84399
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPRegexTest.java
@@ -0,0 +1,226 @@
+package de.ids_mannheim.korap.query.test.cqp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.query.test.BaseQueryTest;
+
+public class CQPRegexTest extends BaseQueryTest {
+
+    private JsonNode result;
+
+    public CQPRegexTest () {
+        super("CQP");
+    }
+
+
+    @Test
+    public void testRegex () throws JsonProcessingException {
+        query = "[orth=\"M(a|ä)nn(er)?\"]";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("M(a|ä)nn(er)?", result.at("/query/wrap/key").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+
+        query = "\".*?Mann.*?\"";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals(".*?Mann.*?", result.at("/query/wrap/key").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+
+        // issue #56
+        query = "„.*?Mann.*?“";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals(".*?Mann.*?", result.at("/query/wrap/key").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+
+        query = "“.*?Mann.*?”";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals(".*?Mann.*?", result.at("/query/wrap/key").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+
+        query = "\"z.B.\""; // /x is not implemented
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("z.B.", result.at("/query/wrap/key").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        
+        
+        query = "\"?\"";
+        result = runQuery(query);
+        assertEquals(302, result.at("/errors/0/0").asInt());
+        
+        query = "\"\"\"";
+        result = runQuery(query);
+        assertEquals("", result.at("/query/wrap/key").asText());
+        assertNotEquals(302, result.at("/errors/0/0").asInt());
+    }
+
+
+    @Test
+    public void testRegexEscape () throws JsonProcessingException {
+        // Escape regex symbols
+
+        query = "\"a\\.\"";
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("a\\.", result.at("/query/wrap/key").asText());
+
+        // escape doublequoutes and singlequoutes
+        query = "'\"';";  // query= '"';
+        result = runQuery(query);
+
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("\"", result.at("/query/wrap/key").asText());
+
+        query = "'copil\"';";  // query= 'copil"';
+        result = runQuery(query);
+
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("copil\"", result.at("/query/wrap/key").asText());
+
+        query = "\"copil'\";";  // query= "copil'";
+        result = runQuery(query);
+
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("copil'", result.at("/query/wrap/key").asText());
+
+        query = "\"copil\"\"\";"; //  query= "copil"""; same as query= 'copil"';
+        //escape doublequoutes by doubling them: we are looking for string: copil"
+        result = runQuery(query);
+
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("copil\"", result.at("/query/wrap/key").asText());
+
+        query = "'copil''';"; // same as  query= "copil'";
+        // escape quoutes by doubling them
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("copil'", result.at("/query/wrap/key").asText());
+
+        query = "\"22\\\"-inch\";"; // query = "22"-inch"
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("22\\\"-inch", result.at("/query/wrap/key").asText());
+
+
+        query = "'a''.+?';"; //query = 'a''.+?'
+        result = runQuery(query);
+
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("a'.+?", result.at("/query/wrap/key").asText());
+
+
+
+        query = "\"a\"\".+?\";"; //query = "a"".?"
+        result = runQuery(query);
+
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("a\".+?", result.at("/query/wrap/key").asText());
+
+
+
+        query = "\"a.+?\"";    // query = "a.+?"
+        result = runQuery(query);
+
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("a.+?", result.at("/query/wrap/key").asText());
+
+        query = "\"a\\.\"";  //query = "a\."
+        result = runQuery(query);
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("a\\.", result.at("/query/wrap/key").asText());
+
+        query = "\"a\\.\\+\\?\\\\\""; //query = "a\.\+\?\\"
+        result = runQuery(query);
+
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("a\\.\\+\\?\\\\", result.at("/query/wrap/key").asText());
+    }
+
+
+    @Test
+    public void testRegexWhiteSpace () throws JsonProcessingException {
+        // Escape regex symbols
+        query = "\"a b\"";
+        result = runQuery(query);
+
+        assertEquals("koral:token", result.at("/query/@type").asText());
+        assertEquals("koral:term", result.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", result.at("/query/wrap/type").asText());
+        assertEquals("orth", result.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", result.at("/query/wrap/match").asText());
+        assertEquals("a b", result.at("/query/wrap/key").asText());
+    }
+
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPRegionTest.java b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPRegionTest.java
new file mode 100644
index 0000000..1a52a17
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPRegionTest.java
@@ -0,0 +1,197 @@
+package de.ids_mannheim.korap.query.test.cqp;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.Lists;
+
+import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
+import de.ids_mannheim.korap.query.test.BaseQueryTest;
+
+/**
+ * @author Elena Irimia, margaretha
+ *
+ */
+public class CQPRegionTest extends BaseQueryTest {
+
+    public CQPRegionTest () {
+        super("CQP");
+    }
+
+
+    @Test
+    public void testMatchingAttributeForAllRegion ()
+            throws JsonMappingException, JsonProcessingException {
+
+
+        // /region needs a span argument
+         JsonNode n = runQuery("/region[(class=\"header\")]");
+
+        // EM: is this the expected result? Elena: i guess so...
+        assertEquals(StatusCodes.MALFORMED_QUERY, n.at("/errors/0/0").asInt());
+    }
+
+
+    @Test
+    public void testMatchingAttributeInSentence ()
+            throws JsonMappingException, JsonProcessingException {
+        JsonNode res = runQuery("/region[<s (class=\"header\")>]");
+        assertEquals("s", res.at("/query/wrap/key").asText());
+        assertEquals("koral:term", res.at("/query/wrap/@type").asText());
+        assertEquals("koral:term", res.at("/query/attr/@type").asText());
+        assertEquals("class", res.at("/query/attr/key").asText());
+        assertEquals("header", res.at("/query/attr/value").asText());
+        assertEquals("match:eq", res.at("/query/attr/match").asText());
+        
+    }
+
+
+    @Test
+    public void testSpans () throws JsonProcessingException, IOException {
+
+        
+        
+        
+        JsonNode res = runQuery(
+                "/region[<cnx/c!=vp (class!=\"header\" & id=\"7\")>]");
+        assertEquals("koral:span", res.at("/query/@type").asText());
+        assertEquals("vp", res.at("/query/wrap/key").asText());
+        assertEquals("cnx", res.at("/query/wrap/foundry").asText());
+        assertEquals("c", res.at("/query/wrap/layer").asText());
+        assertEquals("match:ne", res.at("/query/wrap/match").asText());
+        assertEquals("koral:termGroup", res.at("/query/attr/@type").asText());
+        assertEquals("relation:and", res.at("/query/attr/relation").asText());
+        operands = Lists
+                .newArrayList(res.at("/query/attr/operands").elements());
+        assertEquals("koral:term", operands.get(0).at("/@type").asText());
+        assertEquals("class", operands.get(0).at("/key").asText());
+        assertEquals("header", operands.get(0).at("/value").asText());
+        assertEquals("match:ne", operands.get(0).at("/match").asText());
+        assertEquals("koral:term", operands.get(1).at("/@type").asText());
+        assertEquals("id", operands.get(1).at("/key").asText());
+        assertEquals(7, operands.get(1).at("/value").asInt());
+        assertEquals("match:eq", operands.get(1).at("/match").asText());
+
+        
+
+        res = runQuery("/region[<cnx/c=vp (class!=\"header\" & id=\"7\")>]");
+        assertEquals("koral:span", res.at("/query/@type").asText());
+        assertEquals("vp", res.at("/query/wrap/key").asText());
+        assertEquals("cnx", res.at("/query/wrap/foundry").asText());
+        assertEquals("c", res.at("/query/wrap/layer").asText());
+        assertEquals("koral:termGroup", res.at("/query/attr/@type").asText());
+        assertEquals("relation:and", res.at("/query/attr/relation").asText());
+        operands = Lists
+                .newArrayList(res.at("/query/attr/operands").elements());
+        assertEquals("koral:term", operands.get(0).at("/@type").asText());
+        assertEquals("class", operands.get(0).at("/key").asText());
+        assertEquals("header", operands.get(0).at("/value").asText());
+        assertEquals("match:ne", operands.get(0).at("/match").asText());
+        assertEquals("koral:term", operands.get(1).at("/@type").asText());
+        assertEquals("id", operands.get(1).at("/key").asText());
+        assertEquals(7, operands.get(1).at("/value").asInt());
+        assertEquals("match:eq", operands.get(1).at("/match").asText());
+
+        res = runQuery("/region[<cnx/c=vp (class=\"header\")>]");
+
+        assertEquals("koral:span", res.at("/query/@type").asText());
+        assertEquals("vp", res.at("/query/wrap/key").asText());
+        assertEquals("cnx", res.at("/query/wrap/foundry").asText());
+        assertEquals("c", res.at("/query/wrap/layer").asText());
+        assertEquals("class", res.at("/query/attr/key").asText());
+        assertEquals("header", res.at("/query/attr/value").asText());
+        assertEquals("match:eq", res.at("/query/attr/match").asText());
+
+        // matches all sentences
+        query = "/region[s]";
+        res = runQuery(query);
+        assertEquals("koral:span", res.at("/query/@type").asText());
+        assertEquals("s", res.at("/query/wrap/key").asText());
+
+        // matches all vps;
+        query = "/region[<vp>]";
+        res = runQuery(query);
+        assertEquals("koral:span", res.at("/query/@type").asText());
+        assertEquals("vp", res.at("/query/wrap/key").asText());
+
+        
+
+        query = "/region[<cnx/c=vp>]";
+        res = runQuery(query);
+        assertEquals("koral:span", res.at("/query/@type").asText());
+        assertEquals("vp", res.at("/query/wrap/key").asText());
+        assertEquals("cnx", res.at("/query/wrap/foundry").asText());
+        assertEquals("c", res.at("/query/wrap/layer").asText());
+
+
+        query = "/region[<cnx/c!=vp>]";
+        res = runQuery(query);
+        assertEquals("koral:span", res.at("/query/@type").asText());
+        assertEquals("vp", res.at("/query/wrap/key").asText());
+        assertEquals("cnx", res.at("/query/wrap/foundry").asText());
+        assertEquals("c", res.at("/query/wrap/layer").asText());
+        assertEquals("match:ne", res.at("/query/wrap/match").asText());
+
+
+        query = "/region[<cnx/c!=vp class!=\"header\">]";
+        res = runQuery(query);
+        assertEquals("koral:span", res.at("/query/@type").asText());
+        assertEquals("vp", res.at("/query/wrap/key").asText());
+        assertEquals("cnx", res.at("/query/wrap/foundry").asText());
+        assertEquals("c", res.at("/query/wrap/layer").asText());
+        assertEquals("match:ne", res.at("/query/wrap/match").asText());
+        assertEquals("class", res.at("/query/attr/key").asText());
+        assertEquals("header", res.at("/query/attr/value").asText());
+        assertEquals("match:ne", res.at("/query/attr/match").asText());
+
+
+
+        query = "/region[<cnx/c!=vp !(class!=\"header\")>]";
+        res = runQuery(query);
+        assertEquals("koral:span", res.at("/query/@type").asText());
+        assertEquals("vp", res.at("/query/wrap/key").asText());
+        assertEquals("cnx", res.at("/query/wrap/foundry").asText());
+        assertEquals("c", res.at("/query/wrap/layer").asText());
+        assertEquals("match:ne", res.at("/query/wrap/match").asText());
+        assertEquals("class", res.at("/query/attr/key").asText());
+        assertEquals("header", res.at("/query/attr/value").asText());
+        assertEquals("match:eq", res.at("/query/attr/match").asText());
+
+    }
+    
+
+    @Test
+    public void testRegionAttributeGroupNegation ()
+            throws JsonMappingException, JsonProcessingException {
+        query = "/region[<cnx/c!=vp !(class=\"header\" & id=\"7\")>]";
+        JsonNode res = runQuery(query);
+
+        assertEquals("koral:span", res.at("/query/@type").asText());
+        assertEquals("vp", res.at("/query/wrap/key").asText());
+        assertEquals("cnx", res.at("/query/wrap/foundry").asText());
+        assertEquals("c", res.at("/query/wrap/layer").asText());
+        assertEquals("match:ne", res.at("/query/wrap/match").asText());
+        assertEquals("koral:termGroup", res.at("/query/attr/@type").asText());
+
+        assertEquals("relation:or", res.at("/query/attr/relation").asText());
+
+        operands = Lists
+                .newArrayList(res.at("/query/attr/operands").elements());
+        assertEquals("koral:term", operands.get(0).at("/@type").asText());
+        assertEquals("class", operands.get(0).at("/key").asText());
+        assertEquals("header", operands.get(0).at("/value").asText());
+        assertEquals("match:ne", operands.get(0).at("/match").asText());
+        assertEquals("koral:term", operands.get(1).at("/@type").asText());
+        assertEquals("id", operands.get(1).at("/key").asText());
+        assertEquals(7, operands.get(1).at("/value").asInt());
+        assertEquals("match:ne", operands.get(1).at("/match").asText());
+    }
+
+
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPRepetitionTest.java b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPRepetitionTest.java
new file mode 100644
index 0000000..558ff94
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPRepetitionTest.java
@@ -0,0 +1,119 @@
+package de.ids_mannheim.korap.query.test.cqp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.query.test.BaseQueryTest;
+
+public class CQPRepetitionTest extends BaseQueryTest {
+
+    private JsonNode result;
+
+    public CQPRepetitionTest () {
+        super("CQP");
+    }
+
+    private void checkKoralTerm (JsonNode node) {
+        assertEquals("koral:term", node.at("/@type").asText());
+        assertEquals("match:eq", node.at("/match").asText());
+        assertEquals("type:regex", node.at("/type").asText());
+        assertEquals("orth", node.at("/layer").asText());
+        assertEquals("der", node.at("/key").asText());
+    }
+    
+    @Test
+    public void testRepetition () throws JsonProcessingException, IOException {
+        query = "'der'{3}";
+        result = runQuery(query);
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:repetition", result.at("/query/operation")
+                .asText());
+        assertEquals(1, result.at("/query/operands").size());
+        
+        checkKoralTerm(result.at("/query/operands/0/wrap"));
+        
+        assertEquals("koral:boundary", result.at("/query/boundary/@type").asText());
+        assertEquals(3, result.at("/query/boundary/min").asInt());
+        assertEquals(3, result.at("/query/boundary/max").asInt());
+
+        
+        query = "\"der\"{,3}";
+        result = runQuery(query);
+        assertEquals(0, result.at("/query/boundary/min").asInt());
+        assertEquals(3, result.at("/query/boundary/max").asInt());
+
+        query = "'der'{3,}";
+        result = runQuery(query);
+        assertEquals(3, result.at("/query/boundary/min").asInt());
+        assertTrue(result.at("/query/boundary/max").isMissingNode());
+
+        query = "\"der\"{3,7}";
+        result = runQuery(query);
+        assertEquals(3, result.at("/query/boundary/min").asInt());
+        assertEquals(7, result.at("/query/boundary/max").asInt());
+
+        query = "'der'*";
+        result = runQuery(query);
+        assertEquals(0, result.at("/query/boundary/min").asInt());
+        assertTrue(result.at("/query/boundary/max").isMissingNode());
+
+        query = "'der'+";
+        result = runQuery(query);
+        assertEquals(1, result.at("/query/boundary/min").asInt());
+        assertTrue(result.at("/query/boundary/max").isMissingNode());
+    };
+
+    @Test
+    public void testRepetitionInSentence () throws JsonProcessingException{
+        // this queries were written in PQ+ with contains()
+        query = "<s> []+ (\"der\")* []+ </s>";
+        result = runQuery(query);
+
+        assertEquals("koral:group", result.at("/query/@type").asText());
+        assertEquals("operation:position", result.at("/query/operation")
+                .asText());
+        assertEquals("frames:isAround", result.at("/query/frames/0").asText());
+        assertEquals(2, result.at("/query/operands").size());
+        
+        assertEquals("koral:span", result.at("/query/operands/0/@type").asText());
+        assertEquals("koral:term", result.at("/query/operands/0/wrap/@type").asText());
+        assertEquals("s", result.at("/query/operands/0/wrap/key").asText());
+        
+        assertEquals("koral:group", result.at("/query/operands/1/@type").asText());
+        assertEquals("operation:repetition", result.at("/query/operands/1/operation")
+                .asText());
+        assertEquals(1, result.at("/query/operands/1/operands").size());
+        
+        checkKoralTerm(result.at("/query/operands/1/operands/0/wrap"));
+        
+        assertEquals(0, result.at("/query/operands/1/operands/0/boundary/min").asInt());
+        assertTrue(result.at("/query/operands/1/operands/0/boundary/max").isMissingNode());
+        assertTrue(result.at("/query/operands/1/operands/2/").isMissingNode()); // []* are not taken into account as tokens;
+    }
+    
+    @Test
+    public void testGroupRepetition () throws JsonProcessingException{
+        query = "<s> []+ (\"der\"){3,} []+ </s>";
+        result = runQuery(query);
+        assertEquals(3, result.at("/query/operands/1/boundary/min").asInt());
+        assertTrue(result.at("/query/operands/1/boundary/max")
+                .isMissingNode());
+
+        query = "<s> []+ (\"der\"){,3} []+ </s>";
+        result = runQuery(query);
+        assertEquals(0, result.at("/query/operands/1/boundary/min").asInt());
+        assertEquals(3, result.at("/query/operands/1/boundary/max").asInt());
+
+        query = "<s> []+ (\"der\"){3,7} []+ </s>";
+        result = runQuery(query);
+        assertEquals(3, result.at("/query/operands/1/boundary/min").asInt());
+        assertEquals(7, result.at("/query/operands/1/boundary/max").asInt());
+    };
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPSkipSentenceBoundaries.java b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPSkipSentenceBoundaries.java
new file mode 100644
index 0000000..d3ad302
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPSkipSentenceBoundaries.java
@@ -0,0 +1,190 @@
+package de.ids_mannheim.korap.query.test.cqp;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.junit.Test;
+// import org.junit.Ignore;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+//import com.google.common.collect.Lists;
+
+
+import de.ids_mannheim.korap.query.serialize.QuerySerializer;
+
+import de.ids_mannheim.korap.query.test.BaseQueryTest;
+
+public class CQPSkipSentenceBoundaries extends BaseQueryTest {
+
+    String query;
+    ArrayList<JsonNode> operands;
+
+    QuerySerializer qs = new QuerySerializer();
+    ObjectMapper mapper = new ObjectMapper();
+    JsonNode res;
+
+
+    public CQPSkipSentenceBoundaries () {
+        super("CQP");
+    }
+    
+    @Test
+    public void spansegment () throws JsonProcessingException, IOException {
+    query = "<corenlp/c=NP>;";
+    qs.setQuery(query, "CQP");
+    res = mapper.readTree(qs.toJSON());
+    assertEquals("koral:span", res.at("/query/@type").asText());
+    assertEquals("corenlp", res.at("/query/wrap/foundry").asText());
+    assertEquals("koral:term", res.at("/query/wrap/@type").asText());
+    assertEquals("c", res.at("/query/wrap/layer").asText());
+    assertEquals("NP", res.at("/query/wrap/key").asText());
+  
+  
+    // this is too complicated to implement in a stable way; 
+    //query = "[(pos = \"NNS?\") & !np ]";
+    //qs.setQuery(query, "CQP");
+    //res = mapper.readTree(qs.toJSON());
+    //assertNotEquals("koral:group", res.at("/query/@type").asText());
+}
+    
+    
+    @Test
+
+    public void skipendswith () throws JsonProcessingException, IOException {
+       // all the sequence is in the span of a sentence, at the end of it.
+        query = "\"copil\" []{,5} \"cuminte\" </base/s=s>";
+        qs.setQuery(query, "CQP");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", res.at("/query/@type").asText());
+        assertEquals("operation:position", res.at("/query/operation").asText());
+        assertEquals(2, res.at("/query/operands").size());
+        assertEquals(2, res.at("/query/frames").size());
+        assertEquals("frames:endsWith", res.at("/query/frames/0").asText());
+        assertEquals("frames:matches", res.at("/query/frames/1").asText());
+ 
+        
+        assertEquals("koral:span", res.at("/query/operands/0/@type").asText());
+        assertEquals("s", res.at("/query/operands/0/wrap/key").asText());
+        assertEquals("base", res.at("/query/operands/0/wrap/foundry").asText());
+        assertEquals("s", res.at("/query/operands/0/wrap/layer").asText());
+        
+        assertEquals("koral:reference", res.at("/query/operands/1/@type").asText());
+        assertEquals("operation:focus", res.at("/query/operands/1/operation").asText());
+
+        assertEquals("koral:group", res.at("/query/operands/1/operands/0/@type").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/1/operands/0/operation").asText());
+
+       
+        assertEquals("koral:group", res.at("/query/operands/1/operands/0/operands/0/@type").asText());
+        assertEquals("operation:class", res.at("/query/operands/1/operands/0/operands/0/operation").asText());
+        assertEquals("koral:token", res.at("/query/operands/1/operands/0/operands/0/operands/0/@type").asText());
+        assertEquals("copil", res.at("/query/operands/1/operands/0/operands/0/operands/0/wrap/key").asText());
+        
+        assertEquals("koral:group", res.at("/query/operands/1/operands/0/operands/1/@type").asText());
+        assertEquals("operation:class", res.at("/query/operands/1/operands/0/operands/1/operation").asText());
+        assertEquals("operation:repetition",
+                res.at("/query/operands/1/operands/0/operands/1/operands/0/operation").asText());
+        assertEquals("koral:token",
+                res.at("/query/operands/1/operands/0/operands/1/operands/0/operands/0/@type").asText());
+        assertEquals("0", res.at("/query/operands/1/operands/0/operands/1/operands/0/boundary/min").asText());
+        assertEquals("5", res.at("/query/operands/1/operands/0/operands/1/operands/0/boundary/max").asText());        
+
+        
+        assertEquals("koral:group", res.at("/query/operands/1/operands/0/operands/2/@type").asText());
+        assertEquals("operation:class", res.at("/query/operands/1/operands/0/operands/2/operation").asText());
+        assertEquals("koral:token", res.at("/query/operands/1/operands/0/operands/2/operands/0/@type").asText());
+        assertEquals("cuminte", res.at("/query/operands/1/operands/0/operands/2/operands/0/wrap/key").asText());
+
+
+
+    }
+
+
+    @Test
+    public void skipstartswith () throws JsonProcessingException, IOException {
+        query = "<base/s=s> \"copil\" []{,5} \"cuminte\"";
+        // all the sequence is in the span of a sentence, at the beggining of it.
+        
+        qs.setQuery(query, "CQP");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", res.at("/query/@type").asText());
+        assertEquals("operation:position", res.at("/query/operation").asText());
+        assertEquals(2, res.at("/query/operands").size());
+        assertEquals(2, res.at("/query/frames").size());
+        assertEquals("frames:startsWith", res.at("/query/frames/0").asText());
+        assertEquals("frames:matches", res.at("/query/frames/1").asText());
+ 
+        
+        assertEquals("koral:span", res.at("/query/operands/0/@type").asText());
+        assertEquals("s", res.at("/query/operands/0/wrap/key").asText());
+        assertEquals("base", res.at("/query/operands/0/wrap/foundry").asText());
+        assertEquals("s", res.at("/query/operands/0/wrap/layer").asText());
+        
+        assertEquals("koral:reference", res.at("/query/operands/1/@type").asText());
+        assertEquals("operation:focus", res.at("/query/operands/1/operation").asText());
+
+        assertEquals("koral:group", res.at("/query/operands/1/operands/0/@type").asText());
+        assertEquals("operation:sequence", res.at("/query/operands/1/operands/0/operation").asText());
+
+       
+        assertEquals("koral:group", res.at("/query/operands/1/operands/0/operands/0/@type").asText());
+        assertEquals("operation:class", res.at("/query/operands/1/operands/0/operands/0/operation").asText());
+        assertEquals("koral:token", res.at("/query/operands/1/operands/0/operands/0/operands/0/@type").asText());
+        assertEquals("copil", res.at("/query/operands/1/operands/0/operands/0/operands/0/wrap/key").asText());
+        
+        assertEquals("koral:group", res.at("/query/operands/1/operands/0/operands/1/@type").asText());
+        assertEquals("operation:class", res.at("/query/operands/1/operands/0/operands/1/operation").asText());
+        assertEquals("operation:repetition",
+                res.at("/query/operands/1/operands/0/operands/1/operands/0/operation").asText());
+        assertEquals("koral:token",
+                res.at("/query/operands/1/operands/0/operands/1/operands/0/operands/0/@type").asText());
+        assertEquals("0", res.at("/query/operands/1/operands/0/operands/1/operands/0/boundary/min").asText());
+        assertEquals("5", res.at("/query/operands/1/operands/0/operands/1/operands/0/boundary/max").asText());        
+
+        
+        assertEquals("koral:group", res.at("/query/operands/1/operands/0/operands/2/@type").asText());
+        assertEquals("operation:class", res.at("/query/operands/1/operands/0/operands/2/operation").asText());
+        assertEquals("koral:token", res.at("/query/operands/1/operands/0/operands/2/operands/0/@type").asText());
+        assertEquals("cuminte", res.at("/query/operands/1/operands/0/operands/2/operands/0/wrap/key").asText());
+
+
+    }
+
+    @Test
+    public void testSequenceQueryWithSentenceStart ()  throws JsonMappingException, JsonProcessingException {
+           query = "\"copil\" []{,5} \"cuminte\" <base/s=s>";
+           qs.setQuery(query, "CQP");
+           res = mapper.readTree(qs.toJSON());
+           assertEquals("koral:reference", res.at("/query/@type").asText());
+           assertEquals("operation:focus", res.at("/query/operation").asText());
+           assertEquals("koral:group", res.at("/query/operands/0/@type").asText());
+           assertEquals("operation:sequence", res.at("/query/operands/0/operation").asText());  
+           assertEquals("koral:group", res.at("/query/operands/0/operands/0/@type").asText());
+           assertEquals("operation:class", res.at("/query/operands/0/operands/0/operation").asText());
+           assertEquals("koral:token", res.at("/query/operands/0/operands/0/operands/0/@type").asText());
+           assertEquals("type:regex", res.at("/query/operands/0/operands/0/operands/0/wrap/type").asText());
+           assertEquals("copil", res.at("/query/operands/0/operands/0/operands/0/wrap/key").asText());
+        assertEquals("koral:group", res.at("/query/operands/0/operands/1/@type").asText());
+        assertEquals("operation:class", res.at("/query/operands/0/operands/1/operation").asText());
+        assertEquals("koral:group", res.at("/query/operands/0/operands/1/operands/0/@type").asText());
+        assertEquals("operation:repetition", res.at("/query/operands/0/operands/1/operands/0/operation").asText());
+        assertEquals("koral:token", res.at("/query/operands/0/operands/1/operands/0/operands/0/@type").asText());
+        assertEquals("0", res.at("/query/operands/0/operands/1/operands/0/boundary/min").asText());
+        assertEquals("5", res.at("/query/operands/0/operands/1/operands/0/boundary/max").asText());
+       
+        assertEquals("type:regex", res.at("/query/operands/0/operands/2/operands/0/wrap/type").asText());
+        assertEquals("cuminte", res.at("/query/operands/0/operands/2/operands/0/wrap/key").asText());
+        
+        assertEquals("koral:span", res.at("/query/operands/0/operands/3/@type").asText());
+        
+       
+    }
+
+
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/test/cqp/CqpGrammarTest.java b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CqpGrammarTest.java
new file mode 100644
index 0000000..1908a0c
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CqpGrammarTest.java
@@ -0,0 +1,962 @@
+package de.ids_mannheim.korap.query.test.cqp;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+import org.junit.Ignore;
+
+import de.ids_mannheim.korap.query.parse.cqp.CQPLexer;
+import de.ids_mannheim.korap.query.parse.cqp.CQPParser;
+import de.ids_mannheim.korap.query.serialize.util.Antlr4DescriptiveErrorListener;
+
+import org.antlr.v4.runtime.*;
+import org.antlr.v4.runtime.tree.*;
+import java.lang.reflect.*;
+//import java.lang.*;
+
+/**
+ * Tests for CQP grammar parsing.
+ */
+public class CqpGrammarTest {
+
+    String query;
+    Lexer lexer = new CQPLexer((CharStream) null);
+    ParserRuleContext tree = null;
+	@Test
+	public void squoutes_verbatim () {
+
+		
+		assertEquals(
+			"(request (query \" ? \"))",  // it should not parse the query
+			treeString("\"?\"")
+			);
+		assertNotEquals(
+			"(request (query \" \" \"))",  // it should not parse the query
+			treeString("\"\"\"")
+			);
+		assertEquals(
+		"(request (query (segment (token (key (regex 'copil'))))))", 
+		treeString("'copil'")
+		);
+		assertEquals(
+		"(request (query (segment (token (key (regex 'copil')) (flag %l)))))", 
+		treeString("'copil'%l")
+		);
+
+		assertEquals(
+		"(request (query (segment (token (key (regex '\\''))))))",  
+		treeString("'\\''")
+		);
+		assertEquals(
+		"(request (query (segment (token (key (regex '\\'')) (flag %l)))))",  
+		treeString("'\\''%l")
+		);
+		
+		assertEquals(
+			"(request (query (segment (token (key (regex ''))))))",  // it should not parse the query
+			treeString("'''")
+			);
+	};
+		
+	
+	@Test
+	public void spantest () {
+				assertEquals(
+					"(request (query < cnx / c ! = vp ! ! >))",  // not parsing the query
+					treeString("<cnx/c!=vp!!>")
+					);
+					
+				
+			
+	};
+
+	
+	@Test
+    public void testRegex$1 () {
+
+    	 	 assertEquals(
+    	 	 "(request (query (segment (token (key (regex \"copil\"))))) ;)", 
+    	 	 treeString("\"copil\";")
+    	 	 );
+    };
+
+    @Test
+    public void testRegex$2 () {
+
+    	 	 assertEquals( 
+    	 	 "(request (query (segment (token (key (regex \"copil.*\"))))) ;)",
+    	 	 treeString("\"copil.*\";")
+    	 	 );
+    };
+
+    @Test
+    public void testRegex$3 () {
+
+    	 	 assertEquals( 
+    	 	 "(request (query (segment (token (key (regex \"copil(ul|a)\"))))) ;)", 
+    	 	 treeString("\"copil(ul|a)\";")
+    	 	 );
+    };
+
+    @Test
+    public void testRegexQuoute () {
+
+    	 	assertEquals( 
+    		"(request (query (segment (token (key (regex 'copil(ul|a)'))))) ;)", 
+    		treeString("'copil(ul|a)';")
+    		);
+    };
+    @Test
+    public void testRegexARond () {
+
+    	 	assertEquals( 
+    		"(request (query (segment (token (key (regex 'elena@racai.ro'))))) ;)", 
+    		treeString("'elena@racai.ro';")
+    		);
+    };
+    @Test
+    public void testRegexFlags () {
+
+    	 	 assertEquals( 
+    		"(request (query (segment (token (key (regex \"copil\")) (flag %ldc)))) ;)", 
+    		treeString("\"copil\" %ldc;")
+    		);
+    		
+    };
+	@Test
+    public void testRegexVerbatim () {
+
+     
+    	assertEquals( 
+    		"(request (query (segment (token (key (regex \"D'Man\\n\")) (flag %l)))) ;)", 
+    		treeString("\"D'Man\n\"%l;")
+    		);
+			
+			assertEquals( 
+    		"(request (query (segment (token [ (term (foundry mate) / (layer b) (termOp =) (key (regex \"D'Man\\n\")) (flag %l)) ]))) ;)", 
+    		treeString("[mate/b=\"D'Man\n\"%l];")
+    		);
+   
+			assertEquals( 
+    		"(request (query (segment (token [ (term (foundry mate) / (layer b) (termOp =) (key (regex 'D\\'Ma \\\\nn')) (flag %l)) ]))))", 
+    		treeString("[mate/b='D\\'Ma \\\\nn'%l]")
+    		);
+			
+    };
+    @Test
+    public void testRegexEscapedQuoutes () {
+
+        // escape by doubling the double quote 	
+    	
+		
+		assertEquals( 
+    		"(request (query (segment (token (key (regex \"22\"\"-inch\"))))) ;)", 
+    		treeString("\"22\"\"-inch\";")
+    		);
+		// when not doubling!
+			assertNotEquals( 
+    		"(request (query (segment (token (key (regex \"22\"-inch\"))))) ;)", 
+    		treeString("\"22\"-inch\";")
+    		);
+    	// escape by using single quotes to encapsulate expressions that contain double quotes
+	 	assertEquals( 
+	    		"(request (query (segment (token (key (regex '22\"-inch'))))) ;)", 
+	    		treeString("'22\"-inch\';")
+	    		);
+    	assertEquals( // this one is working in https://cqpweb.lancs.ac.uk/familyx/
+        		"(request (query (segment (token (key (regex \"22\\\"-inch\"))))) ;)", 
+        		treeString("\"22\\\"-inch\";")
+        		);
+    
+    };
+
+    @Test
+    public void testRegexEscapedSquoutes () {
+    		// escape by doubling the single quote
+    	 	
+			assertEquals( 
+    		"(request (query (segment (token (key (regex ''))))))", 
+    		treeString("''';") // this should not parse!! should signal an error! the regex is constructed when the parser finds the second '  !
+    		);  // how are these situations treated in PQ+?
+			assertEquals( 
+    		"(request (query (segment (token (key (regex 'anna''s house'))))) ;)", 
+    		treeString("'anna''s house';")
+    		);
+			//when not doubling
+			assertNotEquals( 
+    		"(request (query (segment (token (key (regex 'anna's house'))))) ;)", 
+    		treeString("'anna's house';")
+    		);
+    	 	//escape by using double quotes to encapsulate expressions that contain single quotes
+    	 	assertEquals( 
+    	    		"(request (query (segment (token (key (regex \"anna's house\"))))) ;)", 
+    	    		treeString("\"anna's house\";")
+    	    		);
+    	 	assertEquals( 
+    	    		"(request (query (segment (token (key (regex 'anna\\'s house'))))) ;)", 
+    	    		treeString("'anna\\'s house';")
+    	    		);
+    };
+    
+    @Test
+    public void testTerm1 () {
+    		
+    		assertEquals(
+    		"(request (query (segment (token [ (term (layer pos) (termOp =) (key (regex \"JJ\"))) ]))) ;)", 
+    		treeString("[pos = \"JJ\"];")
+    		);
+    };
+
+    @Test
+    public void testTermFlag () {
+
+    	 	assertEquals( 
+    		"(request (query (segment (token [ (term (layer lemma) (termOp =) (key (regex \"pole\")) (flag %c)) ]))) ;)", 
+    		treeString("[lemma = \"pole\" %c];")
+    		);
+    };
+
+    @Test
+    public void testBoolOp1 () {
+    		assertEquals( 
+    		"(request (query (segment (token [ (termGroup (term (layer lemma) (termOp =) (key (regex \"under.+\"))) (boolOp &) (term (layer pos) (termOp =) (key (regex \"V.*\")))) ]))) ;)", 
+    		treeString(" [lemma=\"under.+\" & pos=\"V.*\"];")
+    		);
+    };
+
+    @Test
+    public void testNegTerm () {
+
+    	 	// do more tests for negation!! 
+    		assertEquals( 
+    	 	 "(request (query (segment (token [ (termGroup (term ( (term (layer lemma) (termOp =) (key (regex \"under.+\"))) )) (boolOp &) (term ! ( (term (layer pos) (termOp =) (key (regex \"V.*\"))) ))) ]))) ;)", 
+    	 	 treeString("[(lemma=\"under.+\") & !(pos=\"V.*\")];")
+    	 	 );
+
+			  assertEquals( 
+				"(request (query (segment (token [ (termGroup (term ( (term (layer lemma) (termOp ! =) (key (regex \"under.+\"))) )) (boolOp &) (term ( (term (layer pos) (termOp ! =) (key (regex \"V.*\"))) ))) ]))) ;)", 
+				treeString("[(lemma!=\"under.+\") & (pos!=\"V.*\")];")
+				);
+    };
+
+    @Test
+    public void testNegTermGroup () {
+
+    	 	//do more tests! 
+    	    assertEquals(
+    	 	 "(request (query (segment (token [ (termGroup (term (layer lemma) (termOp =) (key (regex \"go\"))) (boolOp &) (termGroup ! ( (termGroup (term (layer word) (termOp =) (key (regex \"went\")) (flag %c)) (boolOp |) (term (layer word) (termOp =) (key (regex \"gone\")) (flag %c))) ))) ]))) ;)", 
+    	 	 treeString(" [lemma=\"go\" & ! (word=\"went\"%c | word = \"gone\" %c)];")
+    	 	 );
+			  assertEquals(
+				"(request (query (segment (token ! [ (termGroup (term (layer lemma) (termOp =) (key (regex \"go\"))) (boolOp &) (termGroup ! ( (termGroup (term (layer word) (termOp =) (key (regex \"went\")) (flag %c)) (boolOp |) (term (layer word) (termOp =) (key (regex \"gone\")) (flag %c))) ))) ]))) ;)", 
+				treeString(" ![lemma=\"go\" & ! (word=\"went\"%c | word = \"gone\" %c)];")
+				);
+    };
+    @Test
+    public void testValue () {
+
+    	     assertEquals(
+    	     "(request (query (segment (token [ (term (foundry mate) / (layer m) (termOp =) (key (regex 'temp')) : (value (regex 'pres'))) ]))))", 
+    	     treeString("[mate/m='temp':'pres']")
+    	     );
+    	    };
+    @Test
+    public void testRegexBoolOp1 () {
+
+    	 	assertEquals(
+    		"(request (query (sequence (segment (token (key (regex \"on\")))) (segment (token (key (regex \"and\")))) (segment (token (key (regex \"on|off\")))))) ;)", 
+    		treeString("\"on\" \"and\" \"on|off\";")
+    		);
+    };
+    
+    
+    @Test
+    public void testVerbatim () {
+
+    	 	assertEquals(
+    		"(request (query (segment (token [ (term (foundry mate) / (layer b) (termOp =) (key (regex \"D\\\\Ma \\\\nn\"))) ]))))", 
+    		treeString("[mate/b=\"D\\\\Ma \\\\nn\"]")
+    		);
+    };
+    
+    @Test
+    public void testRegexBoolOp2 () {
+
+    	 	assertEquals( 
+    	 	"(request (query (sequence (segment (token (key (regex \"el\")))) (segment (group ( (disjunction (segment (token (key (regex \"bueno\")))) | (segment (token (key (regex \"malo\"))))) ))) (segment (token [ (term (layer pos) (termOp ! =) (key (regex \"N.*\"))) ])))) ;)", 
+    	 	treeString("\"el\" (\"bueno\"|\"malo\") [pos!=\"N.*\"];")
+    	 	);
+    };
+
+    @Test
+    public void testRegexDisjunction1 () {
+
+    	 	 assertEquals( 
+    	 	 "(request (query (sequence (segment (token (key (regex \"es\")))) (segment (group ( (disjunction (sequence (segment (token (key (regex \"el\")))) (segment (token (key (regex \"bueno\"))))) | (sequence (segment (token (key (regex \"el\")))) (segment (token (key (regex \"malo\")))))) ))) (segment (token [ (term (layer pos) (termOp ! =) (key (regex \"N.*\"))) ])))) ;)", 
+    	 	 treeString("\"es\" (\"el\" \"bueno\"|\"el\" \"malo\") [pos!=\"N.*\"];")
+    	 	 );
+    };
+
+    @Test
+    public void testRegexDisjunction2 () {
+
+    	 	assertEquals( 
+    		"(request (query (sequence (segment (token (key (regex \"in\")))) (segment (token (key (regex \"any|every\")))) (segment (token [ (term (layer pos) (termOp =) (key (regex \"NN\"))) ])))) ;)",
+    		treeString("\"in\" \"any|every\" [pos=\"NN\"];")
+    		);
+    };
+
+    @Test
+    public void testTermOpNegation1 () {
+
+    	 	assertEquals( 
+    		"(request (query (segment (token [ (term ! (layer pos) (termOp =) (key (regex \"ADJA\"))) ]))) ;)",
+    		treeString("[!pos=\"ADJA\"];")
+    		);
+    };
+
+    @Test
+    public void testRegexDisjunction3 () {
+
+    	 	assertEquals( 
+    		"(request (query (disjunction (segment (token (key (regex \"on\")))) | (segment (token (key (regex \"off\")))))) ;)",
+    		treeString("\"on\"|\"off\";")
+    		);
+    };
+
+    @Test
+    public void testTermDisjunction () {
+
+    	 	assertEquals( 
+    		"(request (query (segment (token [ (termGroup (term (layer word) (termOp =) (key (regex \"on\"))) (boolOp |) (term (layer word) (termOp =) (key (regex \"off\")))) ]))) ;)",
+    		treeString("[word= \"on\"|word = \"off\"];")
+    		);
+    };
+
+    @Test
+    public void testTermSequence () {
+
+    	 	assertEquals( 
+    		"(request (query (sequence (segment (token [ (term (layer pos) (termOp =) (key (regex \"IN\"))) ])) (segment (token [ (term (layer pos) (termOp =) (key (regex \"DT\"))) ]) (repetition (kleene ?))) (segment (group ( (sequence (segment (token [ (term (layer pos) (termOp =) (key (regex \"RB\"))) ]) (repetition (kleene ?))) (segment (token [ (term (layer pos) (termOp =) (key (regex \"JJ.*\"))) ]))) )) (repetition (kleene *))) (segment (token [ (term (layer pos) (termOp =) (key (regex \"N.*\"))) ]) (repetition (kleene +))))) ;)",
+    		treeString("[pos = \"IN\"] [pos = \"DT\"]? ([pos = \"RB\"]? [pos = \"JJ.*\"])* [pos = \"N.*\"]+;")
+    		);
+    };
+
+    @Test
+    public void testTermOpNegation2 () {
+
+    	 	assertEquals( 
+    		"(request (query (sequence (segment (token [ (term (layer pos) (termOp ! =) (key (regex \"N.*\"))) ])) (segment (token [ (term ! (layer pos) (termOp =) (key (regex \"V.*\"))) ])))) ;)",
+    		treeString(" [pos!=\"N.*\"] [!pos=\"V.*\"];")
+    		);
+    };
+
+    @Test
+    public void testRepetitionRange () {
+
+    	 	assertEquals( 
+    		"(request (query (sequence (segment (group ( (disjunction (sequence (segment (token [ (term (layer pos) (termOp =) (key (regex \"APPR\"))) ])) (segment (token [ (term (layer pos) (termOp =) (key (regex \"ART\"))) ]))) | (segment (token [ (term (layer pos) (termOp =) (key (regex \"ARPRART\"))) ]))) ))) (segment (group ( (sequence (segment (token [ (term (layer pos) (termOp =) (key (regex \"ADJD|ADV\"))) ]) (repetition (kleene ?))) (segment (token [ (term (layer pos) (termOp =) (key (regex \"ADJA\"))) ]))) )) (repetition (kleene *))) (segment (token [ (term (layer pos) (termOp =) (key (regex \"NN\"))) ]) (repetition (range { , (max 1) }))))) ;)",
+    		treeString(" ([pos = \"APPR\"] [pos=\"ART\"] | [pos = \"ARPRART\"]) ([pos=\"ADJD|ADV\"]? [pos=\"ADJA\"])* [pos=\"NN\"]{,1};")
+    		);
+    };
+
+    @Test
+    public void testEmptyToken () {
+
+    	 	assertEquals( 
+    		"(request (query (sequence (segment (token (key (regex \"right\")))) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene ?)))) (segment (token (key (regex \"left\")))))) ;)",
+    		treeString("\"right\" []? \"left\";")
+    		);
+    };
+
+    @Test
+    public void testEmptyTokenWithin () {
+
+    	 	assertEquals( 
+    		"(request (query (sequence (segment (token (key (regex \"no\")))) (segment (token (key (regex \"sooner\")))) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (segment (token (key (regex \"than\")))))) (within within (span (skey s))) ;)",
+    		treeString("\"no\" \"sooner\" []* \"than\" within s;")
+    		);
+    };
+
+    @Test
+    public void testEmptyTokenRange () {
+
+    	 	assertEquals( 
+    		"(request (query (sequence (segment (token (key (regex \"as\")))) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (range { (min 1) , (max 3) })))) (segment (token (key (regex \"as\")))))) ;)",
+    		treeString(" \"as\" []{1,3} \"as\";")
+    		);
+    };
+
+    @Test
+    public void testEmptyTokenMax () {
+
+    	 	assertEquals( 
+    		"(request (query (sequence (segment (token (key (regex \"as\")))) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (range { (max 3) })))) (segment (token (key (regex \"as\")))))) ;)",
+    		treeString("\"as\" []{3} \"as\";")
+    		);
+    };
+
+    @Test
+    public void testSequenceDisj () {
+
+    	 	 assertEquals( 
+    		"(request (query (disjunction (sequence (segment (token (key (regex \"left\")))) (segment (token (key (regex \"to\")))) (segment (token (key (regex \"right\"))))) | (sequence (segment (token (key (regex \"right\")))) (segment (token (key (regex \"to\")))) (segment (token (key (regex \"left\"))))))) ;)",
+    		treeString("\"left\" \"to\" \"right\" | \"right\" \"to\" \"left\";")
+    		);
+    };
+
+
+
+    @Test
+    public void testSpanClassIDTarget () {
+
+    	 	assertEquals( 
+    		"(request (query (sequence (segment (token (key (regex \"in\")))) (segment (spanclass @ (token [ (term (layer pos) (termOp =) (key (regex \"DT\"))) ]))) (segment (token [ (term (layer lemma) (termOp =) (key (regex \"case\"))) ])))) ;)",
+    		treeString("\"in\" @[pos=\"DT\"] [lemma=\"case\"];")
+    		);
+    	 	assertEquals( 
+    	    		"(request (query (sequence (segment (spanclass @ (token [ (term (layer base) (termOp =) (key (regex 'Mann'))) ]))) (segment (spanclass @1 (token [ (term (layer orth) (termOp =) (key (regex 'Frau'))) ]))))))",
+    	    		treeString("@[base='Mann']@1[orth='Frau']")
+    	    		);
+    	 	
+    };
+
+
+    @Test
+    public void testSpanClassRange () {
+
+    	 	// the target (@) should be the rightmost JJ from the at least 2 repetitions!!
+    		assertEquals( 
+    		"(request (query (sequence (segment (token [ (term (layer pos) (termOp =) (key (regex \"DT\"))) ])) (segment (group ( (sequence (segment (spanclass @ (token [ (term (layer pos) (termOp =) (key (regex \"JJ.*\"))) ]))) (segment (token (key (regex \",\"))) (repetition (kleene ?)))) )) (repetition (range { (min 2) , }))) (segment (token [ (term (layer pos) (termOp =) (key (regex \"NNS\"))) ])))) ;)",
+    		treeString("[pos=\"DT\"] (@[pos=\"JJ.*\"] \",\"?){2,} [pos=\"NNS\"];")
+    		);
+    };
+
+
+    @Test
+    public void testSpanClassIDKeyword () {
+
+    	 	assertEquals( 
+    	 	"(request (query (sequence (segment (token (key (regex \"in\")))) (segment (spanclass @ (token [ (term (layer pos) (termOp =) (key (regex \"DT\"))) ]))) (segment (spanclass @1 (token [ (term (layer pos) (termOp =) (key (regex \"J.*\"))) ])) (repetition (kleene ?))) (segment (token [ (term (layer lemma) (termOp =) (key (regex \"case\"))) ])))) ;)",
+    		treeString("\"in\" @[pos=\"DT\"] @1[pos=\"J.*\"]? [lemma=\"case\"];")
+    		);
+    };
+    @Ignore
+    @Test
+    public void testSpanClassNested () {
+//this will fail because spanclass can only be used with tokens!!
+	 	assertEquals( 
+	 	"(request (query (segment (spanclass @ (segment (group ( (sequence (segment (token (key (regex \"el\")))) (segment (spanclass @1 (segment (token [ (term (layer pos) (termOp =) (key (regex \"A.*\"))) ]))))) )))))) ;)",
+		treeString("@(\"el\" @1[pos=\"A.*\"]);")
+		);
+};
+   
+
+    @Test
+    public void testEmptyTokenSequenceClass () {
+
+    	 	assertEquals( 
+    	 	"(request (query (sequence (segment (token (key (regex \"in\")))) (segment (spanclass @ (segment (emptyTokenSequence (emptyToken [ ]))))) (segment (spanclass @1 (token [ (term (layer pos) (termOp =) (key (regex \"J.*\"))) ])) (repetition (kleene ?))) (segment (token [ (term (layer lemma) (termOp =) (key (regex \"case\"))) ])))) ;)",
+    		treeString("\"in\" @[] @1[pos=\"J.*\"]? [lemma=\"case\"];")
+    		);
+    };
+
+
+    @Test
+    public void testSpanClassTermLabel () {
+
+    	 	assertEquals( 
+    	 	"(request (query (segment (spanclass (label adj) : (segment (token [ (term (layer pos) (termOp =) (key (regex \"JJ.*\"))) ]))))) ;)",
+    		treeString("adj: [pos=\"JJ.*\"];")
+    		);
+    };
+
+    @Test
+    public void testEmptySequenceLabel1 () {
+
+    	 	assertEquals( 
+    		"(request (query (sequence (segment (token [ (term (layer pos) (termOp =) (key (regex \"DT\"))) ])) (segment (spanclass (label a) : (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene ?)))))) (segment (token [ (term (layer pos) (termOp =) (key (regex \"NNS?\"))) ])))) ;)",
+    		treeString("[pos=\"DT\"] a:[]? [pos=\"NNS?\"];")
+    		);
+    };
+
+    @Test
+    public void testEmptySequenceLabel2 () {
+
+    	 	assertEquals( 
+    	 	"(request (query (segment (spanclass (label a) : (sequence (segment (emptyTokenSequence (emptyToken [ ]))) (segment (token (key (regex \"and\")))) (segment (spanclass (label b) : (segment (emptyTokenSequence (emptyToken [ ]))))))))) ;)",
+    		treeString("a:[] \"and\" b:[];")
+    		);
+    };
+
+
+    @Test
+    public void testEmptySequenceLabel3 () {
+
+    	 	assertEquals( 
+    		"(request (query (segment (spanclass (label a) : (sequence (segment (token [ (term (layer pos) (termOp =) (key (regex \"PP\"))) ])) (segment (spanclass (label c) : (segment (emptyTokenSequence (emptyToken [ ]) (repetition (range { (min 0) , (max 5) })))))) (segment (spanclass (label b) : (segment (token [ (term (layer pos) (termOp =) (key (regex \"VB.*\"))) ])))))))) ;)",
+    		treeString("a:[pos=\"PP\"] c:[]{0,5} b:[pos = \"VB.*\"];")
+    		);
+    };
+
+    @Test
+    public void testRegex$37 () {
+
+    	 	assertEquals( 
+    	 	"(request (query (segment (token [ (term (layer _.pos) (termOp =) (key (regex \"NPS\"))) ]))) ;)",
+    		treeString(" [_.pos = \"NPS\"];")
+    		);
+    };
+
+    
+
+/////////////////// **** STRUCT TESTS****	
+    		
+    @Test
+    public void testRegion () {
+
+    	assertEquals( 
+        		"(request (query (sequence (segment (token [ (term (layer base) (termOp =) (key (regex \"Mann\"))) ])) (segment (region / region [ (span < (foundry cnx) / (layer c) (termOp =) (skey vp) >) ])))))",
+        		treeString("[base=\"Mann\"] /region[<cnx/c=vp>]")); 	
+    	assertEquals( 
+    		"(request (query (sequence (segment (token [ (term (layer base) (termOp =) (key (regex \"Mann\"))) ])) (segment (region / region [ (span (skey vp)) ])))))",
+    		treeString("[base=\"Mann\"] /region[vp]"));
+			assertEquals( 
+        		"(request (query (segment (region / region [ (span < (foundry cnx) / (layer c) (termOp ! =) (skey vp) ( (termGroup (term (layer class) (termOp ! =) (key (regex \"header\"))) (boolOp &) (term (layer id) (termOp =) (key (regex \"7\")))) ) >) ]))))",
+        		treeString("/region[<cnx/c!=vp (class!=\"header\" & id=\"7\")>]")); 
+
+    	
+    	
+    };
+
+
+    @Ignore
+	@Test
+    public void testStructDisj () {
+
+    	 	//it should find any np, regardles of embedding level; not working
+    		assertEquals( 
+    	 	 "(request (query (sequence (segment (group ( (disjunction (segment (span < np >)) | (segment (span < np1 >)) | (segment (span < np2 >))) ))) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (segment (group ( (disjunction (segment (span < / np2 >)) | (segment (span < / np1 >)) | (segment (span < / np >))) ))))) ;)",
+    		treeString(" (<np>|<np1>|<np2>) []* (</np2>|</np1>|</np>);")
+    		);
+    };
+
+
+
+@Test
+public void testStructContains3 () {
+	// embedded qstruct;
+		 assertEquals( 
+		"(request (query (qstruct (matches (span < (skey s) >) (sequence (segment (qstruct (matches (span < (skey np) >) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (closingspan < / (skey np) >)))) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (segment (qstruct (matches (span < (skey np) >) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (closingspan < / (skey np) >))))) (closingspan < / (skey s) >)))) ;)",
+		treeString(" <s><np>[]*</np> []* <np>[]*</np></s>; #sentence that starts and ends with a noun phrase (NP);")
+		);
+}
+		
+
+@Test
+public void testmatchingspans () {
+	// embedded qstruct;
+		 
+	// the treeString is returned, but a FailedPredicateException error is also signaled;
+	     assertEquals("(request (query (qstruct (matches (span < (skey s) >) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (closingspan < / (skey z) >)))) ;)", 
+		 			treeString("<s>[]*</z>;"));
+		 assertEquals("rule qstruct failed predicate: {text1.equals(text2)}?", errorMessage("<s>[]*</z>;"));
+};
+
+
+@Test
+public void testStructtriple () {
+	// embedded qstruct;
+		 
+	
+	assertEquals( 
+		"(request (query (qstruct (matches (span < (skey s) >) (sequence (segment (qstruct (matches (span < (skey np) >) (sequence (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (segment (qstruct (matches (span < (skey np) >) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (closingspan < / (skey np) >))))) (closingspan < / (skey np) >)))) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (segment (qstruct (matches (span < (skey np) >) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (closingspan < / (skey np) >))))) (closingspan < / (skey s) >)))) ;)",
+		treeString("<s><np>[]*<np>[]*</np></np> []* <np>[]*</np></s>; #sentence that starts and ends with a noun phrase (NP);")
+		);
+};
+
+//@Ignore
+@Test
+public void teststructstartswithsqstruct () {
+	
+		 assertEquals( 
+		"(request (query (qstruct (matches (span < (skey s) >) (sequence (segment (qstruct (matches (span < (skey np) >) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (closingspan < / (skey np) >)))) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (segment (span < (skey np1) >)) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *))))) (closingspan < / (skey np1) >)))) ;)",
+		treeString(" <s><np>[]*</np> []* <np1>[]*</np1>;")
+		);
+		assertEquals("rule qstruct failed predicate: {text1.equals(text2)}?", errorMessage("<s>[]*</z>;"));
+};
+
+//@Ignore
+@Test
+public void teststructendswithqstruct () {
+	
+		assertEquals( "(request (query (qstruct (matches (span < (skey np) >) (sequence (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (segment (closingspan < / (skey np) >)) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (segment (qstruct (matches (span < (skey np) >) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (closingspan < / (skey np) >))))) (closingspan < / (skey s) >)))) ;)",
+		treeString("<np>[]*</np> []* <np>[]*</np></s>;")
+		);
+		assertEquals("rule qstruct failed predicate: {text1.equals(text2)}?", errorMessage("<s>[]*</z>;"));
+};
+@Test
+public void testsegmentafterstruct () {
+
+		 assertEquals("(request (query (qstruct (matches (span < (skey np) >) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (closingspan < / (skey np) >))) (segment (token (key (regex \"copil\"))))) ;)",
+		treeString("<np>[]*</np> \"copil\" ;")
+		);
+};
+
+@Test
+public void testsegmentbeforestruct () {
+	
+		 assertEquals( 
+		"(request (query (segment (token (key (regex \"copil\")))) (qstruct (matches (span < (skey np) >) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (closingspan < / (skey np) >)))) ;)",
+		treeString("\"copil\" <np>[]*</np>  ;")
+		);
+};
+
+@Test
+public void testsequenceafterstruct () {
+	
+		 assertEquals( 
+		"(request (query (qstruct (matches (span < (skey np) >) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (closingspan < / (skey np) >))) (sequence (segment (token (key (regex \"copil\")))) (segment (token (key (regex \"cuminte\")))))) ;)",
+		treeString("<np>[]*</np> \"copil\" \"cuminte\" ;")
+		);
+};
+
+@Test
+public void testsequencebeforestruct () {
+	
+		 assertEquals( 
+		"(request (query (sequence (segment (token (key (regex \"copil\")))) (segment (token (key (regex \"cuminte\"))))) (qstruct (matches (span < (skey np) >) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (closingspan < / (skey np) >)))) ;)",
+		treeString("\"copil\" \"cuminte\"  <np>[]*</np> ;")
+		);
+};
+
+    @Test
+    public void testStructStartsWith () {
+
+    	 	assertEquals( 
+    		"(request (query (sstruct (startswith (span < (skey s) >) (segment (token [ (term (layer pos) (termOp =) (key (regex \"VBG\"))) ]))))) ;)",
+    		treeString(" <s> [pos=\"VBG\"];")
+    		);
+    };
+    
+    
+    @Test
+    public void testStructSegmStartsWith () {
+
+    	 	assertEquals( 
+    		"(request (query (sstruct (startswith (span < (skey s) >) (sequence (segment (token (key (regex \"copilul\")))) (segment (token [ (term (layer pos) (termOp =) (key (regex \"VBG\"))) ])))))) ;)",
+    		treeString(" <s> \"copilul\" [pos=\"VBG\"];")
+    		);
+    };
+	@Test
+    public void testStructsSeqafterClosingSpan () {
+
+    	 	assertEquals( 
+    		"(request (query (sequence (segment (closingspan < / (skey s) >)) (segment (token (key (regex \"copilul\")))))) ;)",
+    		treeString(" </s> \"copilul\";")
+    		);
+    };
+    
+    @Test
+    public void testStructSeqStartsWith () {
+
+    	 	assertEquals( 
+    		"(request (query (sstruct (startswith (span < (skey s) >) (sequence (segment (token (key (regex \"copilul\")))) (segment (token (key (regex \"cuminte\")))) (segment (token [ (term (layer pos) (termOp =) (key (regex \"VBG\"))) ])))))) ;)",
+    		treeString("<s> \"copilul\" \"cuminte\" [pos=\"VBG\"];")
+    		);
+    };
+    @Test
+    public void testStructSegmEndsWith () {
+
+    	 	assertEquals( 
+    		"(request (query (sstruct (endswith (sequence (segment (token [ (term (layer pos) (termOp =) (key (regex \"VBG\"))) ])) (segment (token [ (term (layer pos) (termOp =) (key (regex \"SENT\"))) ]) (repetition (kleene ?)))) (closingspan < / (foundry base) / (layer s) (termOp =) (skey s) >)))) ;)",
+    		treeString("[pos = \"VBG\"] [pos = \"SENT\"]? </base/s=s>;"));
+
+    };
+    
+    
+ 
+    @Test
+    public void testStructContainsVsMatches1 () {
+
+    	 	assertEquals( 
+    		"(request (query (qstruct (matches (span < (skey np) >) (sequence (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (segment (group ( (sequence (segment (token [ (term (layer pos) (termOp =) (key (regex \"JJ.*\"))) ])) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *))))) )) (repetition (range { (min 3) , })))) (closingspan < / (skey np) >)))) ;)",
+    		treeString(" <np> []* ([pos=\"JJ.*\"] []*){3,} </np>;")
+    		);
+    };
+    
+	@Test
+    public void testStructContainsVsMatches1bis () {
+
+    	 	assertEquals( 
+    		"(request (query (qstruct (matches (span < (skey s) >) (sequence (segment (token (key (regex \"Today\")))) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *))))) (closingspan < / (skey s) >)))) ;)",
+    		treeString(" <s> \"Today\" []* </s>;")
+    		);
+    };
+
+    @Test
+    public void ttestStructContainsVsMatches2 () {
+
+    	 	assertEquals( 
+    		"(request (query (qstruct (isaround (span < (skey np) >) (emptyTokenSequenceAround (emptyToken [ ]) +) (segment ( (segment (token [ (term (layer pos) (termOp =) (key (regex \"JJ.*\"))) ])) ) (repetition (range { (min 3) , }))) (emptyTokenSequenceAround (emptyToken [ ]) +) (closingspan < / (skey np) >)))) ;)",
+    		treeString(" <np> []+ ([pos=\"JJ.*\"]){3,} []+ </np>; #contains (NP, sequence)")
+    		);
+    };
+    
+    @Test
+    public void testStructContains1 () {
+// isAround ; equivalent with contains in PQ+
+	 	assertEquals( 
+			 "(request (query (qstruct (isaround (span < (skey np) >) (emptyTokenSequenceAround (emptyToken [ ]) +) (segment (token (key (regex \"copil\")))) (emptyTokenSequenceAround (emptyToken [ ]) +) (closingspan < / (skey np) >)))) ;)",
+		treeString(" <np> []+ \"copil\" []+ </np>; #contains (NP, copil)"));
+
+	//	 matches []* \"copil\" [] \"cuminte\" []*;
+		assertEquals( 
+			"(request (query (qstruct (matches (span < (foundry base) / (layer s) (termOp =) (skey s) >) (sequence (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (segment (token (key (regex \"copil\")))) (segment (emptyTokenSequence (emptyToken [ ]))) (segment (token (key (regex \"cuminte\")))) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *))))) (closingspan < / (foundry base) / (layer s) (termOp =) (skey s) >)))))",
+		treeString("<base/s=s> []* \"copil\" [] \"cuminte\" []* </base/s=s>")); 
+// matches []* \"copil\"  []*
+		assertEquals( 
+			"(request (query (qstruct (matches (span < (foundry base) / (layer s) (termOp =) (skey s) >) (sequence (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (segment (token (key (regex \"copil\")))) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *))))) (closingspan < / (foundry base) / (layer s) (termOp =) (skey s) >)))))",
+		treeString("<base/s=s> []* \"copil\" []* </base/s=s>")); 
+};
+
+    @Test
+    public void testStructMatchesWholeSent () {
+// matches
+    	 	assertEquals( 
+    		"(request (query (qstruct (matches (span < (foundry base) / (layer s) (termOp =) (skey s) >) (segment (token (key (regex \"copil\")))) (closingspan < / (foundry base) / (layer s) (termOp =) (skey s) >)))) ;)",
+    		treeString(" <base/s=s> \"copil\" </base/s=s>;")
+    		);
+    };
+    @Test
+    public void testStartsWith () {
+//startswith
+	 	assertEquals( 
+		"(request (query (sstruct (startswith (span < (foundry base) / (layer s) (termOp =) (skey s) >) (segment (token (key (regex \"copil\"))))))) ;)",
+		treeString(" <base/s=s> \"copil\" ;")
+		);
+    };
+    
+    @Test
+    public void testStructEndsWith () {
+//endswith
+	 	assertEquals( 
+		"(request (query (sstruct (endswith (segment (token (key (regex \"copil\")))) (closingspan < / (foundry base) / (layer s) (termOp =) (skey s) >)))) ;)",
+		treeString("\"copil\" </base/s=s>;")
+		);
+
+};
+
+@Test
+public void testStructSeqEndsWith () {
+//endswith
+	 assertEquals( 
+	"(request (query (sstruct (endswith (sequence (segment (token (key (regex \"copil\")))) (segment (token (key (regex \"cuminte\"))))) (closingspan < / (foundry base) / (layer s) (termOp =) (skey s) >)))) ;)",
+	treeString("\"copil\" \"cuminte\" </base/s=s>;")
+	);
+
+};
+
+
+
+@Test
+public void testPositionOpsLBoundTerm () {
+
+	assertEquals( 
+	"(request (query (segment (token [ (term ! ( (layer lemma) (termOp =) (key (regex \"copil\")) ) & (position lbound ( (span < (foundry base) / (layer s) (termOp =) (skey s) >) ))) ]))) ;)",
+	treeString("[!(lemma=\"copil\") & lbound(<base/s=s>) ];") //
+
+	);
+};
+
+@Test
+public void testPositionOpsRBoundSegment () {
+
+ 	assertEquals( 
+	"(request (query (segment (token [ (term (key (regex \"copil\")) & (position rbound ( (span < (foundry base) / (layer s) (termOp =) (skey s) >) ))) ]))) ;)",
+	treeString("[\"copil\" & rbound(<base/s=s>)];") 
+	);
+};
+
+@Test
+public void testPositionOpsRBoundSequence () {
+		
+ 	assertEquals( 
+	"(request (query (sequence (segment (token [ (term (layer word) (termOp =) (key (regex \"acest\"))) ])) (segment (token [ (term (key (regex \"copil\")) & (position rbound ( (span < (foundry base) / (layer s) (termOp =) (skey s) >) ))) ])))) ;)",
+	treeString("[word = \"acest\"][\"copil\" & rbound(<base/s=s>)] ;")
+	);
+};
+
+
+
+
+    @Test
+    public void testWithinNp () {
+
+    	 	assertEquals( 
+    		"(request (query (sequence (segment (token [ (term (layer pos) (termOp =) (key (regex \"NN\"))) ])) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (segment (token [ (term (layer pos) (termOp =) (key (regex \"NN\"))) ])))) (within within (span (skey np))) ;)",
+    		treeString(" [pos=\"NN\"] []* [pos=\"NN\"] within np;")
+    		);
+
+			assertEquals( 
+    		"(request (query (sequence (segment (token [ (term (layer pos) (termOp =) (key (regex \"NN\"))) ])) (segment (emptyTokenSequence (emptyToken [ ]) (repetition (kleene *)))) (segment (token [ (term (layer pos) (termOp =) (key (regex \"NN\"))) ])))) (within within (span < (skey np) >)) ;)",
+    		treeString(" [pos=\"NN\"] []* [pos=\"NN\"] within <np>;")
+    		);
+    };
+
+
+
+    @Test
+    public void testMU0 () {
+
+	 	assertEquals( 
+		"(request (query (segment (matching MU ( (meetunion (segment (spanclass meet (segment (token (key (regex \"in\")))))) (segment (token (key (regex \"due\")))) 1 1) )))) ;)",
+		treeString("MU(meet \"in\" \"due\" 1 1);")
+		);
+    };
+    @Test
+    public void testMU01 () {
+
+	 	assertEquals( 
+		"(request (query (segment (matching MU ( (meetunion (segment (spanclass meet (segment (token (key (regex \"in\")))))) (segment (token (key (regex \"due\")))) -1 1) )))) ;)",
+		treeString("MU(meet \"in\" \"due\" -1 1);")
+		);
+    };
+
+	@Test
+    public void testMU01rec () {
+
+	 	assertEquals( 
+		"(request (query (segment (matching MU ( (meetunion (segment (spanclass meet ( (meetunion (segment (spanclass meet (segment (token (key (regex \"in\")))))) (segment (token (key (regex \"due\")))) -1 1) ))) (segment (token (key (regex \"time\")))) (span (skey s))) )))) ;)",
+		treeString("MU(meet (meet \"in\" \"due\" -1 1) \"time\" s);")
+		);
+    };
+ 
+	@Test
+	public void testMU1 () {
+
+    	 	assertEquals( 
+    		"(request (query (segment (matching MU ( (meetunion (segment (spanclass meet (segment (token (key (regex \"in\")))))) ( (meetunion (segment (spanclass meet (segment (token (key (regex \"due\")))))) (segment (token (key (regex \"course\")))) 1 1) ) 1 1) )))) ;)",
+    		treeString(" MU(meet \"in\" (meet \"due\" \"course\" 1 1) 1 1);")
+    		);
+    	 	
+    	 
+    	 	assertEquals( 
+    	    		"(request (query (segment (matching MU ( (meetunion (segment (spanclass meet (segment (token (key (regex 'de')))))) ( (meetunion (segment (spanclass meet (segment (token (key (regex 'color')))))) (segment (token (key (regex 'piel')))) 2 2) ) -1 -1) )))))",
+    	    		treeString("MU(meet 'de' (meet 'color' 'piel' 2 2) -1 -1)")
+    	    		);
+    	 	assertEquals( 
+    	    		"(request (query (segment (matching MU ( (meetunion (segment (spanclass meet ( (meetunion (segment (spanclass meet (segment (token (key (regex 'color')))))) (segment (token (key (regex 'de')))) 1 1) ))) (segment (token (key (regex 'piel')))) 2 2) )))))",
+    	    		treeString("MU(meet (meet 'color' 'de' 1 1) 'piel' 2 2)")
+    	    		);
+    	 	;
+    };
+
+    @Test
+    public void testMU2 () {
+
+    	 	assertEquals( 
+    	 	"(request (query (segment (matching MU ( (meetunion (segment (spanclass meet (segment (token (key (regex \"in\")))))) ( (meetunion (segment (spanclass meet (segment (emptyTokenSequence (emptyToken [ ]))))) (segment (token (key (regex \"course\")))) 1 1) ) 1 1) )))) ;)",
+    		treeString(" MU(meet \"in\" (meet [] \"course\" 1 1) 1 1);")
+    		);
+    };
+
+    @Test
+    public void testMU3 () {
+
+    	 	assertEquals( 
+    		"(request (query (segment (matching MU ( (meetunion (segment (spanclass meet ( (meetunion (segment (spanclass meet (segment (token (key (regex \"course\")))))) (segment (token (key (regex \"due\")))) -1 -1) ))) (segment (token (key (regex \"in\")))) -2 -2) )))) ;)",
+    		treeString(" MU(meet (meet \"course\" \"due\" -1 -1) \"in\" -2 -2);")
+    		);
+    };
+
+    @Test
+    public void testMU4 () {
+
+    	 	assertEquals( 
+    		"(request (query (segment (matching MU ( (meetunion (segment (spanclass meet ( (meetunion (segment (spanclass meet (segment (token [ (term (layer pos) (termOp =) (key (regex \"NN.*\"))) ])))) (segment (token (key (regex \"virtue\")))) 2 2) ))) (segment (token (key (regex \"of\")))) 1 1) )))) ;)",
+    		treeString(" MU(meet (meet [pos=\"NN.*\"] \"virtue\" 2 2) \"of\" 1 1);")
+    		);
+    };
+
+    @Test
+    public void testMU5 () {
+
+    	 	assertEquals( 
+    		"(request (query (segment (matching MU ( (meetunion (segment (spanclass meet ( (meetunion (segment (spanclass meet (segment (token (key (regex \"one\")))))) (segment (token (key (regex \"hand\")))) 1 1) ))) ( (meetunion (segment (spanclass meet (segment (token (key (regex \"other\")))))) (segment (token (key (regex \"hand\")))) 1 1) ) (span (skey s))) )))) ;)",
+    		treeString(" MU(meet (meet \"one\" \"hand\" 1 1) (meet \"other\" \"hand\" 1 1) s);")
+    		);
+    };
+
+
+    @Test
+    public void testMUinS () {
+
+    	 	assertEquals( 
+    		"(request (query (segment (matching MU ( (meetunion (segment (spanclass meet (segment (token (key (regex \"tea\")) (flag %c))))) (segment (token (key (regex \"cakes\")) (flag %c))) (span (skey s))) )))) ;)",
+    		treeString(" MU(meet \"tea\"%c \"cakes\"%c s);")
+    		);
+    };
+
+	@Test
+    public void testMUInSrec () {
+		assertEquals( "(request (query (segment (matching MU ( (meetunion (segment (spanclass meet ( (meetunion (segment (spanclass meet (segment (token (key (regex \"piel\")))))) (segment (token (key (regex \"azul\")))) (span (skey np))) ))) (segment (token (key (regex \"de\")))) (span (skey s))) )))) ;)", 
+		treeString("MU(meet (meet \"piel\" \"azul\" np)  \"de\" s);"));
+	}
+		
+	@Test
+    public void testMUInSrec1 () {
+		assertEquals( "(request (query (segment (matching MU ( (meetunion (segment (spanclass meet ( (meetunion (segment (spanclass meet (segment (token (key (regex \"piel\")))))) (segment (token (key (regex \"azul\")))) (span (skey np))) ))) ( (meetunion (segment (spanclass meet (segment (token (key (regex \"de\")))))) (segment (token (key (regex \"color\")))) (span (skey pp))) ) (span (skey s))) )))) ;)", 
+		treeString("MU(meet (meet \"piel\" \"azul\" np)  (meet \"de\" \"color\" pp) s);"));
+	}
+
+
+
+
+    private String treeString (String query) {
+        try {
+            Method startRule = CQPParser.class.getMethod("request");
+            ANTLRInputStream input = new ANTLRInputStream(query);
+            Antlr4DescriptiveErrorListener errorListener = new Antlr4DescriptiveErrorListener(
+                query);
+			lexer.setInputStream(input);
+			lexer.removeErrorListeners();
+			lexer.addErrorListener(errorListener);
+            CQPParser parser = new CQPParser(new CommonTokenStream(lexer));
+			parser.removeErrorListeners();
+            parser.addErrorListener(errorListener);
+            // Get starting rule from parser
+            tree = (ParserRuleContext) startRule.invoke(parser, (Object[]) null);
+				return Trees.toStringTree(tree, parser);
+			
+			
+        }
+        catch (Exception e) {
+            System.err.println(e);
+        };
+        return "";
+    }
+
+	 private String  errorMessage(String query) {
+        try {
+            Method startRule = CQPParser.class.getMethod("request");
+            ANTLRInputStream input = new ANTLRInputStream(query);
+            Antlr4DescriptiveErrorListener errorListener = new Antlr4DescriptiveErrorListener(
+                query);
+			lexer.setInputStream(input);
+			lexer.removeErrorListeners();
+			lexer.addErrorListener(errorListener);
+            CQPParser parser = new CQPParser(new CommonTokenStream(lexer));
+			parser.removeErrorListeners();
+            parser.addErrorListener(errorListener);
+            // Get starting rule from parser
+            tree = (ParserRuleContext) startRule.invoke(parser, (Object[]) null);
+            if(errorListener.getMessage().contains("text1"))
+			{return errorListener.getMessage();}
+			
+        }
+        catch (Exception e) {
+            System.err.println(e);
+        };
+        return "";
+    }
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/test/poliqarpplus/PQGrammarTest.java b/src/test/java/de/ids_mannheim/korap/query/test/poliqarpplus/PQGrammarTest.java
new file mode 100644
index 0000000..4bb2f75
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/test/poliqarpplus/PQGrammarTest.java
@@ -0,0 +1,103 @@
+package de.ids_mannheim.korap.query.test.poliqarpplus;
+import static org.junit.Assert.*;
+import org.junit.Test;
+import org.junit.Ignore;
+
+import de.ids_mannheim.korap.query.parse.poliqarpplus.PoliqarpPlusLexer;
+import de.ids_mannheim.korap.query.parse.poliqarpplus.PoliqarpPlusParser;
+
+
+import org.antlr.v4.runtime.*;
+import org.antlr.v4.runtime.tree.*;
+import java.lang.reflect.*;
+//import java.lang.*;
+
+/**
+ * Tests for PQ+ grammar parsing.
+ */
+
+public class PQGrammarTest {
+    String query;
+    Lexer lexer = new PoliqarpPlusLexer((CharStream) null);
+    ParserRuleContext tree = null;
+    @Test
+    public void squoutes_verbatim () {
+
+    	 	 assertEquals(
+    	 	 "(request (query (segment (token (key (verbatim ' copil '))))) <EOF>)", 
+    	 	 treeString("'copil'")
+    	 	 );
+
+              
+            assertEquals(
+    	 	 "(request (query ' ' ') <EOF>)",   // not parsing the query
+    	 	 treeString("'''")
+    	 	 );
+                assertEquals(
+    	 	 "(request (query ' ' -key ') <EOF>)",  // not parsing the query
+    	 	 treeString("''-key'")
+    	 	 );
+
+
+            assertEquals(
+                "(request (query (segment (token (key (verbatim ' \\' '))))) <EOF>)",  
+                treeString("'\\''")
+                );
+    }
+    
+    @Test
+    public void layerWithFlag () {
+        assertEquals(
+                "(request (query (segment (token [ (term (foundry mate) (flag /x) (termOp =) (key Baum) (flag /i)) ]))) <EOF>)",  
+                treeString("[mate/x=Baum/i]")
+                );
+    };
+    
+    @Test
+    public void dquoutes () {
+
+
+        
+      assertEquals(
+        "(request (query (segment (token (key (regex \"\"))))) <EOF>)",  // see different behaviour of " and '; for ", the query is parsed and an empty regex is generated
+        treeString("\"\"\"")
+        );
+
+        assertEquals(
+        "(request (query (sequence (segment (token (key (regex \"\")))) (segment (token (key -key))))) <EOF>)",  // see different behaviour of " and '; for ", the query is parsed and an empty regex is generated
+        treeString("\"\"-key\"")
+        );
+        assertEquals(
+            "(request query <EOF>)",  // see different behaviour of " and '; for ", the query is parsed and an empty regex is generated
+            treeString("\"?\"")
+            );
+      assertEquals(
+          "(request (query (segment (token (key (verbatim ' \\' '))))) <EOF>)",  // not parsing the query
+          treeString("'\\''")
+          );
+}
+@Test
+    public void spantest () {
+        assertEquals(
+            "(request (query < cnx / c ! = vp ! ! >) <EOF>)",  // not parsing the query
+            treeString("<cnx/c!=vp!!>")
+            );
+    
+    }
+    private String treeString (String query) {
+        try {
+            Method startRule = PoliqarpPlusParser.class.getMethod("request");
+            ANTLRInputStream input = new ANTLRInputStream(query);
+            lexer.setInputStream(input);
+            PoliqarpPlusParser parser = new PoliqarpPlusParser(new CommonTokenStream(lexer));
+
+            // Get starting rule from parser
+            tree = (ParserRuleContext) startRule.invoke(parser, (Object[]) null);
+            return Trees.toStringTree(tree, parser);
+        }
+        catch (Exception e) {
+            System.err.println(e);
+        };
+        return "";
+    }
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/test/poliqarpplus/PoliqarpPlusQueryProcessorTest.java b/src/test/java/de/ids_mannheim/korap/query/test/poliqarpplus/PoliqarpPlusQueryProcessorTest.java
index c13c7f4..cb69cb0 100644
--- a/src/test/java/de/ids_mannheim/korap/query/test/poliqarpplus/PoliqarpPlusQueryProcessorTest.java
+++ b/src/test/java/de/ids_mannheim/korap/query/test/poliqarpplus/PoliqarpPlusQueryProcessorTest.java
@@ -187,12 +187,32 @@
         assertEquals("mate", res.at("/query/wrap/foundry").asText());
         assertEquals("match:eq", res.at("/query/wrap/match").asText());
 
-		query = "[mate/b='D\\'Ma \\\\nn']";
+        query = "[mate/b=\"Der + Mann\"]";
         qs.setQuery(query, "poliqarpplus");
 		assertFalse(qs.hasErrors());
 		res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", res.at("/query/@type").asText());
+        assertEquals("koral:term", res.at("/query/wrap/@type").asText());
+        assertEquals("Der + Mann", res.at("/query/wrap/key").asText());
+        assertEquals("b", res.at("/query/wrap/layer").asText());
+        assertEquals("mate", res.at("/query/wrap/foundry").asText());
+        assertEquals("match:eq", res.at("/query/wrap/match").asText());
+
+
+      // ' and " in the same verbatim query
+        query = "[mate/b='D\\'Ma \\\\nn - \"yeah!\" - works']";
+        qs.setQuery(query, "poliqarpplus");
+	assertFalse(qs.hasErrors());
+	res = mapper.readTree(qs.toJSON());
+        assertEquals("D'Ma \\nn - \"yeah!\" - works", res.at("/query/wrap/key").asText());
+
+        
+        query = "[mate/b='D\\'Ma \\\\nn']";  
+        qs.setQuery(query, "poliqarpplus");
+	assertFalse(qs.hasErrors());
+	res = mapper.readTree(qs.toJSON());
         assertEquals("D'Ma \\nn", res.at("/query/wrap/key").asText());
-	}
+}
 
 
     // todo:
@@ -277,13 +297,52 @@
         query = "\"?\"";
         qs.setQuery(query, "poliqarpplus");
         res = mapper.readTree(qs.toJSON());
-        System.err.println(qs.toJSON());
+        // System.err.println(qs.toJSON());
         assertEquals(302, res.at("/errors/0/0").asInt());
     }
 
+    public void testRegexDQuoute () throws JsonProcessingException, IOException {
+
+        // tests for issue https://github.com/KorAP/Koral/issues/110
+        //this query is not parsed vs. the following 2 queries are. why?
+        query = "\"\"a.+?\"";
+        qs.setQuery(query, "poliqarpplus");
+        assertTrue(qs.hasErrors());
+    
+        query = "\"\"a\"";
+        qs.setQuery(query, "poliqarpplus");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:group", res.at("/query/@type").asText());
+        assertEquals("koral:token", res.at("/query/operands/0/@type").asText());
+        assertEquals("type:regex", res.at("/query/operands/0/wrap/type").asText());
+        assertEquals("orth", res.at("/query/operands/0/wrap/layer").asText());
+        assertEquals("match:eq", res.at("/query/operands/0/wrap/match").asText());
+        assertEquals("", res.at("/query/operands/0/wrap/key").asText());
+    
+        assertEquals("koral:token", res.at("/query/operands/1/@type").asText());
+        assertNotEquals("type:regex", res.at("/query/operands/1/wrap/type").asText());
+        assertEquals("orth", res.at("/query/operands/1/wrap/layer").asText());
+        assertEquals("match:eq", res.at("/query/operands/1/wrap/match").asText());
+        assertEquals("a", res.at("/query/operands/1/wrap/key").asText());
+    
+     
+        query = "\"\"\"";
+        qs.setQuery(query, "poliqarpplus");
+        res = mapper.readTree(qs.toJSON());
+        assertEquals("koral:token", res.at("/query/@type").asText());
+        assertEquals("koral:term", res.at("/query/wrap/@type").asText());
+        assertEquals("type:regex", res.at("/query/wrap/type").asText());
+        assertEquals("orth", res.at("/query/wrap/layer").asText());
+        assertEquals("match:eq", res.at("/query/wrap/match").asText());
+        assertEquals("", res.at("/query/wrap/key").asText());
+    }
+    
+    
     @Test
     public void testRegexEscape () throws JsonProcessingException, IOException {
         // Escape regex symbols
+        
+       
         query = "\"a.+?\"";
         qs.setQuery(query, "poliqarpplus");
         res = mapper.readTree(qs.toJSON());
@@ -468,6 +527,13 @@
         assertEquals("c", res.at("/query/wrap/layer").asText());
         assertEquals("match:ne", res.at("/query/wrap/match").asText());
 
+
+        query = "<cnx/c!=vp!!>";
+        qs.setQuery(query, "poliqarpplus");
+        res = mapper.readTree(qs.toJSON());
+        assertNotEquals("koral:span", res.at("/query/@type").asText());
+
+
         query = "<cnx/c!=vp class!=header>";
         qs.setQuery(query, "poliqarpplus");
         res = mapper.readTree(qs.toJSON());
@@ -492,7 +558,7 @@
         assertEquals("header", res.at("/query/attr/value").asText());
         assertEquals("match:eq", res.at("/query/attr/match").asText());
 
-        query = "<cnx/c!=vp !(class=header & id=7)>";
+        query = "<cnx/c!=vp !(class=header & id=7)>"; //de Morgan's Laws
         qs.setQuery(query, "poliqarpplus");
         res = mapper.readTree(qs.toJSON());
         assertEquals("koral:span", res.at("/query/@type").asText());
@@ -501,7 +567,7 @@
         assertEquals("c", res.at("/query/wrap/layer").asText());
         assertEquals("match:ne", res.at("/query/wrap/match").asText());
         assertEquals("koral:termGroup", res.at("/query/attr/@type").asText());
-        assertEquals("relation:and", res.at("/query/attr/relation").asText());
+        assertEquals("relation:or", res.at("/query/attr/relation").asText());
         operands = Lists
                 .newArrayList(res.at("/query/attr/operands").elements());
         assertEquals("koral:term", operands.get(0).at("/@type").asText());
@@ -602,6 +668,23 @@
 
     @Test
     public void testPositions () throws JsonProcessingException, IOException {
+        
+        
+        query= "contains(<np>, ([pos=\"JJ.*\"]){3,})";
+        qs.setQuery(query, "poliqarpplus");
+        res = mapper.readTree(qs.toJSON());
+       
+        assertEquals("koral:group", res.at("/query/@type").asText());
+        assertEquals("operation:position", res.at("/query/operation").asText());
+        assertEquals("frames:isAround", res.at("/query/frames/0").asText());
+        assertEquals("koral:span", res.at("/query/operands/0/@type").asText());
+        assertEquals("np", res.at("/query/operands/0/wrap/key").asText());
+        assertEquals("koral:group", res.at("/query/operands/1/@type").asText());
+        assertEquals("operation:repetition", res.at("/query/operands/1/operation").asText());
+        assertEquals("JJ.*", res.at("/query/operands/1/operands/0/wrap/key").asText());
+        assertEquals(3, res.at("/query/operands/1/boundary/min").asInt());
+    
+        
         query = "contains(<s>, der)";
         qs.setQuery(query, "poliqarpplus");
         res = mapper.readTree(qs.toJSON());
@@ -1591,7 +1674,7 @@
         assertEquals("operation:position", res.at("/query/operation").asText());
         assertEquals("frames:isAround", res.at("/query/frames/0").asText());
         assertEquals("s", res.at("/query/operands/0/wrap/key").asText());
-        assertEquals("s", res.at("/query/operands/0/wrap/layer").asText());
+//        assertEquals("s", res.at("/query/operands/0/wrap/layer").asText());
         assertEquals("VVFIN", res.at("/query/operands/1/wrap/key").asText());
     }
     
@@ -1692,6 +1775,6 @@
         assertEquals("author", res.at("/collection/operands/1/key").asText());
         assertEquals("Smith", res.at("/collection/operands/1/value").asText());
 
-        // TODO more tests
+      
     }
 }