diff --git a/annis-service/src/main/antlr4/annis/ql/AqlLexer.g4 b/annis-service/src/main/antlr4/annis/ql/AqlLexer.g4 index c7270e774f..039bf13b8b 100644 --- a/annis-service/src/main/antlr4/annis/ql/AqlLexer.g4 +++ b/annis-service/src/main/antlr4/annis/ql/AqlLexer.g4 @@ -58,16 +58,16 @@ DOUBLECOLON:'::'; WS : ( ' ' | '\t' | '\r' | '\n' )+ -> skip ; -ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_'|'-')* - ; - +VAR_DEF + : ('a'..'z'|'A'..'Z') ( '0' .. '9'|'a'..'z'|'A'..'Z')* '#' + ; + REF : '#' ( '0' .. '9'|'a'..'z'|'A'..'Z')+ ; -VAR_DEF - : ('a'..'z'|'A'..'Z') ( '0' .. '9'|'a'..'z'|'A'..'Z')* '#' - ; +ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_'|'-')* + ; DIGITS : ('0'..'9')+; diff --git a/annis-service/src/main/antlr4/annis/ql/AqlParser.g4 b/annis-service/src/main/antlr4/annis/ql/AqlParser.g4 index c9eff6642d..5fa717459e 100644 --- a/annis-service/src/main/antlr4/annis/ql/AqlParser.g4 +++ b/annis-service/src/main/antlr4/annis/ql/AqlParser.g4 @@ -51,11 +51,16 @@ edgeSpec : BRACKET_OPEN edgeAnno+ BRACKET_CLOSE ; +refOrNode + : REF # ReferenceRef + | VAR_DEF? variableExpr # ReferenceNode + ; + precedence - : left=REF PRECEDENCE (layer=ID)? right=REF # DirectPrecedence - | left=REF PRECEDENCE (layer=ID)? STAR right=REF # IndirectPrecedence - | left=REF PRECEDENCE (layer=ID COMMA?)? rangeSpec right=REF #RangePrecedence + : left=refOrNode PRECEDENCE (layer=ID)? right=refOrNode # DirectPrecedence + | left=refOrNode PRECEDENCE (layer=ID)? STAR right=refOrNode # IndirectPrecedence + | left=refOrNode PRECEDENCE (layer=ID COMMA?)? rangeSpec right=refOrNode #RangePrecedence ; dominance @@ -106,8 +111,8 @@ variableExpr ; expr - : VAR_DEF variableExpr # VariableTermExpr - | variableExpr # NoVariableTermExpr + : VAR_DEF variableExpr # NamedVariableTermExpr + | variableExpr # VariableTermExpr | unary_linguistic_term # UnaryTermExpr | binary_linguistic_term # BinaryTermExpr | META DOUBLECOLON id=qName op=EQ txt=textSpec # MetaTermExpr diff --git a/annis-service/src/main/java/annis/ql/parser/AnnisParserAntlr.java b/annis-service/src/main/java/annis/ql/parser/AnnisParserAntlr.java index 45de2d4e90..c2db26480c 100644 --- a/annis-service/src/main/java/annis/ql/parser/AnnisParserAntlr.java +++ b/annis-service/src/main/java/annis/ql/parser/AnnisParserAntlr.java @@ -96,7 +96,8 @@ public QueryData parse(String aql, List corpusList) data.setCorpusList(corpusList); data.addMetaAnnotations(nodeListener.getMetaData()); - JoinListener joinListener = new JoinListener(data, precedenceBound); + JoinListener joinListener = new JoinListener(data, precedenceBound, + nodeListener.getTokenPositionToNode()); walker.walk(joinListener, treeDNF); if (postProcessors != null) diff --git a/annis-service/src/main/java/annis/ql/parser/JoinListener.java b/annis-service/src/main/java/annis/ql/parser/JoinListener.java index 326ae98d8e..6a219121cb 100644 --- a/annis-service/src/main/java/annis/ql/parser/JoinListener.java +++ b/annis-service/src/main/java/annis/ql/parser/JoinListener.java @@ -38,14 +38,17 @@ import annis.sqlgen.model.Sibling; import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.misc.Interval; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,17 +66,22 @@ public class JoinListener extends AqlParserBaseListener * Each entry maps node variables to a collection of query nodes. */ private final Multimap[] alternativeNodes; + /** Maps a token interval to a query nodes. + */ + private final Map tokenPositionToNode; private int alternativeIndex; /** * Constructor. * @param data The {@link QueryData} containing the already parsed nodes. * @param precedenceBound maximal range of precedence + * @param tokenPositionToNode maps a token interval to a query nodes */ - public JoinListener(QueryData data, int precedenceBound) + public JoinListener(QueryData data, int precedenceBound, Map tokenPositionToNode) { this.precedenceBound = precedenceBound; this.alternativeNodes = new Multimap[data.getAlternatives().size()]; + this.tokenPositionToNode = tokenPositionToNode; int i=0; for(List alternative : data.getAlternatives()) @@ -143,8 +151,8 @@ public void enterTokenArityTerm(AqlParser.TokenArityTermContext ctx) public void enterDirectPrecedence( AqlParser.DirectPrecedenceContext ctx) { - Collection nodesLeft = nodesByRef(ctx.left); - Collection nodesRight = nodesByRef(ctx.right); + Collection nodesLeft = nodes(ctx.left); + Collection nodesRight = nodes(ctx.right); Preconditions.checkArgument(!nodesLeft.isEmpty(), errorLHS("precendence") + ": " + ctx.getText()); Preconditions.checkArgument(!nodesRight.isEmpty(), errorRHS("precendence") @@ -164,12 +172,13 @@ public void enterDirectPrecedence( } } + @Override public void enterIndirectPrecedence( AqlParser.IndirectPrecedenceContext ctx) { - Collection nodesLeft = nodesByRef(ctx.left); - Collection nodesRight = nodesByRef(ctx.right); + Collection nodesLeft = nodes(ctx.left); + Collection nodesRight = nodes(ctx.right); Preconditions.checkArgument(!nodesLeft.isEmpty(), errorLHS("precendence") + ": " + ctx.getText()); Preconditions.checkNotNull(!nodesRight.isEmpty(), errorRHS("precendence") @@ -201,8 +210,8 @@ public void enterIndirectPrecedence( @Override public void enterRangePrecedence(AqlParser.RangePrecedenceContext ctx) { - Collection nodesLeft = nodesByRef(ctx.left); - Collection nodesRight = nodesByRef(ctx.right); + Collection nodesLeft = nodes(ctx.left); + Collection nodesRight = nodes(ctx.right); Preconditions.checkNotNull(!nodesLeft.isEmpty(), errorLHS("precendence") + ": " + ctx.getText()); Preconditions.checkArgument(!nodesRight.isEmpty(), errorRHS("precendence") @@ -573,6 +582,45 @@ private LinkedList fromEdgeAnnotation( return annos; } + private Collection nodes(AqlParser.RefOrNodeContext ctx) + { + if(ctx instanceof AqlParser.ReferenceNodeContext) + { + return nodesByDef((AqlParser.ReferenceNodeContext) ctx); + } + else if(ctx instanceof AqlParser.ReferenceRefContext) + { + return nodesByRef(((AqlParser.ReferenceRefContext) ctx).REF().getSymbol()); + } + else + { + return new LinkedList(); + } + } + + private Collection nodesByDef(AqlParser.ReferenceNodeContext ctx) + { + if(ctx.VAR_DEF() == null) + { + QueryNode result = tokenPositionToNode.get(ctx.variableExpr().getSourceInterval()); + if(result == null) + { + return new LinkedList(); + } + else + { + return Lists.newArrayList(result); + } + } + else + { + String varDefText = ctx.VAR_DEF().getText(); + // remove trailing # + varDefText = varDefText.substring(0, varDefText.length()-1); + return alternativeNodes[alternativeIndex].get(varDefText); + } + } + private Collection nodesByRef(Token ref) { return alternativeNodes[alternativeIndex].get("" + ref.getText().substring(1)); diff --git a/annis-service/src/main/java/annis/ql/parser/QueryNodeListener.java b/annis-service/src/main/java/annis/ql/parser/QueryNodeListener.java index 0af53650c1..748575cd0d 100644 --- a/annis-service/src/main/java/annis/ql/parser/QueryNodeListener.java +++ b/annis-service/src/main/java/annis/ql/parser/QueryNodeListener.java @@ -48,7 +48,7 @@ public class QueryNodeListener extends AqlParserBaseListener private String lastVariableDefinition = null; private final Multimap localNodes = HashMultimap.create(); - private final Map tokenposition2NodeID = Maps.newHashMap(); + private final Map tokenPositionToNode = Maps.newHashMap(); private final List metaData = new ArrayList(); @@ -162,7 +162,7 @@ public void enterMetaTermExpr(AqlParser.MetaTermExprContext ctx) } @Override - public void enterVariableTermExpr(AqlParser.VariableTermExprContext ctx) + public void enterNamedVariableTermExpr(AqlParser.NamedVariableTermExprContext ctx) { lastVariableDefinition = null; if(ctx != null) @@ -179,6 +179,28 @@ public void enterVariableTermExpr(AqlParser.VariableTermExprContext ctx) } } } + + @Override + public void enterReferenceNode(AqlParser.ReferenceNodeContext ctx) + { + if(ctx != null && ctx.VAR_DEF() != null) + { + lastVariableDefinition = null; + + String text = ctx.VAR_DEF().getText(); + // remove the trailing "#" + if(text.endsWith("#")) + { + lastVariableDefinition = text.substring(0, text.length()-1); + } + else + { + lastVariableDefinition = text; + } + + } + } + @@ -215,14 +237,10 @@ else if (txt instanceof AqlParser.RegexTextSpecContext) return null; } - private Collection nodesByRef(Token ref) - { - return localNodes.get("" + ref.getText().substring(1)); - } - private QueryNode newNode(ParserRuleContext ctx) { - Long existingID = tokenposition2NodeID.get(ctx.getSourceInterval()); + QueryNode existingNode = tokenPositionToNode.get(ctx.getSourceInterval()); + Long existingID = existingNode == null ? null : existingNode.getId(); if(existingID == null) { @@ -242,9 +260,17 @@ private QueryNode newNode(ParserRuleContext ctx) currentAlternative.add(n); localNodes.put(n.getVariable(), n); - tokenposition2NodeID.put(ctx.getSourceInterval(), n.getId()); + tokenPositionToNode.put(ctx.getSourceInterval(), n); return n; } + + public Map getTokenPositionToNode() + { + return tokenPositionToNode; + } + + + } diff --git a/annis-service/src/test/java/annis/ql/parser/TestAnnisParser-examples.xml b/annis-service/src/test/java/annis/ql/parser/TestAnnisParser-examples.xml index 4ae29b265e..a7a15b2e99 100644 --- a/annis-service/src/test/java/annis/ql/parser/TestAnnisParser-examples.xml +++ b/annis-service/src/test/java/annis/ql/parser/TestAnnisParser-examples.xml @@ -7,6 +7,10 @@ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd"> + + + +