Merge branch 'master' into opPROX

Change-Id: I2fd1b42edeab6339853f649ee6a0616d3e31bc72
diff --git a/src/main/antlr/cosmas/c2ps.g b/src/main/antlr/cosmas/c2ps.g
index 2a63bb7..dd750f3 100644
--- a/src/main/antlr/cosmas/c2ps.g
+++ b/src/main/antlr/cosmas/c2ps.g
@@ -74,10 +74,25 @@
 
 fragment DISTVALUE
 	:	 ('0' .. '9')+ (':' ('0'..'9')+)? ;
-		
+
+fragment DISTTYPE // 30.11.23/FB
+	: 	('w'|'s'|'p'|'t');
+
+fragment DISTDIR // 30.11.23/FB
+	: 	('+'|'-');
+	
+/* old version (before 30.11.23/FB)
 fragment DIST
 	:	('+'|'-')? (DISTVALUE ('w'|'s'|'p'|'t') | ('w'|'s'|'p'|'t') DISTVALUE);
-	
+*/
+
+// accept these 3 options in any order.
+// afterwards, we will have to check if any of them is missing.
+// 30.11.23/FB
+
+fragment DIST // 30.11.23/FB
+	:	(DISTDIR | DISTTYPE | DISTVALUE )+;
+
 fragment GROUP
 	:	('min' | 'max');
 
@@ -260,7 +275,7 @@
 op2	:	(opPROX | opIN | opOV | opAND | opOR | opNOT) ;
 		
 // AST with Options for opPROX is returned by c2ps_opPROX.check():
-opPROX	:	OP_PROX -> ^(OPPROX {c2ps_opPROX.check($OP_PROX.text, $OP_PROX.index)} );
+opPROX	:	OP_PROX -> ^(OPPROX {c2ps_opPROX.check($OP_PROX.text, $OP_PROX.pos)} );
 
 opIN	: 	OP_IN -> {c2ps_opIN.check($OP_IN.text, $OP_IN.index)};
 
diff --git a/src/main/antlr/cosmas/c2ps_opPROX.g b/src/main/antlr/cosmas/c2ps_opPROX.g
index f7a42f5..ab0f4cb 100644
--- a/src/main/antlr/cosmas/c2ps_opPROX.g
+++ b/src/main/antlr/cosmas/c2ps_opPROX.g
@@ -1,9 +1,10 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-//												//
-// 	lokale Grammatik der COSMAS II zeilenorientierten Suchanfragesprache (= c2ps)		//
-//	für den Abstandsoperator /w... und %w...						//
-//	v-1.0 - 07.12.12/FB									//
-//												//
+//												
+// 	lokale Grammatik der COSMAS II zeilenorientierten Suchanfragesprache (= c2ps)		
+//	für den Abstandsoperator /w... und %w...	
+//	v-1.0 - 07.12.12/FB							
+//  v-1.1 - 30.11.23/FB opPROX accepts any order of direction, measure and value.
+//												
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
 grammar c2ps_opPROX;
@@ -16,9 +17,46 @@
 	  MEAS; // measure
 	  DIR; PLUS; MINUS; BOTH;
 	  GRP; MIN; MAX; }
-@header {package de.ids_mannheim.korap.query.parse.cosmas;}
+	  
+@header {package de.ids_mannheim.korap.query.parse.cosmas;
+		 import  de.ids_mannheim.korap.util.C2RecognitionException;}
+		 
 @lexer::header {package de.ids_mannheim.korap.query.parse.cosmas;}
 
