AQL support for commonparent and commonancestor operators
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java b/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
index a09be61..c9f0386 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
@@ -30,8 +30,8 @@
*
*/
public class AqlTree extends Antlr4AbstractSyntaxTree {
- private org.slf4j.Logger log = LoggerFactory
- .getLogger(AqlTree.class);
+ private org.slf4j.Logger log = LoggerFactory
+ .getLogger(AqlTree.class);
/**
* Top-level map representing the whole request.
*/
@@ -60,7 +60,7 @@
* Keeps track of operands that are to be integrated into yet uncreated objects.
*/
LinkedList<LinkedHashMap<String,Object>> operandStack = new LinkedList<LinkedHashMap<String,Object>>();
-
+
/**
* Keeps track of explicitly (by #-var definition) or implicitly (number as reference) introduced entities (for later reference by #-operator)
*/
@@ -97,27 +97,24 @@
* nodes here and exclude the operands from being written into the query map individually.
*/
private LinkedList<String> operandOnlyNodeRefs = new LinkedList<String>();
-
- private LinkedList<Integer> establishedFoci = new LinkedList<Integer>();
- private LinkedList<Integer> usedReferences = new LinkedList<Integer>();
-// private List<String> mirroredPositionFrames = Arrays.asList(new String[]{"startswith", "endswith", "overlaps", "contains"});
- private List<String> mirroredPositionFrames = Arrays.asList(new String[]{});
- List<ParseTree> globalLingTermNodes = new ArrayList<ParseTree>();
+
+ private List<ParseTree> globalLingTermNodes = new ArrayList<ParseTree>();
private int totalRelationCount;
+ /**
+ * Keeps a record of reference-class-mapping, i.e. which 'class' has been assigned to which #n reference. This is important when introducing korap:reference
+ * spans to refer back to previously established classes for entities.
+ */
private LinkedHashMap<String, Integer> refClassMapping = new LinkedHashMap<String, Integer>();
private LinkedHashMap<String, Integer> nodeReferencesTotal = new LinkedHashMap<String, Integer>();
private LinkedHashMap<String, Integer> nodeReferencesProcessed = new LinkedHashMap<String, Integer>();
public static boolean verbose = false;
-
+
/**
*
* @param tree The syntax tree as returned by ANTLR
* @param parser The ANTLR parser instance that generated the parse tree
*/
public AqlTree(String query) {
-// prepareContext();
-// parseAnnisQuery(query);
-// super.parser = this.parser;
requestMap.put("@context", "http://ids-mannheim.de/ns/KorAP/json-ld/v0.1/context.jsonld");
try {
process(query);
@@ -133,16 +130,16 @@
LinkedHashMap<String,Object> operands = new LinkedHashMap<String,Object>();
LinkedHashMap<String,Object> relation = new LinkedHashMap<String,Object>();
LinkedHashMap<String,Object> classMap = new LinkedHashMap<String,Object>();
-
+
operands.put("@id", "korap:operands");
operands.put("@container", "@list");
-
+
relation.put("@id", "korap:relation");
relation.put("@type", "korap:relation#types");
-
+
classMap.put("@id", "korap:class");
classMap.put("@type", "xsd:integer");
-
+
context.put("korap", "http://korap.ids-mannheim.de/ns/query");
context.put("@language", "de");
context.put("operands", operands);
@@ -151,7 +148,7 @@
context.put("query", "korap:query");
context.put("filter", "korap:filter");
context.put("meta", "korap:meta");
-
+
requestMap.put("@context", context);
}
@@ -159,7 +156,7 @@
public Map<String, Object> getRequestMap() {
return requestMap;
}
-
+
@Override
public void process(String query) throws QueryException {
ParseTree tree = parseAnnisQuery(query);
@@ -175,24 +172,22 @@
processNode(tree);
log.info(requestMap.toString());
}
-
+
@SuppressWarnings("unchecked")
private void processNode(ParseTree node) {
// Top-down processing
if (visited.contains(node)) return;
else visited.add(node);
-
+
String nodeCat = getNodeCat(node);
openNodeCats.push(nodeCat);
-
+
stackedObjects = 0;
-
+
if (verbose) {
System.err.println(" "+objectStack);
System.out.println(openNodeCats);
}
-
-
/*
****************************************************************
@@ -201,16 +196,15 @@
****************************************************************
****************************************************************
*/
- if (nodeCat.equals("start")) {
- }
-
if (nodeCat.equals("exprTop")) {
- // has several andTopExpr as children delimited by OR (Disj normal form)
- if (node.getChildCount() > 1) {
- // TODO or-groups for every and
+ List<ParseTree> andTopExprs = getChildrenWithCat(node, "andTopExpr");
+ if (andTopExprs.size() > 1) {
+ LinkedHashMap<String, Object> topOr = makeGroup("or");
+ requestMap.put("query", topOr);
+ objectStack.push(topOr);
}
}
-
+
if (nodeCat.equals("andTopExpr")) {
// Before processing any child expr node, check if it has one or more "*ary_linguistic_term" nodes.
// Those nodes may use references to earlier established operand nodes.
@@ -218,7 +212,6 @@
// naturally as operands of the relations/groups introduced by the
// *node. For that purpose, this section mines all used references
// and stores them in a list for later reference.
-
for (ParseTree exprNode : getChildrenWithCat(node,"expr")) {
// Pre-process any 'variableExpr' such that the variableReferences map can be filled
List<ParseTree> definitionNodes = new ArrayList<ParseTree>();
@@ -248,132 +241,18 @@
}
}
}
-
- // establish new variables or relations between vars
- if (nodeCat.equals("expr")) {
- }
-
+
if (nodeCat.equals("unary_linguistic_term")) {
LinkedHashMap<String, Object> unaryOperator = parseUnaryOperator(node);
String reference = node.getChild(0).toStringTree(parser).substring(1);
LinkedHashMap<String, Object> object = variableReferences.get(reference);
object.putAll(unaryOperator);
}
-
+
if (nodeCat.equals("n_ary_linguistic_term")) {
- System.err.println(operandStack);
- relationCounter++;
- // get operator and determine type of group (sequence/treeRelation/relation/...)
- // It's possible in Annis QL to concatenate operators, so there may be several operators under one n_ary_linguistic_term node.
- // Counter 'i' will iteratively point to all operator nodes (odd-numbered) under this node.
- for (int i=1; i<node.getChildCount(); i = i+2) {
- ParseTree operandTree1 = node.getChild(i-1);
- ParseTree operandTree2 = node.getChild(i+1);
-
- LinkedHashMap<String, Object> operatorGroup = parseOperatorNode(node.getChild(i).getChild(0));
- String groupType;
- try {
- groupType = (String) operatorGroup.get("groupType");
- } catch (ClassCastException | NullPointerException n) {
- groupType = "relation";
- }
- LinkedHashMap<String, Object> group = makeGroup(groupType);
- if (groupType.equals("relation") || groupType.equals("treeRelation")) {
- LinkedHashMap<String, Object> relationGroup = new LinkedHashMap<String, Object>();
- putAllButGroupType(relationGroup, operatorGroup);
- group.put("relation", relationGroup);
- } else if (groupType.equals("sequence") || groupType.equals("position")) {
- putAllButGroupType(group, operatorGroup);
- }
- // Get operands list before possible re-assignment of 'group' (see following 'if')
- ArrayList<Object> operands = (ArrayList<Object>) group.get("operands");
- // Wrap in reference object in case other relations are following
- if (i < node.getChildCount()-2) {
- group = wrapInReference(group, classCounter);
- }
- // Retrieve operands.
- String ref1 = null;
- String ref2 = null;
- LinkedHashMap<String, Object> operand1 = null;
- LinkedHashMap<String, Object> operand2 = null;
- // Operand 1
- if (!getNodeCat(operandTree1.getChild(0)).equals("variableExpr")) {
- ref1 = operandTree1.getChild(0).toStringTree(parser).substring(1);
- operand1 = variableReferences.get(ref1);
- if (nodeReferencesTotal.get(ref1) > 1) {
- if (nodeReferencesProcessed.get(ref1) == 0) {
- refClassMapping.put(ref1, classCounter);
- operand1 = wrapInClass(operand1);
- nodeReferencesProcessed.put(ref1, nodeReferencesProcessed.get(ref1)+1);
- } else if (nodeReferencesProcessed.get(ref1)>0 && nodeReferencesTotal.get(ref1)>1) {
- try {
- operand1 = wrapInReference(operandStack.pop(), refClassMapping.get(ref1));
- } catch (NoSuchElementException e) {
- operand1 = makeReference(refClassMapping.get(ref1));
- }
- }
- }
- }
- // Operand 2
- if (!getNodeCat(operandTree2.getChild(0)).equals("variableExpr")) {
- ref2 = operandTree2.getChild(0).toStringTree(parser).substring(1);
- operand2 = variableReferences.get(ref2);
- if (nodeReferencesTotal.get(ref2) > 1) {
- if (nodeReferencesProcessed.get(ref2)==0) {
- refClassMapping.put(ref2, classCounter);
- operand2 = wrapInClass(operand2);
- nodeReferencesProcessed.put(ref2, nodeReferencesProcessed.get(ref2)+1);
- } else if (nodeReferencesProcessed.get(ref2)>0 && nodeReferencesTotal.get(ref2)>1) {
- try {
- operand2 = wrapInReference(operandStack.pop(), refClassMapping.get(ref2));
- } catch (NoSuchElementException e) {
- operand2 = makeReference(refClassMapping.get(ref2));
- }
- }
- }
- }
- // Inject operands.
- // -> Case distinction:
- if (node.getChildCount()==3) {
- // Things are easy when there's just one operator (thus 3 children incl. operands)...
- if (operand1 != null) operands.add(operand1);
- if (operand2 != null) operands.add(operand2);
- } else {
- // ... but things get a little more complicated here. The AST is of this form: (operand1 operator 1 operand2 operator2 operand3 operator3 ...)
- // but we'll have to serialize it in a nested, binary way: (((operand1 operator1 operand2) operator2 operand3) operator3 ...)
- // the following code will do just that:
- if (i == 1) {
- // for the first operator, include both operands
- if (operand1 != null) operands.add(operand1);
- if (operand2 != null) operands.add(wrapInClass(operand2));
- // Don't put this into the super object directly but store on operandStack
- // (because this group will have to be an operand of a subsequent operator)
- operandStack.push(group);
- // for all subsequent operators, only take the 2nd operand (first was already added by previous operator)
- } else if (i < node.getChildCount()-2){
- // for all intermediate operators, include other previous groups and 2nd operand. Store this on the operandStack, too.
- if (operand2 != null) operands.add(wrapInClass(operand2));
- operands.add(0, operandStack.pop());
- operandStack.push(group);
- } else if (i == node.getChildCount()-2) {
- // This is the last operator. Include 2nd operand only
- if (operand2 != null) operands.add(operand2);
- }
- }
- // Final step: decide what to do with the 'group' object, depending on whether all relations have been processed
- if (i == node.getChildCount()-2 && relationCounter == totalRelationCount) {
- putIntoSuperObject(group);
- if (!operandStack.isEmpty()) {
- operands.add(0, operandStack.pop());
- }
- objectStack.push(group);
- stackedObjects++;
- } else {
- operandStack.push(group);
- }
- }
+ processN_ary_linguistic_term(node);
}
-
+
if (nodeCat.equals("variableExpr")) {
// simplex word or complex assignment (like qname = textSpec)?
String firstChildNodeCat = getNodeCat(node.getChild(0));
@@ -403,7 +282,7 @@
object.put("wrap", term);
term.putAll(parseTextSpec(node.getChild(0)));
}
-
+
if (node.getChildCount() == 3) { // (foundry/)?layer=key specification
if (object.get("@type").equals("korap:token")) {
HashMap<String, Object> term = (HashMap<String, Object>) object.get("wrap");
@@ -414,7 +293,7 @@
object.put("match", parseMatchOperator(node.getChild(1)));
}
}
-
+
if (object != null) {
if (! operandOnlyNodeRefs.contains(variableCounter.toString())) {
putIntoSuperObject(object);
@@ -425,7 +304,7 @@
}
objectsToPop.push(stackedObjects);
-
+
/*
****************************************************************
****************************************************************
@@ -451,22 +330,162 @@
openNodeCats.pop();
}
- private LinkedHashMap<String, Object> wrapInReference(LinkedHashMap<String, Object> group, Integer classId) {
- LinkedHashMap<String, Object> refGroup = makeReference(classId);
- ArrayList<Object> operands = new ArrayList<Object>();
- operands.add(group);
- refGroup.put("operands", operands);
- return refGroup;
+
+
+ /**
+ * Processes an operand node, creating a map for the operand containing all its information
+ * given in the node definition (referenced via '#'). If this node has been referred to and used earlier,
+ * a korap:reference is created in its place.
+ * The operand will be wrapped in a class group if necessary.
+ * @param operandTree
+ * @return A map object with the appropriate CQLF representation of the operand
+ */
+ private LinkedHashMap<String, Object> retrieveOperand(ParseTree operandTree) {
+ LinkedHashMap<String, Object> operand = null;
+ if (!getNodeCat(operandTree.getChild(0)).equals("variableExpr")) {
+ String ref = operandTree.getChild(0).toStringTree(parser).substring(1);
+ operand = variableReferences.get(ref);
+ if (nodeReferencesTotal.get(ref) > 1) {
+ if (nodeReferencesProcessed.get(ref)==0) {
+ refClassMapping.put(ref, classCounter);
+ operand = wrapInClass(operand, classCounter++);
+ nodeReferencesProcessed.put(ref, nodeReferencesProcessed.get(ref)+1);
+ } else if (nodeReferencesProcessed.get(ref)>0 && nodeReferencesTotal.get(ref)>1) {
+ try {
+ operand = wrapInReference(operandStack.pop(), refClassMapping.get(ref));
+ } catch (NoSuchElementException e) {
+ operand = makeReference(refClassMapping.get(ref));
+ }
+ }
+ }
+ }
+ return operand;
}
@SuppressWarnings("unchecked")
- private LinkedHashMap<String, Object> wrapInClass(LinkedHashMap<String, Object> group) {
- LinkedHashMap<String, Object> classGroup = makeSpanClass(classCounter);
- ((ArrayList<Object>) classGroup.get("operands")).add(group);
- classCounter++;
- return classGroup;
+ private void processN_ary_linguistic_term(ParseTree node) {
+ relationCounter++;
+ // get operator and determine type of group (sequence/treeRelation/relation/...)
+ // It's possible in Annis QL to concatenate operators, so there may be several operators under one n_ary_linguistic_term node.
+ // Counter 'i' will iteratively point to all operator nodes (odd-numbered) under this node.
+ for (int i=1; i<node.getChildCount(); i = i+2) {
+ ParseTree operandTree1 = node.getChild(i-1);
+ ParseTree operandTree2 = node.getChild(i+1);
+ String reltype = getNodeCat(node.getChild(i).getChild(0));
+
+ LinkedHashMap<String,Object> group = null;
+ ArrayList<Object> operands = null;
+ // Retrieve operands.
+ LinkedHashMap<String, Object> operand1 = retrieveOperand(operandTree1);
+ LinkedHashMap<String, Object> operand2 = retrieveOperand(operandTree2);
+
+ // 'Proper' n_ary_linguistic_operators receive a considerably different serialisation than 'commonparent' and 'commonancestor'.
+ // For the latter cases, a dummy span is introduced and declared as a span class that has a dominance relation towards
+ // the two operands, one after the other, thus resulting in two nested relations! A Poliqarp+ equivalent for A $ B would be
+ // contains(focus(1:contains({1:<>},A)), B).
+ // This is modeled here...
+ if (reltype.equals("commonparent") || reltype.equals("commonancestor")) {
+ // make an (outer) group and an inner group containing the dummy node or previous relations
+ group = makeGroup("relation");
+ LinkedHashMap<String,Object> innerGroup = makeGroup("relation");
+ LinkedHashMap<String,Object> treeRelation = makeTreeRelation("dominance");
+ // commonancestor is an indirect commonparent relation
+ if (reltype.equals("commonancestor")) treeRelation.put("distance", makeDistance("r", 1, MAXIMUM_DISTANCE));
+ group.put("relation", treeRelation);
+ innerGroup.put("relation", treeRelation);
+ // Get operands list before possible re-assignment of 'group' (see following 'if')
+ ArrayList<Object> outerOperands = (ArrayList<Object>) group.get("operands");
+ ArrayList<Object> innerOperands = (ArrayList<Object>) innerGroup.get("operands");
+ // for lowest level, add the underspecified node as first operand and wrap it in a class group
+ if (i == 1) {
+ innerOperands.add(wrapInClass(makeSpan(), classCounter));
+ // add the first operand and wrap the whole group in a focusing reference
+ innerOperands.add(operand1);
+ innerGroup = wrapInReference(innerGroup, classCounter);
+ outerOperands.add(innerGroup);
+ } else {
+ outerOperands.add(operandStack.pop());
+ }
+ // Lookahead: if next operator is not commonparent or commonancestor, wrap in class for accessibility
+ if (i < node.getChildCount()-2 && !getNodeCat(node.getChild(i+2).getChild(0)).startsWith("common")) {
+ operand2 = wrapInClass(operand2, ++classCounter);
+ }
+ outerOperands.add(operand2);
+
+ // Wrap in another reference object in case other relations are following
+ if (i < node.getChildCount()-2) {
+ group = wrapInReference(group, classCounter);
+ }
+ // All other n-ary linguistic relations have special 'relation' attributes defined in CQLF and can be
+ // handled more easily...
+ } else {
+ LinkedHashMap<String, Object> operatorGroup = parseOperatorNode(node.getChild(i).getChild(0));
+ String groupType;
+ try {
+ groupType = (String) operatorGroup.get("groupType");
+ } catch (ClassCastException | NullPointerException n) {
+ groupType = "relation";
+ }
+ group = makeGroup(groupType);
+ if (groupType.equals("relation") || groupType.equals("treeRelation")) {
+ LinkedHashMap<String, Object> relationGroup = new LinkedHashMap<String, Object>();
+ putAllButGroupType(relationGroup, operatorGroup);
+ group.put("relation", relationGroup);
+ } else if (groupType.equals("sequence") || groupType.equals("position")) {
+ putAllButGroupType(group, operatorGroup);
+ }
+ // Get operands list before possible re-assignment of 'group' (see following 'if')
+ operands = (ArrayList<Object>) group.get("operands");
+ // Wrap in reference object in case other relations are following
+ if (i < node.getChildCount()-2) {
+ group = wrapInReference(group, classCounter);
+ }
+
+ // Inject operands.
+ // -> Case distinction:
+ if (node.getChildCount()==3) {
+ // Things are easy when there's just one operator (thus 3 children incl. operands)...
+ if (operand1 != null) operands.add(operand1);
+ if (operand2 != null) operands.add(operand2);
+ } else {
+ // ... but things get a little more complicated here. The AST is of this form: (operand1 operator 1 operand2 operator2 operand3 operator3 ...)
+ // but we'll have to serialize it in a nested, binary way: (((operand1 operator1 operand2) operator2 operand3) operator3 ...)
+ // the following code will do just that:
+ if (i == 1) {
+ // for the first operator, include both operands
+ if (operand1 != null) operands.add(operand1);
+ if (operand2 != null) operands.add(wrapInClass(operand2, classCounter++));
+ // Don't put this into the super object directly but store on operandStack
+ // (because this group will have to be an operand of a subsequent operator)
+ operandStack.push(group);
+ // for all subsequent operators, only take the 2nd operand (first was already added by previous operator)
+ } else if (i < node.getChildCount()-2) {
+ // for all intermediate operators, include other previous groups and 2nd operand. Store this on the operandStack, too.
+ if (operand2 != null) operands.add(wrapInClass(operand2, classCounter++));
+ operands.add(0, operandStack.pop());
+ operandStack.push(group);
+ } else if (i == node.getChildCount()-2) {
+ // This is the last operator. Include 2nd operand only
+ if (operand2 != null) operands.add(operand2);
+ }
+ }
+ }
+ // Final step: decide what to do with the 'group' object, depending on whether all relations have been processed
+ if (i == node.getChildCount()-2 && relationCounter == totalRelationCount) {
+ putIntoSuperObject(group);
+ if (!operandStack.isEmpty()) {
+ operands.add(0, operandStack.pop());
+ }
+ objectStack.push(group);
+ stackedObjects++;
+ } else {
+ operandStack.push(group);
+ }
+ }
}
+
+
/**
* Parses a unary_linguistic_operator node. Possible operators are: root, arity, tokenarity.
* Operators are embedded into a korap:term, in turn wrapped by an 'attr' property in a korap:span.
@@ -483,7 +502,6 @@
} else {
term.put(op, true);
}
-
attr.put("attr", term);
return attr;
}
@@ -507,22 +525,22 @@
if (edgeSpec != null) relation.put("wrap", parseEdgeSpec(edgeSpec)) ;
if (star != null) relation.put("distance", makeDistance("r", 0, 100));
if (rangeSpec != null) relation.put("distance", distanceFromRangeSpec("r", rangeSpec));
-
+
}
else if (operator.equals("pointing")) {
-// String reltype = operatorNode.getChild(1).toStringTree(parser);
+ // String reltype = operatorNode.getChild(1).toStringTree(parser);
relation = makeRelation(null);
relation.put("groupType", "relation");
ParseTree qName = getFirstChildWithCat(operatorNode, "qName");
ParseTree edgeSpec = getFirstChildWithCat(operatorNode, "edgeSpec");
ParseTree star = getFirstChildWithCat(operatorNode, "*");
ParseTree rangeSpec = getFirstChildWithCat(operatorNode, "rangeSpec");
-// if (qName != null) relation.putAll(parseQNameNode(qName));
+ // if (qName != null) relation.putAll(parseQNameNode(qName));
if (qName != null) relation.put("reltype", qName.getText());
if (edgeSpec != null) relation.put("wrap", parseEdgeSpec(edgeSpec)) ;
if (star != null) relation.put("distance", makeDistance("r", 0, 100));
if (rangeSpec != null) relation.put("distance", distanceFromRangeSpec("r", rangeSpec));
-
+
}
else if (operator.equals("precedence")) {
relation = new LinkedHashMap<String, Object>();
@@ -541,47 +559,40 @@
relation.put("inOrder", true);
}
else if (operator.equals("spanrelation")) {
- relation = makeGroup(null);
+ relation = makeGroup("position");
relation.put("groupType", "position");
String reltype = operatorNode.getChild(0).toStringTree(parser);
String frame = null;
boolean inOrder = true;
switch (reltype) {
- case "_=_":
- frame = "matches"; break;
- case "_l_":
- frame = "startswith";
- inOrder = false;
- break;
- case "_r_":
- frame = "endswith";
- inOrder = false;
- break;
- case "_i_":
- frame = "contains"; break;
- case "_o_":
- frame = "overlaps";
- inOrder = false;
- break;
- case "_ol_":
- frame = "overlapsLeft";
- inOrder = false;
- break;
- case "_or_":
- frame = "overlapsRight";
- inOrder = false;
- break;
+ case "_=_":
+ frame = "matches"; break;
+ case "_l_":
+ frame = "startswith";
+ inOrder = false;
+ break;
+ case "_r_":
+ frame = "endswith";
+ inOrder = false;
+ break;
+ case "_i_":
+ frame = "contains"; break;
+ case "_o_":
+ frame = "overlaps";
+ inOrder = false;
+ break;
+ case "_ol_":
+ frame = "overlapsLeft";
+ inOrder = false;
+ break;
+ case "_or_":
+ frame = "overlapsRight";
+ inOrder = false;
+ break;
}
- relation.put("operation", "operation:position");
if (!inOrder) relation.put("inOrder", false);
relation.put("frame", "frame:"+frame);
}
- else if (operator.equals("commonparent")) {
- //TODO
- }
- else if (operator.equals("commonancestor")) {
- //TODO
- }
else if (operator.equals("identity")) {
//TODO
}
@@ -621,7 +632,7 @@
private LinkedHashMap<String, Object> boundaryFromRangeSpec(ParseTree rangeSpec) {
return boundaryFromRangeSpec(rangeSpec, true);
}
-
+
private LinkedHashMap<String, Object> boundaryFromRangeSpec(ParseTree rangeSpec, boolean expandToMax) {
Integer min = Integer.parseInt(rangeSpec.getChild(0).toStringTree(parser));
Integer max = min;
@@ -638,7 +649,7 @@
max = Integer.parseInt(rangeSpec.getChild(2).toStringTree(parser));
return makeDistance(key, min, max);
}
-
+
private LinkedHashMap<String, Object> parseDistance(ParseTree rangeSpec) {
Integer min = Integer.parseInt(rangeSpec.getChild(0).toStringTree(parser));
Integer max = MAXIMUM_DISTANCE;
@@ -646,7 +657,7 @@
max = Integer.parseInt(rangeSpec.getChild(2).toStringTree(parser));
return makeDistance("w", min, max);
}
-
+
private LinkedHashMap<String, Object> parseTextSpec(ParseTree node) {
LinkedHashMap<String, Object> term = new LinkedHashMap<String, Object>();
if (hasChild(node, "regex")) {
@@ -667,27 +678,6 @@
private String parseMatchOperator(ParseTree node) {
return node.toStringTree(parser).equals("=") ? "match:eq" : "match:ne";
}
-
-
- /**
- * Parses a textSpec node (which holds the 'key' field)
- * @param node
- * @return
- */
- private LinkedHashMap<String, Object> parseVarKey(ParseTree node) {
- LinkedHashMap<String, Object> fields = new LinkedHashMap<String, Object>();
- if (node.getChildCount() == 2) { // no content, empty quotes
-
- } else if (node.getChildCount() == 3) {
- fields.put("key", node.getChild(1).toStringTree(parser));
- if (node.getChild(0).toStringTree(parser).equals("/") && // slashes -> regex
- node.getChild(2).toStringTree(parser).equals("/")) {
- fields.put("type", "type:regex");
- }
- }
- return fields;
- }
-
private LinkedHashMap<String, Object> parseQNameNode(ParseTree node) {
LinkedHashMap<String, Object> fields = new LinkedHashMap<String, Object>();
@@ -701,7 +691,7 @@
private void putIntoSuperObject(LinkedHashMap<String, Object> object) {
putIntoSuperObject(object, 0);
}
-
+
@SuppressWarnings({ "unchecked" })
private void putIntoSuperObject(LinkedHashMap<String, Object> object, int objStackPosition) {
if (objectStack.size()>objStackPosition) {
@@ -716,14 +706,6 @@
}
}
- @SuppressWarnings("unchecked")
- private void putGroupAndWrapFocus(LinkedHashMap<String, Object> thisObject, LinkedHashMap<String, Object> formerObject, int focusClass) {
- LinkedHashMap<String, Object> focusWrap = makeGroup("focus");
- focusWrap.put("classRef", focusClass);
- ((ArrayList<Object>) focusWrap.get("operands")).add(formerObject);
- ((ArrayList<Object>) thisObject.get("operands")).add(focusWrap);
- }
-
private void putAllButGroupType(Map<String, Object> container, Map<String, Object> input) {
for (String key : input.keySet()) {
if (!key.equals("groupType")) {
@@ -731,73 +713,58 @@
}
}
}
-
+
private ParserRuleContext parseAnnisQuery (String p) throws QueryException {
Lexer poliqarpLexer = new AqlLexer((CharStream)null);
- ParserRuleContext tree = null;
- // Like p. 111
- try {
+ ParserRuleContext tree = null;
+ // Like p. 111
+ try {
- // Tokenize input data
- ANTLRInputStream input = new ANTLRInputStream(p);
- poliqarpLexer.setInputStream(input);
- CommonTokenStream tokens = new CommonTokenStream(poliqarpLexer);
- parser = new AqlParser(tokens);
+ // Tokenize input data
+ ANTLRInputStream input = new ANTLRInputStream(p);
+ poliqarpLexer.setInputStream(input);
+ CommonTokenStream tokens = new CommonTokenStream(poliqarpLexer);
+ parser = new AqlParser(tokens);
- // Don't throw out erroneous stuff
- parser.setErrorHandler(new BailErrorStrategy());
- parser.removeErrorListeners();
+ // Don't throw out erroneous stuff
+ parser.setErrorHandler(new BailErrorStrategy());
+ parser.removeErrorListeners();
- // Get starting rule from parser
- Method startRule = AqlParser.class.getMethod("start");
- tree = (ParserRuleContext) startRule.invoke(parser, (Object[])null);
- }
+ // Get starting rule from parser
+ Method startRule = AqlParser.class.getMethod("start");
+ tree = (ParserRuleContext) startRule.invoke(parser, (Object[])null);
+ }
- // Some things went wrong ...
- catch (Exception e) {
- log.error(e.getMessage());
- System.err.println( e.getMessage() );
- }
-
- if (tree == null) {
- log.error("Could not parse query. Make sure it is correct ANNIS QL syntax.");
- throw new QueryException("Could not parse query. Make sure it is correct ANNIS QL syntax.");
- }
+ // Some things went wrong ...
+ catch (Exception e) {
+ log.error(e.getMessage());
+ System.err.println( e.getMessage() );
+ }
- // Return the generated tree
- return tree;
- }
-
+ if (tree == null) {
+ log.error("Could not parse query. Make sure it is correct ANNIS QL syntax.");
+ throw new QueryException("Could not parse query. Make sure it is correct ANNIS QL syntax.");
+ }
+
+ // Return the generated tree
+ return tree;
+ }
+
public static void main(String[] args) {
/*
* For testing
*/
String[] queries = new String[] {
-// "node & #1:root",
-// "pos=\"N\" & pos=\"V\" & pos=\"N\" & #1 . #2 & #2 . #3",
-// "cat=\"NP\" & #1:tokenarity=2",
-// "node & node & node & #1 . #2 . #3",
-// "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & #1 > #2 > #3",
-// "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & cat=\"DP\" & #1 > #2 > #3 > #4",
-// "pos=\"N\" & pos=\"V\" & pos=\"P\" & #1 . #2 & #2 . #3"
-// "cnx/cat=\"NP\" > node",
-// "node > node",
-// "cat=/NP/ > node",
-// "/Mann/",
-// "node > tok=\"foo\"",
-// "tok=\"Sonne\" & tok=\"Mond\" & #1 > #2 .0,4 tok=\"Sterne\"",
-// "pos=\"N\" & pos=\"V\" & pos=\"P\" & #1 . #2 & #2 . #3",
-// "cat=\"NP\" & pos=\"V\" & pos=\"P\" & #1 > #2 & #1 > #3 & #2 . #3",
- "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & #1 > #2 > #3"
- };
- AqlTree.verbose=true;
+ "cat=\"NP\" & cat=\"VP\" & #1 $ #2 ",
+ };
+ // AqlTree.verbose=true;
for (String q : queries) {
try {
System.out.println(q);
AqlTree at = new AqlTree(q);
System.out.println(at.parseAnnisQuery(q).toStringTree(at.parser));
System.out.println();
-
+
} catch (NullPointerException | QueryException npe) {
npe.printStackTrace();
}
diff --git a/src/test/java/AqlTreeTest.java b/src/test/java/AqlTreeTest.java
index 9106dad..1fd636f 100644
--- a/src/test/java/AqlTreeTest.java
+++ b/src/test/java/AqlTreeTest.java
@@ -181,7 +181,7 @@
query = "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & #1 > #2 > #3";
String dom1 =
"{@type=korap:group, operation=operation:relation, operands=[" +
- "{@type=korap:reference, classRef=[0], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
"{@type=korap:group, operation=operation:relation, operands=[" +
"{@type=korap:span, layer=cat, key=CP, match=match:eq}," +
"{@type=korap:group, operation=operation:class, class=0, operands=[" +
@@ -199,9 +199,9 @@
query = "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & cat=\"DP\" & #1 > #2 > #3 > #4";
String dom2 =
"{@type=korap:group, operation=operation:relation, operands=[" +
- "{@type=korap:reference, classRef=[1], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[1], operands=[" +
"{@type=korap:group, operation=operation:relation, operands=[" +
- "{@type=korap:reference, classRef=[0], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
"{@type=korap:group, operation=operation:relation, operands=[" +
"{@type=korap:span, layer=cat, key=CP, match=match:eq}," +
"{@type=korap:group, operation=operation:class, class=0, operands=[" +
@@ -295,7 +295,7 @@
String seq4 =
"{@type=korap:group, operation=operation:sequence," +
"operands=[" +
- "{@type=korap:reference, classRef=[0], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
"{@type=korap:group, operation=operation:sequence, operands=[" +
"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sonne, match=match:eq}}," +
"{@type=korap:group, operation=operation:class, class=0, operands=[" +
@@ -317,7 +317,7 @@
query = "node & node & node & #1 . #2 .1,3 #3";
String seq5 =
"{@type=korap:group, operation=operation:sequence, operands=[" +
- "{@type=korap:reference, classRef=[0], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
"{@type=korap:group, operation=operation:sequence, operands=[" +
"{@type=korap:span}," +
"{@type=korap:group, operation=operation:class, class=0, operands=[" +
@@ -337,9 +337,9 @@
query = "tok=\"Sonne\" & tok=\"Mond\" & tok=\"Sterne\" & tok=\"Himmel\" & #1 .0,2 #2 .0,4 #3 . #4";
String seq6 =
"{@type=korap:group, operation=operation:sequence, operands=[" +
- "{@type=korap:reference, classRef=[1], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[1], operands=[" +
"{@type=korap:group, operation=operation:sequence, operands=[" +
- "{@type=korap:reference, classRef=[0], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
"{@type=korap:group, operation=operation:sequence, operands=[" +
"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sonne, match=match:eq}}," +
"{@type=korap:group, operation=operation:class, class=0, operands=[" +
@@ -368,7 +368,7 @@
query = "tok=\"Sonne\" & tok=\"Mond\" & tok=\"Sterne\" & #1 > #2 .0,4 #3";
String seq4 =
"{@type=korap:group, operation=operation:sequence, operands=[" +
- "{@type=korap:reference, classRef=[0], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
"{@type=korap:group, operation=operation:relation, operands=[" +
"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sonne, match=match:eq}}," +
"{@type=korap:group, operation=operation:class, class=0, operands=[" +
@@ -388,7 +388,7 @@
query = "tok=\"Sonne\" & tok=\"Mond\" & #1 > #2 .0,4 tok=\"Sterne\"";
String seq5 =
"{@type=korap:group, operation=operation:sequence, operands=[" +
- "{@type=korap:reference, classRef=[0], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
"{@type=korap:group, operation=operation:relation, operands=[" +
"{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sonne, match=match:eq}}," +
"{@type=korap:group, operation=operation:class, class=0, operands=[" +
@@ -404,6 +404,32 @@
aqlt = new AqlTree(query);
map = aqlt.getRequestMap().get("query").toString();
assertEquals(seq5.replaceAll(" ", ""), map.replaceAll(" ", ""));
+
+ query = "cat=\"NP\" & cat=\"VP\" & cat=\"PP\" & #1 $ #2 > #3";
+ String cp2 =
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[1], operands=[" +
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:group, operation=operation:class, class=0, operands=[" +
+ "{@type=korap:span}" +
+ "]}," +
+ "{@type=korap:span, layer=cat, key=NP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}}" +
+ "]}," +
+ "{@type=korap:group, operation=operation:class, class=1, operands=[" +
+ "{@type=korap:span, layer=cat, key=VP, match=match:eq}" +
+ "]}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}" +
+ "}" +
+ "]}," +
+ "{@type=korap:span, layer=cat, key=PP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}" +
+ "}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(cp2.replaceAll(" ", ""), map.replaceAll(" ", ""));
}
@Test
@@ -470,7 +496,7 @@
query = "pos=\"N\" & pos=\"V\" & pos=\"P\" & #1 . #2 & #2 . #3";
String mult1 =
"{@type=korap:group, operation=operation:sequence, operands=[" +
- "{@type=korap:reference, classRef=[0], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
"{@type=korap:group, operation=operation:sequence, operands=[" +
"{@type=korap:token, wrap={@type=korap:term, layer=pos, key=N, match=match:eq}}," +
"{@type=korap:group, operation=operation:class, class=0, operands=[" +
@@ -488,7 +514,7 @@
query = "pos=\"N\" & pos=\"V\" & #1 . #2 & #2 . pos=\"P\"";
String mult2 =
"{@type=korap:group, operation=operation:sequence, operands=[" +
- "{@type=korap:reference, classRef=[0], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
"{@type=korap:group, operation=operation:sequence, operands=[" +
"{@type=korap:token, wrap={@type=korap:term, layer=pos, key=N, match=match:eq}}," +
"{@type=korap:group, operation=operation:class, class=0, operands=[" +
@@ -505,7 +531,7 @@
query = "pos=\"N\" & pos=\"V\" & pos=\"P\" & #1 > #2 & #1 > #3";
String mult3 =
"{@type=korap:group, operation=operation:relation, operands=[" +
- "{@type=korap:reference, classRef=[0], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
"{@type=korap:group, operation=operation:relation, operands=[" +
"{@type=korap:group, operation=operation:class, class=0, operands=[" +
"{@type=korap:token, wrap={@type=korap:term, layer=pos, key=N, match=match:eq}}" +
@@ -523,10 +549,10 @@
String mult4 =
"{@type=korap:group, operation=operation:sequence, operands=[" +
// reduce dominance relations "#1 > #2 & #1 > #3" to operand #2 in order to make it accessible for #2 . #3 (the last/outermost relation)
- "{@type=korap:reference, classRef=[1], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[1], operands=[" +
"{@type=korap:group, operation=operation:relation, operands=[" +
// dominance relation #1 > #2 is reduced to #1, for expressing #1 > #3
- "{@type=korap:reference, classRef=[0], operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
"{@type=korap:group, operation=operation:relation, operands=[" +
"{@type=korap:group, operation=operation:class, class=0, operands=[" +
"{@type=korap:span, layer=cat, key=NP, match=match:eq}" +
@@ -543,7 +569,7 @@
"], relation={@type=korap:treeRelation, reltype=dominance}}" +
"]}," +
// refer back to class 2 as second operand
- "{@type=korap:reference, classRef=[2]}" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[2]}" +
"], inOrder=true}";
aqlt = new AqlTree(query);
map = aqlt.getRequestMap().get("query").toString();
@@ -585,6 +611,78 @@
assertEquals(unary4.replaceAll(" ", ""), map.replaceAll(" ", ""));
}
- // TODO commonparent, commonancestor
-
+ @Test
+ public void testCommonParent() throws QueryException {
+ query = "cat=\"NP\" & cat=\"VP\" & #1 $ #2";
+ String cp1 =
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:group, operation=operation:class, class=0, operands=[" +
+ "{@type=korap:span}" +
+ "]}," +
+ "{@type=korap:span, layer=cat, key=NP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}}" +
+ "]}," +
+ "{@type=korap:span, layer=cat, key=VP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}}" +
+ "";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(cp1.replaceAll(" ", ""), map.replaceAll(" ", ""));
+
+ query = "cat=\"NP\" & cat=\"VP\" & cat=\"PP\" & #1 $ #2 $ #3";
+ String cp2 =
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:group, operation=operation:class, class=0, operands=[" +
+ "{@type=korap:span}" +
+ "]}," +
+ "{@type=korap:span, layer=cat, key=NP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}}" +
+ "]}," +
+ "{@type=korap:span, layer=cat, key=VP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}" +
+ "}" +
+ "]}," +
+ "{@type=korap:span, layer=cat, key=PP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}" +
+ "}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(cp2.replaceAll(" ", ""), map.replaceAll(" ", ""));
+
+ query = "cat=\"NP\" & cat=\"VP\" & cat=\"PP\" & cat=\"CP\" & #1 $ #2 $ #3 $ #4";
+ String cp3 =
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:reference, operation=operation:focus, classRef=[0], operands=[" +
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:group, operation=operation:class, class=0, operands=[" +
+ "{@type=korap:span}" +
+ "]}," +
+ "{@type=korap:span, layer=cat, key=NP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}}" +
+ "]}," +
+ "{@type=korap:span, layer=cat, key=VP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}}" +
+ "]}," +
+ "{@type=korap:span, layer=cat, key=PP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}}" +
+ "]}," +
+ "{@type=korap:span, layer=cat, key=CP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}" +
+ "}" +
+ "";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(cp3.replaceAll(" ", ""), map.replaceAll(" ", ""));
+ }
+
}
\ No newline at end of file