Added reference query deserialization
Change-Id: I145e608ae114f34ced7788fa124aa0508d21ea5c
diff --git a/src/main/java/de/ids_mannheim/korap/KrillQuery.java b/src/main/java/de/ids_mannheim/korap/KrillQuery.java
index 578d10b..5a05bd5 100644
--- a/src/main/java/de/ids_mannheim/korap/KrillQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/KrillQuery.java
@@ -9,6 +9,8 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import de.ids_mannheim.korap.query.QueryBuilder;
import de.ids_mannheim.korap.query.SpanWithinQuery;
@@ -17,6 +19,7 @@
import de.ids_mannheim.korap.query.wrap.SpanClassQueryWrapper;
import de.ids_mannheim.korap.query.wrap.SpanFocusQueryWrapper;
import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanReferenceQueryWrapper;
import de.ids_mannheim.korap.query.wrap.SpanRegexQueryWrapper;
import de.ids_mannheim.korap.query.wrap.SpanRelationWrapper;
import de.ids_mannheim.korap.query.wrap.SpanRepetitionQueryWrapper;
@@ -220,7 +223,6 @@
throw new QueryException(766,
"Peripheral references are currently not supported");
}
- ;
JsonNode operands = json.get("operands");
@@ -378,6 +380,11 @@
if (DEBUG)
log.trace("Operands are {}", operands);
+ SpanQueryWrapper spanReferenceQueryWrapper = _operationReferenceFromJSON(json, operands);
+ if (spanReferenceQueryWrapper != null) {
+ return spanReferenceQueryWrapper;
+ }
+
// Branch on operation
switch (operation) {
case "operation:junction":
@@ -417,6 +424,87 @@
throw new QueryException(711, "Unknown group operation");
};
+ private SpanQueryWrapper _operationReferenceFromJSON(JsonNode node, JsonNode operands)
+ throws QueryException {
+ boolean isReference = false;
+ int classNum = -1;
+ int refOperandNum = -1;
+ JsonNode childNode;
+
+ for (int i = 0; i < operands.size(); i++) {
+ childNode = operands.get(i);
+ if (childNode.has("@type")
+ && childNode.get("@type").asText()
+ .equals("koral:reference")
+ && childNode.has("operation")
+ && childNode.get("operation").asText()
+ .equals("operation:focus")
+ && !childNode.has("operands")) {
+
+ if (childNode.has("classRef")) {
+ classNum = childNode.get("classRef").get(0).asInt();
+ refOperandNum = i;
+ isReference = true;
+ break;
+ }
+ }
+ }
+
+ if (isReference) {
+ JsonNode resolvedNode = _resolveReference(node, operands,
+ refOperandNum, classNum);
+ return new SpanReferenceQueryWrapper(fromJson(resolvedNode),
+ (byte) classNum);
+ }
+
+ return null;
+ }
+
+ private JsonNode _resolveReference(JsonNode node, JsonNode operands,
+ int refOperandNum, int classNum) throws QueryException {
+ JsonNode referent = null;
+ ObjectMapper m = new ObjectMapper();
+ ArrayNode newOperands = m.createArrayNode();
+ boolean isReferentFound = false;
+ for (int i = 0; i < operands.size(); i++) {
+ if (i != refOperandNum) {
+ if (!isReferentFound) {
+ referent = _extractReferentClass(operands.get(i), classNum);
+ if (referent != null) isReferentFound = true;
+ }
+ newOperands.insert(i, operands.get(i));
+ }
+ }
+
+ if (isReferentFound) {
+ newOperands.insert(refOperandNum, referent);
+ ((ObjectNode) node).set("operands", newOperands);
+ return node;
+ }
+ else
+ throw new QueryException("Referent node is not found");
+
+ }
+
+ private JsonNode _extractReferentClass(JsonNode node, int classNum) {
+ JsonNode referent;
+ if (node.has("classOut") && node.get("classOut").asInt() == classNum) {
+ // System.out.println("found: " + node.toString());
+ return node;
+ }
+ else {
+ if (node.has("operands") && node.get("operands").isArray()) {
+ for (JsonNode childOperand : node.get("operands")) {
+ referent = _extractReferentClass(childOperand, classNum);
+ if (referent != null) {
+ return referent;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
private SpanQueryWrapper _operationRelationFromJson (JsonNode operands,
JsonNode relation) throws QueryException {
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanReferenceQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanReferenceQueryWrapper.java
new file mode 100644
index 0000000..02a734f
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanReferenceQueryWrapper.java
@@ -0,0 +1,47 @@
+package de.ids_mannheim.korap.query.wrap;
+
+import org.apache.lucene.search.spans.SpanQuery;
+
+import de.ids_mannheim.korap.query.SpanReferenceQuery;
+import de.ids_mannheim.korap.util.QueryException;
+
+public class SpanReferenceQueryWrapper extends SpanQueryWrapper {
+
+ private SpanQueryWrapper subQuery;
+ private byte classNum;
+
+ public SpanReferenceQueryWrapper (SpanQueryWrapper subQueryWrapper,
+ byte classNum) {
+ this.subQuery = subQueryWrapper;
+ if (subQuery != null) {
+ this.isNull = false;
+ }
+ else
+ return;
+
+ if (subQuery.isEmpty) {
+ this.isEmpty = true;
+ return;
+ }
+
+ if (classNum < 0) {
+ throw new IllegalArgumentException(
+ "Class number must be bigger than 0.");
+ }
+ this.classNum = classNum;
+ }
+
+ @Override
+ public SpanQuery toQuery() throws QueryException {
+
+ if (this.isNull() || this.isEmpty()) {
+ return null;
+ }
+
+ SpanQuery sq = subQuery.retrieveNode(this.retrieveNode).toQuery();
+ if (sq == null) return null;
+
+ return new SpanReferenceQuery(sq, classNum, true);
+ }
+
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/TestSpanReferenceQueryJSON.java b/src/test/java/de/ids_mannheim/korap/query/TestSpanReferenceQueryJSON.java
new file mode 100644
index 0000000..b8a21b8
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/TestSpanReferenceQueryJSON.java
@@ -0,0 +1,84 @@
+package de.ids_mannheim.korap.query;
+
+import static de.ids_mannheim.korap.TestSimple.getJSONQuery;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.apache.lucene.search.spans.SpanQuery;
+import org.junit.Test;
+
+import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
+import de.ids_mannheim.korap.util.QueryException;
+
+public class TestSpanReferenceQueryJSON {
+
+ @Test
+ public void testFirstOperandRef() throws IOException, QueryException {
+
+ String filepath = getClass().getResource(
+ "/queries/reference/first-operand-reference.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+
+ // 'cat="V" & cat="NP" & cat="PP" & #2 . #1 & #1 ->dep #3 &
+ // #3 . #2
+ assertEquals(
+ "spanReference(spanNext({3: tokens:p:P}, focus(2: focus(#[1,2]spanSegment(focus(#2: "
+ + "spanSegment(spanRelation(tokens:>:mate/d:HEAD), focus(1: spanNext("
+ + "{2: tokens:p:V}, {1: <tokens:c:NP />})))), {3: tokens:p:P})))), 3)",
+ sq.toString());
+ }
+
+ @Test
+ public void testSecondOperandRef() throws QueryException {
+
+ String filepath = getClass().getResource(
+ "/queries/reference/second-operand-reference.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+
+ // 'cat="V" & cat="NP" & cat="PP" & #2 . #1 & #1 ->dep #3 &
+ // #2 . #3
+ assertEquals(
+ "spanReference(spanNext(focus(2: focus(#[1,2]spanSegment(focus(#2: "
+ + "spanSegment(spanRelation(tokens:>:mate/d:HEAD), focus(1: spanNext("
+ + "{2: tokens:p:V}, {1: <tokens:c:NP />})))), {3: tokens:p:P}))), "
+ + "{3: tokens:p:P}), 3)", sq.toString());
+ }
+
+ @Test
+ public void testMultipleReferences() throws QueryException {
+ String filepath = getClass().getResource(
+ "/queries/reference/multiple-references.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+
+ // 'cat="VP" & cat="NP" & cat="PP" & #1 . #2 & #2 . #3 & #1 .
+ // #3 & #2 ->dep #1'
+ assertEquals(
+ "spanReference(focus(#[1,2]spanSegment(focus(#2: spanSegment(spanRelation(tokens:>:mate/d:HEAD), "
+ + "focus(2: spanReference(spanNext(focus(1: spanNext(focus(2: spanNext({1: <tokens:c:VP />}, "
+ + "{2: <tokens:c:NP />})), {3: <tokens:c:PP />})), {3: <tokens:c:PP />}), 3)))), "
+ + "{1: <tokens:c:VP />})), 1)",
+ sq.toString());
+ }
+
+ @Test
+ public void testDistanceReferences() throws QueryException {
+ String filepath = getClass().getResource(
+ // "/queries/reference/distance-reference.jsonld").getFile();
+ "/queries/reference/bug-multiple-distance.jsonld").getFile();
+ SpanQueryWrapper sqwi = getJSONQuery(filepath);
+ SpanQuery sq = sqwi.toQuery();
+
+ // 'cat="VP" & cat="NP" & cat="PP" & #1 . #2 & #2 . #3 & #1 .
+ // #3 & #2 ->dep #1'
+ assertEquals(
+ "spanReference(focus(#[1,2]spanSegment(focus(#2: spanSegment(spanRelation(tokens:>:stanford/d:tag), "
+ + "focus(2: spanDistance(focus(1: spanDistance(<tokens:c:vb />, {1: <tokens:c:prp />}, "
+ + "[(w[1:1], notOrdered, notExcluded)])), {2: <tokens:c:nn />}, [(w[0:2], ordered, notExcluded)])))), "
+ + "{1: <tokens:c:prp />})), 1)",
+ sq.toString());
+ }
+}
diff --git a/src/test/resources/queries/reference/bug-multiple-distance.jsonld b/src/test/resources/queries/reference/bug-multiple-distance.jsonld
new file mode 100644
index 0000000..4d59d1f
--- /dev/null
+++ b/src/test/resources/queries/reference/bug-multiple-distance.jsonld
@@ -0,0 +1,97 @@
+{
+ "query": {
+ "@type": "koral:group",
+ "operation": "operation:relation",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [2],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [1],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "koral:span",
+ "layer": "c",
+ "key": "vb",
+ "match": "match:eq"
+ },
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 1,
+ "operands": [{
+ "@type": "koral:span",
+ "layer": "c",
+ "key": "prp",
+ "match": "match:eq"
+ }]
+ }
+ ],
+ "inOrder": false,
+ "distances": [{
+ "@type": "koral:distance",
+ "key": "w",
+ "boundary": {
+ "@type": "koral:boundary",
+ "min": 1,
+ "max": 1
+ },
+ "min": 1,
+ "max": 1
+ }]
+ }]
+ },
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 2,
+ "operands": [{
+ "@type": "koral:span",
+ "layer": "c",
+ "key": "nn",
+ "match": "match:eq"
+ }]
+ }
+ ],
+ "distances": [{
+ "@type": "koral:distance",
+ "key": "w",
+ "boundary": {
+ "@type": "koral:boundary",
+ "min": 0,
+ "max": 2
+ },
+ "min": 0,
+ "max": 2
+ }],
+ "inOrder": true
+ }]
+ },
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [1]
+ }
+ ],
+ "relation": {
+ "@type": "koral:relation",
+ "wrap": {
+ "@type": "koral:term",
+ "foundry": "stanford",
+ "layer": "d",
+ "key":"tag"
+ }
+ }
+ },
+ "meta": {}
+}
diff --git a/src/test/resources/queries/reference/distance-multiple-references.jsonld b/src/test/resources/queries/reference/distance-multiple-references.jsonld
new file mode 100644
index 0000000..31ef098
--- /dev/null
+++ b/src/test/resources/queries/reference/distance-multiple-references.jsonld
@@ -0,0 +1,124 @@
+{
+ "query": {
+ "@type": "koral:group",
+ "operation": "operation:relation",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [1],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:relation",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [3],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [1],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 1,
+ "operands": [{
+ "@type": "koral:span",
+ "key": "vb",
+ "match": "match:eq"
+ }]
+ },
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 2,
+ "operands": [{
+ "@type": "koral:span",
+ "key": "prp",
+ "match": "match:eq"
+ }]
+ }
+ ],
+ "distances": [{
+ "@type": "koral:distance",
+ "key": "w",
+ "boundary": {
+ "@type": "koral:boundary",
+ "min": 0,
+ "max": 1
+ },
+ "min": 0,
+ "max": 1
+ }],
+ "inOrder": false
+ }]
+ },
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 3,
+ "operands": [{
+ "@type": "koral:span",
+ "key": "nn",
+ "match": "match:eq"
+ }]
+ }
+ ],
+ "distances": [{
+ "@type": "koral:distance",
+ "key": "w",
+ "boundary": {
+ "@type": "koral:boundary",
+ "min": 0,
+ "max": 2
+ },
+ "min": 0,
+ "max": 2
+ }],
+ "inOrder": false
+ }]
+ },
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [2]
+ }
+ ],
+ "relation": {
+ "@type": "koral:relation",
+ "wrap": {
+ "@type": "koral:term",
+ "foundry": "stanford",
+ "layer": "d",
+ "key":"tag"
+ }
+ }
+ }]
+ },
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [3]
+ }
+ ],
+ "relation": {
+ "@type": "koral:relation",
+ "wrap": {
+ "@type": "koral:term",
+ "foundry": "stanford",
+ "layer": "d",
+ "key":"tag"
+ }
+ }
+ },
+ "meta": {}
+}
diff --git a/src/test/resources/queries/reference/distance-reference.jsonld b/src/test/resources/queries/reference/distance-reference.jsonld
new file mode 100644
index 0000000..cf23596
--- /dev/null
+++ b/src/test/resources/queries/reference/distance-reference.jsonld
@@ -0,0 +1,99 @@
+{
+ "query": {
+ "@type": "koral:group",
+ "operation": "operation:relation",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [3],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [1],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 1,
+ "operands": [{
+ "@type": "koral:span",
+ "key": "vb",
+ "match": "match:eq"
+ }]
+ },
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 2,
+ "operands": [{
+ "@type": "koral:span",
+ "key": "prp",
+ "match": "match:eq"
+ }]
+ }
+ ],
+ "distances": [{
+ "@type": "koral:distance",
+ "key": "w",
+ "boundary": {
+ "@type": "koral:boundary",
+ "min": 0,
+ "max": 1
+ },
+ "min": 0,
+ "max": 1
+ }],
+ "inOrder": false
+ }]
+ },
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 3,
+ "operands": [{
+ "@type": "koral:span",
+ "key": "nn",
+ "match": "match:eq"
+ }]
+ }
+ ],
+ "distances": [{
+ "@type": "koral:distance",
+ "key": "w",
+ "boundary": {
+ "@type": "koral:boundary",
+ "min": 0,
+ "max": 2
+ },
+ "min": 0,
+ "max": 2
+ }],
+ "inOrder": false
+ }]
+ },
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [2]
+ }
+ ],
+ "relation": {
+ "@type": "koral:relation",
+ "wrap": {
+ "@type": "koral:term",
+ "foundry": "stanford",
+ "layer": "d",
+ "key":"tag"
+ }
+ }
+ },
+ "meta": {}
+}
diff --git a/src/test/resources/queries/reference/first-operand-reference.jsonld b/src/test/resources/queries/reference/first-operand-reference.jsonld
new file mode 100644
index 0000000..44792db
--- /dev/null
+++ b/src/test/resources/queries/reference/first-operand-reference.jsonld
@@ -0,0 +1,86 @@
+{
+ "query": {
+ "@type": "koral:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [3]
+ },
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [2],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:relation",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [1],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 2,
+ "operands": [{
+ "@type": "koral:token",
+ "wrap": {
+ "@type": "koral:term",
+ "layer": "p",
+ "key": "V",
+ "match": "match:eq"
+ }
+ }]
+ },
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 1,
+ "operands": [{
+ "@type": "koral:span",
+ "layer": "c",
+ "key": "NP",
+ "match": "match:eq"
+ }]
+ }
+ ],
+ "inOrder": true
+ }]
+ },
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 3,
+ "operands": [{
+ "@type": "koral:token",
+ "wrap": {
+ "@type": "koral:term",
+ "layer": "p",
+ "key": "P",
+ "match": "match:eq"
+ }
+ }]
+ }
+ ],
+ "relation": {
+ "@type": "koral:relation",
+ "wrap": {
+ "@type": "koral:term",
+ "foundry": "mate",
+ "layer": "d",
+ "key" : "HEAD"
+ }
+ }
+ }]
+ }
+ ],
+ "inOrder": true
+ },
+ "meta": {}
+}
diff --git a/src/test/resources/queries/reference/multiple-references.jsonld b/src/test/resources/queries/reference/multiple-references.jsonld
new file mode 100644
index 0000000..f94cfb5
--- /dev/null
+++ b/src/test/resources/queries/reference/multiple-references.jsonld
@@ -0,0 +1,97 @@
+{
+ "query": {
+ "@type": "koral:group",
+ "operation": "operation:relation",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [2],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [1],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [2],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 1,
+ "operands": [{
+ "@type": "koral:span",
+ "layer": "c",
+ "key": "VP",
+ "match": "match:eq"
+ }]
+ },
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 2,
+ "operands": [{
+ "@type": "koral:span",
+ "layer": "c",
+ "key": "NP",
+ "match": "match:eq"
+ }]
+ }
+ ],
+ "inOrder": true
+ }]
+ },
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 3,
+ "operands": [{
+ "@type": "koral:span",
+ "layer": "c",
+ "key": "PP",
+ "match": "match:eq"
+ }]
+ }
+ ],
+ "inOrder": true
+ }]
+ },
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [3]
+ }
+ ],
+ "inOrder": true
+ }]
+ },
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [1]
+ }
+ ],
+ "relation": {
+ "@type": "koral:relation",
+ "wrap": {
+ "@type": "koral:term",
+ "foundry" : "mate",
+ "layer": "d",
+ "key" : "HEAD"
+ }
+ }
+ },
+ "meta": {}
+}
diff --git a/src/test/resources/queries/reference/second-operand-reference.jsonld b/src/test/resources/queries/reference/second-operand-reference.jsonld
new file mode 100644
index 0000000..47388cc
--- /dev/null
+++ b/src/test/resources/queries/reference/second-operand-reference.jsonld
@@ -0,0 +1,86 @@
+{
+ "query": {
+ "@type": "koral:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [2],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:relation",
+ "operands": [
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [1],
+ "operands": [{
+ "@type": "koral:group",
+ "operation": "operation:sequence",
+ "operands": [
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 2,
+ "operands": [{
+ "@type": "koral:token",
+ "wrap": {
+ "@type": "koral:term",
+ "layer": "p",
+ "key": "V",
+ "match": "match:eq"
+ }
+ }]
+ },
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 1,
+ "operands": [{
+ "@type": "koral:span",
+ "layer": "c",
+ "key": "NP",
+ "match": "match:eq"
+ }]
+ }
+ ],
+ "inOrder": true
+ }]
+ },
+ {
+ "@type": "koral:group",
+ "operation": "operation:class",
+ "classOut": 3,
+ "operands": [{
+ "@type": "koral:token",
+ "wrap": {
+ "@type": "koral:term",
+ "layer": "p",
+ "key": "P",
+ "match": "match:eq"
+ }
+ }]
+ }
+ ],
+ "relation": {
+ "@type": "koral:relation",
+ "wrap": {
+ "@type": "koral:term",
+ "foundry": "mate",
+ "layer": "d",
+ "key" : "HEAD"
+ }
+ }
+ }]
+ },
+ {
+ "@type": "koral:reference",
+ "operation": "operation:focus",
+ "classRef": [3]
+ }
+ ],
+ "inOrder": true
+ },
+ "meta": {}
+}