+@members {
+    public void displayRecognitionError(String[] tokenNames,
+                                        RecognitionException e) {
+        String hdr = getErrorHeader(e);
+        String msg = getErrorMessage(e, tokenNames);
+        System.err.println("Debug: displayRecognitionError: hdr = " + hdr + ".");
+        System.err.println("Debug: displayRecognitionError: msg='" + msg + "'.");
+        System.err.println("Debug: displayRecognitionError: e = " + e.toString() + ".");
+        
+        if( e instanceof C2RecognitionException )
+        	{
+        	C2RecognitionException c2e = (C2RecognitionException) e;
+        	String c2msg = hdr + ": PROX options mismatch at '" + c2e.getMismatchedToken() + "'...";
+        	
+        	emitErrorMessage(c2msg);
+        	}
+        else
+        	emitErrorMessage(hdr + " prox options mismatch...");
+       
+        // Now do something with hdr and msg...
+    }
+}
+
+@rulecatch {
+  catch (C2RecognitionException c2e) {
+    //Custom handling of an exception. Any java code is allowed.
+    System.err.printf("Debug: overall rulecatch for c2ps_opPROX: c2RecognitionException.\n");
+    //reportError(c2e);
+    //recover(c2e.input, (RecognitionException) c2e);
+    //throw (RecognitionException)c2e;
+    //System.err.printf("Debug: overall rulecatch: back from reportError(c2e).\n");
+  }
+} // rulecatch
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 //
 // 						PROX-Lexer
@@ -35,36 +73,51 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
 
-opPROX	:	proxTyp proxDist (',' proxDist)* (',' proxGroup)? 
+opPROX[int pos]	:	proxTyp proxDist[$pos] (',' proxDist[$pos])* (',' proxGroup)? 
 		
 		-> ^(PROX_OPTS {$proxTyp.tree} ^(DIST_LIST proxDist+) {$proxGroup.tree});
 	
-proxTyp	:	  '/' -> ^(TYP PROX)	// klassischer Abstand.
-		| '%' -> ^(TYP EXCL);	// ausschließender Abstand.
+proxTyp	:  '/' -> ^(TYP PROX)	// klassischer Abstand.
+		|  '%' -> ^(TYP EXCL);	// ausschließender Abstand.
 
 // proxDist: e.g. +5w or -s0 or /w2:4 etc.
 // kein proxDirection? hier, weil der Default erst innerhalb von Regel proxDirection erzeugt werden kann.
+/* incomplete original version:
 proxDist:	proxDirection (v1=proxDistValue m1=proxMeasure | m2=proxMeasure v2=proxDistValue)
 
 		-> {$v1.tree != null}? ^(DIST {$proxDirection.tree} {$v1.tree} {$m1.tree})
-		-> 		       ^(DIST {$proxDirection.tree} {$v2.tree} {$m2.tree});
+		->		 		       ^(DIST {$proxDirection.tree} {$v2.tree} {$m2.tree});
+*/
+
+// new rule: accepts options in any order:
+// count each option type and find out if any one  is missing or occures multiple times.
+// 28.11.23/FB
+
+proxDist[int pos]
+@init{ int countM=0; int countD=0; int countV=0;}
+	:
+		((m=proxMeasure {countM++;})|(d=proxDirection {countD++;})|(v=proxDistValue {countV++;}) )+
+		 
+	->  {c2ps_opPROX.encodeDIST(DIST, DIR, $d.tree, $m.tree, $v.tree, $proxDist.text, countD, countM, countV, $pos)};
+	
+
+// new rule accepts only '+' and '-'; default tree for direction is 
+// set in c2ps_opPROX.encodeDIST() now.
+// 28.11.23/FB
 
 proxDirection
-	:	(p='+'|m='-')?	-> {$p != null}? ^(DIR PLUS)
-				-> {$m != null}? ^(DIR MINUS)
-				->               ^(DIR BOTH) ;
-/*
-proxDistValue	// proxDistMin ( ':' proxDistMax)? ;
-	:	(m1=proxDistMin -> ^(DIST_RANGE VAL0 $m1)) (':' m2=proxDistMax -> ^(DIST_RANGE $m1 $m2))? ;
-*/
-proxDistValue	// proxDistMin ( ':' proxDistMax)? ;
-	:	(m1=proxDistMin ) (':' m2=proxDistMax)? 
+		: '+'	-> ^(DIR PLUS)
+		| '-'	-> ^(DIR MINUS);
+
+proxDistValue	:	(m1=proxDistMin ) (':' m2=proxDistMax)? 
 	
 		-> {$m2.text != null}? ^(RANGE $m1  $m2)
-		->		       ^(RANGE VAL0 $m1);
-		
+		->				       ^(RANGE VAL0 $m1);
+
+// mentioning >1 measures will be checked/rejected in c2ps_opPROX.encodeDIST(). 
+
 proxMeasure
-	:	(m='w'|m='s'|m='p'|m='t') -> ^(MEAS $m);
+	:	(meas='w'|meas='s'|meas='p'|meas='t') -> ^(MEAS $meas) ;  
 
 proxDistMin
 	:	DISTVALUE;
@@ -75,4 +128,4 @@
 proxGroup
 	:	'min' -> ^(GRP MIN)
 	|	'max' -> ^(GRP MAX);
-	
+	
\ No newline at end of file
diff --git a/src/main/java/de/ids_mannheim/korap/query/parse/cosmas/c2ps_opPROX.java b/src/main/java/de/ids_mannheim/korap/query/parse/cosmas/c2ps_opPROX.java
index 2a5b163..82bc9b9 100644
--- a/src/main/java/de/ids_mannheim/korap/query/parse/cosmas/c2ps_opPROX.java
+++ b/src/main/java/de/ids_mannheim/korap/query/parse/cosmas/c2ps_opPROX.java
@@ -3,29 +3,167 @@
 import org.antlr.runtime.*;
 import org.antlr.runtime.tree.*;
 
+import de.ids_mannheim.korap.query.serialize.Antlr3AbstractQueryProcessor;
+import de.ids_mannheim.korap.query.serialize.util.Antlr3DescriptiveErrorListener;
+import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
+import de.ids_mannheim.korap.util.*;
+
 /*
  * parses Opts of PROX: /w3:4,s0,min or %w3:4,s0,min.
  */
 
-public class c2ps_opPROX
+public class c2ps_opPROX 
 
 {
+	final static int typeERROR = 1; // type of an Error CommonToken.
+	final static int ERR_MEAS_NULL = 300;
+	final static int ERR_MEAS_TOOGREAT = 301;
+	final static int ERR_VAL_NULL = 302;
+	final static int ERR_VAL_TOOGREAT = 303;
+	final static int ERR_DIR_TOOGREAT = 304;
+	
+	private static CommonTree buildErrorTree(String text, int errCode, int typeDIST, int pos)
+	
+	{
+	CommonTree
+		errorTree = new CommonTree(new CommonToken(typeDIST, "DIST")); 
+	CommonTree
+		errorNode = new CommonTree(new CommonToken(typeERROR, "ERROR"));
+	CommonTree
+		errorPos  = new CommonTree(new CommonToken(typeERROR, Integer.toString(errCode)));
+	CommonTree
+		errorArg  = new CommonTree(new CommonToken(1, text));
+	CommonTree
+		errorMes;
+	String
+		mess;
+	
+	switch( errCode )
+		{
+	case ERR_MEAS_NULL:
+		mess      = String.format("Abstandsoperator an der Stelle '%s' es fehlt eine der folgenden Angaben: w,s,p!", text);
+		errorMes  = new CommonTree(new CommonToken(typeERROR, mess));
+		break;
+	case ERR_MEAS_TOOGREAT:
+		mess      = String.format("Abstandsoperator an der Stelle '%s': Bitte nur 1 der folgenden Angaben einsetzen: w,s,p! " +
+							 "Falls Mehrfachangabe erwünscht, dann durch Kommata trennen (z.B.: /+w2,s0).", text);
+		errorMes  = new CommonTree(new CommonToken(typeERROR, mess));
+		break;
+	case ERR_VAL_NULL:
+		mess      = String.format("Abstandsoperator an der Stelle '%s': Bitte einen numerischen Wert einsetzen (z.B. /+w5)! ", text);
+		errorMes  = new CommonTree(new CommonToken(typeERROR, mess));
+		break;
+	case ERR_VAL_TOOGREAT:
+		mess      = String.format("Abstandsoperator an der Stelle '%s': Bitte nur 1 numerischen Wert einsetzen (z.B. /+w5)! ", text);
+		errorMes  = new CommonTree(new CommonToken(typeERROR, mess));
+		break;
+	case ERR_DIR_TOOGREAT:
+		mess      = String.format("Abstandsoperator an der Stelle '%s': Bitte nur 1 Angabe '+' oder '-' oder keine! ", text);
+		errorMes  = new CommonTree(new CommonToken(typeERROR, mess));
+		break;
+	default:
+		mess = String.format("Abstandsoperator an der Stelle '%s': unbekannter Fehler. Korrekte Syntax z.B.: /+w2 oder /w10,s0.", text);
 
-    public static Tree check (String input, int index) {
+		errorMes  = new CommonTree(new CommonToken(typeERROR, mess));
+		}
+	
+	errorTree.addChild(errorNode);
+	errorNode.addChild(errorPos);
+	errorNode.addChild(errorArg);
+	errorNode.addChild(errorMes);
+
+	// test - 09.01.24/FB
+	//addError("ab");
+	
+	return errorTree;
+	}
+
+	/* encodeDIST():
+	 * - returns a CommonTree built from the Direction/Measure/Distance value.
+	 * - accepts options in any order.
+	 * - creates CommonTree in that order: Direction .. Distance value .. Measure.
+	 * - sets default direction to BOTH if not set yet.
+	 * - unfortunatly, in ANTLR3 it seems that there is no way inside the Parser Grammar to get 
+	 *   the absolute token position from the beginning of the query. Something like $ProxDist.pos or
+	 *   $start.pos is not available, so we have no info in this function about the position at which
+	 *   an error occurs. 
+	 * - For multiple prox options, e.g. /w2,s2,p0, this function if called 3 times.
+	 * Arguments:
+	 * countD	: how many occurences of distance: + or - or nothing. If 0 insert the default BOTH.
+	 * countM	: how many occurences of measure: w,s,p,t: should be 1.
+	 * countV	: how many occurences of distance value: should be 1.
+	 * 28.11.23/FB
+	 */
+	
+	public static Object encodeDIST(int typeDIST, int typeDIR, Object ctDir, Object ctMeas, Object ctVal, String text,
+									int countD, int countM, int countV, int pos)  
+			
+	{
+		CommonTree tree1 = (CommonTree)ctDir;
+		CommonTree tree2 = (CommonTree)ctMeas;
+		CommonTree tree3 = (CommonTree)ctVal;
+		
+		System.err.printf("Debug: encodeDIST: scanned input='%s' countM=%d countD=%d countV=%d pos=%d.\n", 
+					text, countM, countD, countV, pos);
+
+		if( countM == 0 )
+			return buildErrorTree(text, ERR_MEAS_NULL, typeDIST, pos);
+		if( countM > 1 )
+			return buildErrorTree(text, ERR_MEAS_TOOGREAT, typeDIST, pos);
+		if( countV == 0 )
+			return buildErrorTree(text, ERR_VAL_NULL, typeDIST, pos);
+		if( countV > 1 )
+			return buildErrorTree(text, ERR_VAL_TOOGREAT, typeDIST, pos);
+		
+		if( countD == 0 )
+			{
+			// if direction is not specified (ctDir == null or countD==0), return default = BOTH:
+			CommonTree treeDIR  = new CommonTree(new CommonToken(typeDIR, (String)"DIR"));
+			CommonTree treeBOTH = new CommonTree(new CommonToken(typeDIR, "BOTH"));
+			treeDIR.addChild(treeBOTH);
+			
+			System.err.printf("Debug: encodeDIST: tree for DIR: '%s'.\n", 
+					treeDIR.toStringTree());
+			tree1 = treeDIR;
+			}
+		else if( countD > 1 )
+			return buildErrorTree(text, ERR_DIR_TOOGREAT, typeDIST, pos);
+		
+		// create DIST tree:
+		CommonTree 
+			tree = new CommonTree(new CommonToken(typeDIST, "DIST"));
+		
+		tree.addChild(tree1);
+		tree.addChild(tree3); // tree3 before tree2 expected by serialization.
+		tree.addChild(tree2);
+		
+		System.err.printf("Debug: encodeDIST: returning '%s'.\n", tree.toStringTree());
+		
+		return tree;
+	} // encodeDIST
+	
+	public static boolean checkDIST(String input)
+	
+	{
+		return true;
+	}
+	
+	public static Tree check (String input, int pos) 
+	{
         ANTLRStringStream ss = new ANTLRStringStream(input);
         c2ps_opPROXLexer lex = new c2ps_opPROXLexer(ss);
         CommonTokenStream tokens = new CommonTokenStream(lex);
         c2ps_opPROXParser g = new c2ps_opPROXParser(tokens);
         c2ps_opPROXParser.opPROX_return c2PQReturn = null;
 
-        /*
-        System.out.println("check opPROX:" + index + ": " + input);
+        /**/
+        System.out.printf("check opPROX: pos=%d input='%s'.\n", pos, input);
         System.out.flush();
-         */
+        /**/
 
         try {
-            c2PQReturn = g.opPROX();
-        }
+            c2PQReturn = g.opPROX(pos);
+        	}
         catch (RecognitionException e) {
             e.printStackTrace();
         }
@@ -37,7 +175,19 @@
         return tree;
     }
 
-
+	public static boolean checkFalse()
+	{
+	
+	return false; // testwise	
+	}
+	
+	public static boolean checkMeasure( Object measure)
+	{
+		System.err.printf("Debug: checkMeasure: measure = %s.\n",
+				measure == null ? "null" : "not null");
+		return true;
+	}
+	
     /*
      * main testprogram:
      */
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/Cosmas2QueryProcessor.java b/src/main/java/de/ids_mannheim/korap/query/serialize/Cosmas2QueryProcessor.java
index 68f5f7c..47eb907 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/Cosmas2QueryProcessor.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/Cosmas2QueryProcessor.java
@@ -18,12 +18,16 @@
 import de.ids_mannheim.korap.util.StringUtils;
 
 import org.antlr.runtime.ANTLRStringStream;
+import org.antlr.runtime.FailedPredicateException;
 import org.antlr.runtime.RecognitionException;
+import org.antlr.runtime.Token;
 import org.antlr.runtime.tree.Tree;
 import org.antlr.v4.runtime.tree.ParseTree;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.Table;
 
@@ -41,7 +45,7 @@
  */
 public class Cosmas2QueryProcessor extends Antlr3AbstractQueryProcessor {
 
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     private static Logger log =
             LoggerFactory.getLogger(Cosmas2QueryProcessor.class);
@@ -128,6 +132,56 @@
     public static Pattern wildcardPlusPattern = Pattern.compile("([+])");
     public static Pattern wildcardQuestionPattern = Pattern.compile("([?])");
 
+	/**
+	 * searchforError
+	 * - returns true if an error node is found in the tree referenced by 'node'.
+	 * - adds the corresponding error message to the error list.
+	 * @param node
+	 * @return: true: error node was found,
+	 * 			false; no error node found.
+	 * 19.12.23/FB
+	 */
+    
+    private boolean searchforError(Tree node)
+    
+    {
+    	//System.err.printf("Debug: searchforError: '%s' has %d children.\n",
+    	//		node.toStringTree(), node.getChildCount());
+    	
+    	if( node.getType() == 1 && node.getText().compareTo("ERROR") == 0 )
+	    	{
+	    	// error node found:
+    		//System.err.printf("Debug: searchforError: error node found: %s.\n", node.toStringTree());
+    		String
+    			message = String.format("Fehler %s gefunden bei '%s': %s.",
+    					node.getChild(0) != null ? node.getChild(0).getText() : "",
+    	    			node.getChild(1) != null ? node.getChild(1).getText() : "",
+    	    			node.getChild(2) != null ? node.getChild(2).getText() : "");
+    		
+    		addError(345, message);
+    		return true;
+	    	}
+    	
+    	for(int i=0; i<node.getChildCount(); i++)
+	    	{
+    		Tree
+    			son = node.getChild(i);
+    		
+    		/* System.err.printf(" node: text='%s' type=%d start=%d end=%d.\n",
+    				son.getText(), 
+    				son.getType(),
+    				son.getTokenStartIndex(),
+    				son.getTokenStopIndex());
+    		*/
+
+    		if( searchforError(son) )
+    			return true; // error found, stop here.
+	    	}
+    	
+    	// no error node:
+    	return false;
+    }
+
     /**
      * @param tree
      *            The syntax tree as returned by ANTLR
@@ -140,28 +194,40 @@
         KoralObjectGenerator.setQueryProcessor(this);
         this.query = query;
         process(query);
-        if (DEBUG) { 
-            log.debug(">>> " + requestMap.get("query") + " <<<");
-        System.out.printf("Cosmas2QueryProcessor: >>%s<<.\n",  requestMap.get("query"));
-        }
-    }
+        if (verbose) 
+        	{ 
+            //log.debug(">>> " + requestMap.get("query") + " <<<");
+            try {
+	        	// query from requestMap is unformatted JSON. Make it pretty before displaying:
+	        	ObjectMapper mapper = new ObjectMapper();
+	        	String jsonQuery = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(requestMap.get("query"));
+				System.out.printf("Cosmas2QueryProcessor: JSON output: %s\n\n", jsonQuery);
+				} 
+	        catch (JsonProcessingException e) 
+	        	{
+	        	System.out.printf("Cosmas2QueryProcessor: >>%s<<.\n",  requestMap.get("query"));
+	            //e.printStackTraObjectMapper mapper = new ObjectMapper();ce();
+				}
+        	}
+    	}
 
 
     @Override
     public void process (String query) {
         Tree tree = null;
         tree = parseCosmasQuery(query);
-        if (DEBUG) { 
+        if (DEBUG) 
+        	{ 
         	System.out.printf("\nProcessing COSMAS II query: %s.\n\n", query);
             log.debug("Processing CosmasII query: " + query);
-        }
+        	}
+        
         if (tree != null) 
         	{
-            
-                if (DEBUG) {
-            	log.debug("ANTLR parse tree: " + tree.toStringTree());
-                System.out.printf("\nANTLR parse tree: %s.\n\n",  tree.toStringTree());
-                }
+            if (verbose) {
+	        	log.debug("ANTLR parse tree: " + tree.toStringTree());
+	            System.out.printf("\nANTLR parse tree: %s.\n\n",  tree.toStringTree());
+	            }
 
             processNode(tree);
         	}
@@ -181,11 +247,13 @@
         stackedObjects = 0;
         stackedToWrap = 0;
 
-        if (verbose) {
+        /*
+         if (verbose) {
             System.err.println(" " + objectStack);
             System.out.println(openNodeCats);
         }
-
+        */
+        
         /* ***************************************
          * Processing individual node categories *
          * ***************************************
@@ -716,10 +784,19 @@
 
     @SuppressWarnings("unchecked")
     private void processOPPROX (Tree node) {
+    	
         // collect info
         Tree prox_opts = node.getChild(0);
         Tree typ = prox_opts.getChild(0);
         Tree dist_list = prox_opts.getChild(1);
+    	
+    	// search for an error and if there is one, add it to the list:
+  	    if( searchforError(prox_opts) )
+			{
+        	return;
+	    	}
+
+        
         // Step I: create group
         Map<String, Object> group =
                 KoralObjectGenerator.makeGroup(KoralOperation.SEQUENCE);
@@ -1746,9 +1823,6 @@
             org.antlr.runtime.CommonTokenStream tokens =
                     new org.antlr.runtime.CommonTokenStream(lex); // v3
             
-           // System.out.printf("parseCosmasQuery: tokens = %d\n",  tokens.size());
-           // System.out.printf("parseCosmasQuery: tokens = %s\n",  tokens.toString());
-           
             parser = new c2psParser(tokens);
            
             // Use custom error reporters
@@ -1762,26 +1836,44 @@
             tree = (Tree) c2Return.getTree();
             if (DEBUG) log.debug(tree.toStringTree());
             }
+        catch (FailedPredicateException fe)
+	        {
+        	System.out.printf("parseCosmasQuery: FailedPredicateException!\n");
+            addError(StatusCodes.MALFORMED_QUERY,
+                    "failed predicate on prox something.");
+	        }
         catch (RecognitionException e) {
-            log.error(
+            System.out.printf("parseCosmasQuery: RecognitionException!\n");
+        	log.error(
                     "Could not parse query. Please make sure it is well-formed.");
             addError(StatusCodes.MALFORMED_QUERY,
                     "Could not parse query. Please make sure it is well-formed.");
         }
-        String treestring = tree.toStringTree();
 
+        String treestring = tree.toStringTree();
+        System.err.printf("Debug: parseCosmasQuery: tree = '%s'.\n",  treestring);
+        
         boolean erroneous = false;
         if (parser.failed() || parser.getNumberOfSyntaxErrors() > 0) {
             erroneous = true;
             tree = null;
         }
 
+        System.err.printf("Debug: parseCosmasQuery: failed=%b no. of synt. err = %d.\n",
+        			parser.failed(), parser.getNumberOfSyntaxErrors());
+        
         if (erroneous || treestring.contains("<mismatched token")
                 || treestring.contains("<error")
-                || treestring.contains("<unexpected")) {
-            log.error(errorListener.generateFullErrorMsg().toString());
+                || treestring.contains("<unexpected")) 
+        {
+        	System.err.printf("Debug: parseCosmasQuery: tree: '%s'.\n", treestring);
+        	System.err.printf("Debug: parseCosmasQuery: FullErrorMsg:  '%s'.\n", errorListener.generateFullErrorMsg().toString());
+        	log.error(errorListener.generateFullErrorMsg().toString());
             addError(errorListener.generateFullErrorMsg());
         }
+        
+        addError(1234, "Test-Message");
+        
         return tree;
     }
 }
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 94bf15d..ce7802f 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
@@ -18,6 +18,7 @@
 import de.ids_mannheim.korap.query.serialize.util.KoralObjectGenerator;
 import de.ids_mannheim.korap.query.serialize.util.StatusCodes;
 
+import com.fasterxml.jackson.core.JsonPointer;
 
 /**
  * Main class for Koral, serializes queries from concrete QLs to KoralQuery
@@ -33,7 +34,7 @@
     private String version = "Unknown";
     private String name = "Unknown";
     private static Properties info;
-
+    private boolean bDebug = false;
         {
           
             loadInfo();
@@ -104,24 +105,30 @@
         int i = 0;
         String[] queries = null;
         String ql = "poliqarpplus";
-        boolean bDebug = true;
+        boolean bDebug = false;
 
         if (args.length < 2) {
-            System.err
-                    .println("Usage: QuerySerializer \"query\" queryLanguage");
+            System.err.println("\nUsage: QuerySerializer \"query\" queryLanguage [-show]");
             System.exit(1);
         }
         else {
             queries = new String[] { args[0] };
             ql = args[1];
         }
+        if( args.length >= 3 )
+	        {
+        	if( args[2].compareToIgnoreCase("-show") == 0 )
+	        	bDebug = true;	
+	        }
+        
         for (String q : queries) {
             i++;
             try {
-		if( bDebug ) System.out.printf("QuerySerialize: query = >>%s<< lang = %s.\n", q, ql);
-            	
-		jg.run(q, ql);
-                System.out.println();
+				if( bDebug ) 
+					System.out.printf("QuerySerialize: query = >>%s<< lang = %s.\n", q, ql);
+		            	
+				jg.run(q, ql, bDebug);
+		        	System.out.println();
             }
             catch (NullPointerException npe) {
                 npe.printStackTrace();
@@ -145,9 +152,9 @@
      *            'poliqarpplus', 'cqp', 'cosmas2', 'annis' or 'cql'.
      * @throws IOException
      */
-    public void run (String query, String queryLanguage) throws IOException {
+    public void run (String query, String queryLanguage, boolean bDebug) throws IOException {
 
-	ast.verbose = DEBUG ? true : false; // debugging: 01.09.23/FB
+    	ast.verbose = bDebug; // debugging: 01.09.23/FB
 
         if (queryLanguage.equalsIgnoreCase("poliqarp")) {
             ast = new PoliqarpPlusQueryProcessor(query);
@@ -174,7 +181,9 @@
             throw new IllegalArgumentException(
                     queryLanguage + " is not a supported query language!");
         }
-        System.out.println(this.toJSON());
+        
+        if( bDebug )
+        	System.out.println(this.toJSON());
     }
 
     public QuerySerializer setQuery (String query, String ql, String version) {
@@ -230,7 +239,7 @@
     public final String toJSON () {
         String ser;
         try {
-            ser = mapper.writeValueAsString(raw());
+        	ser = mapper.writeValueAsString(raw());
             // System.out.println(ser);
         }
         catch (JsonProcessingException e) {
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr3DescriptiveErrorListener.java b/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr3DescriptiveErrorListener.java
index 6e574fd..f317c18 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr3DescriptiveErrorListener.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/util/Antlr3DescriptiveErrorListener.java
@@ -31,6 +31,10 @@
 
     @Override
     public void reportError (String error) {
+    	
+    	System.err.printf("Debug: reportError (err): entering for error = '%s'.\n", error);
+    	System.out.printf("Debug: reportError (out): entering for error = '%s'.\n", error);
+    	
         String charPositionStr = null;
         String offendingSymbol = null;
         String expected = null;
@@ -52,17 +56,26 @@
 
 
     public ArrayList<Object> generateFullErrorMsg () {
-        ArrayList<Object> errorSpecs = new ArrayList<Object>();
+    	
+    	ArrayList<Object> errorSpecs = new ArrayList<Object>();
         String msg = getDetailedErrorMessage();
         errorSpecs.add(StatusCodes.MALFORMED_QUERY);
         errorSpecs.add(msg);
         errorSpecs.add(getCharPosition());
+
+        System.err.printf("Debug: generateFullErrorMsg: msg = '%s'.\n", msg);
+    	
         return errorSpecs;
     }
 
 
     private String getDetailedErrorMessage () {
-        // default message, in case no detailed info is available;
+
+        System.err.printf("Debug: getDetailedErrorMessage: pos=%d expected='%s' offend='%s' query='%s'.\n", 
+        		charPosition, expected != null ? expected : "null", offendingSymbol != null ? offendingSymbol : "null", 
+        				query != null ? query : "null");
+
+    	// 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)
diff --git a/src/main/java/de/ids_mannheim/korap/util/C2RecognitionException.java b/src/main/java/de/ids_mannheim/korap/util/C2RecognitionException.java
new file mode 100644
index 0000000..92ba9ef
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/util/C2RecognitionException.java
@@ -0,0 +1,27 @@
+package de.ids_mannheim.korap.util;
+
+import org.antlr.runtime.*;
+
+/* general String manipulation functions moved
+ * from de.ids_mannheim.de.korap.query.parse.cosmas.c2ps_opREG.java and Cosmas2QueryProcessor.java.
+ * 24.10.23/FB
+ */
+
+public final class C2RecognitionException extends RecognitionException {
+
+	private static final boolean DEBUG = false;
+	public String mismatchedToken;
+	
+	public C2RecognitionException(String mismatchedToken)
+	
+	{
+		this.mismatchedToken = mismatchedToken;
+		
+	} // constructor C2RecognitionException
+	
+	public String getMismatchedToken()
+	{
+		return this.mismatchedToken;
+	}
+	
+}