[NO-ISSUE][GRAPHIX] Adding a pure-rewrite version of Graphix.
Details:
- Users can create and delete managed graphs w/ CREATE GRAPH and DROP
GRAPH. These will raise an error if a user tries to drop one of their
dependents (and vice-versa).
- Users can introduce a set of variable bindings before UNNEST and JOIN
clauses using the MATCH clause, which will iterate over all "matched"
graph patterns. The MATCH clause also includes a "LEFT" variant.
- Graph edge patterns can be formulated as path finding queries, where a
user can specify the range of hops between the two vertices of the edge
pattern.
- Labels and directions can be inferred using labels and directions of
vertices within the same FROM-GRAPH-CLAUSE. A naive evaluation
strategy is used here (until we reach a fixed point).
- The initial set of Graphix functions are included.
Change-Id: I50f032ea4acc5ba46b86ae1052590a3e945c2497
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb-graph/+/16103
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Glenn Galvizo <ggalvizo@uci.edu>
diff --git a/asterix-graphix/src/main/resources/lang-extension/lang.txt b/asterix-graphix/src/main/resources/lang-extension/lang.txt
index 6d215f0..12643bf 100644
--- a/asterix-graphix/src/main/resources/lang-extension/lang.txt
+++ b/asterix-graphix/src/main/resources/lang-extension/lang.txt
@@ -17,11 +17,24 @@
// under the License.
//
+import java.util.HashSet;
+import java.util.Set;
+
import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier;
+import org.apache.asterix.graphix.lang.clause.FromGraphClause;
+import org.apache.asterix.graphix.lang.clause.GraphSelectBlock;
+import org.apache.asterix.graphix.lang.clause.MatchClause;
+import org.apache.asterix.graphix.lang.expression.EdgePatternExpr;
import org.apache.asterix.graphix.lang.expression.GraphConstructor;
+import org.apache.asterix.graphix.lang.expression.IGraphExpr;
+import org.apache.asterix.graphix.lang.expression.PathPatternExpr;
+import org.apache.asterix.graphix.lang.expression.VertexPatternExpr;
+import org.apache.asterix.graphix.lang.optype.MatchType;
import org.apache.asterix.graphix.lang.statement.CreateGraphStatement;
import org.apache.asterix.graphix.lang.statement.GraphDropStatement;
import org.apache.asterix.graphix.lang.statement.GraphElementDecl;
+import org.apache.asterix.graphix.lang.struct.EdgeDescriptor;
+import org.apache.asterix.graphix.lang.struct.ElementLabel;
import org.apache.asterix.lang.sqlpp.parser.ParseException;
import org.apache.asterix.lang.sqlpp.parser.SqlppParseException;
import org.apache.asterix.lang.sqlpp.parser.Token;
@@ -45,6 +58,102 @@
});
}
+@override
+SelectBlock SelectBlock() throws ParseException:
+{
+ SelectClause selectClause = null;
+ FromClause fromClause = null;
+ FromGraphClause fromGraphClause = null;
+ List<LetClause> fromLetClauses = null;
+ WhereClause whereClause = null;
+ GroupbyClause groupbyClause = null;
+ List<LetClause> gbyLetClauses = null;
+ HavingClause havingClause = null;
+ SourceLocation startSrcLoc = null;
+
+ List<AbstractClause> fromLetWhereClauses = new ArrayList<AbstractClause>();
+ List<AbstractClause> gbyLetHavingClauses = new ArrayList<AbstractClause>();
+}
+{
+ (
+ (
+ selectClause = SelectClause() { startSrcLoc = selectClause.getSourceLocation(); }
+ (
+ (
+ ( LOOKAHEAD(2)
+ fromGraphClause = FromGraphClause()
+ | fromClause = FromClause()
+ )
+ ( fromLetClauses = LetClause() )?
+ ( whereClause = WhereClause() )?
+ ( groupbyClause = GroupbyClause()
+ ( gbyLetClauses = LetClause() )?
+ ( havingClause = HavingClause() )? )?
+ )
+ |
+ ( fromLetClauses = LetClause()
+ {
+ // LET without FROM -> create dummy FROM clause: FROM {{missing}} AS #0
+ SourceLocation sourceLoc = getSourceLocation(token);
+ LiteralExpr missingExpr = new LiteralExpr(MissingLiteral.INSTANCE);
+ missingExpr.setSourceLocation(sourceLoc);
+ List<Expression> list = new ArrayList<Expression>(1);
+ list.add(missingExpr);
+ ListConstructor listExpr = new ListConstructor(ListConstructor.Type.ORDERED_LIST_CONSTRUCTOR, list);
+ listExpr.setSourceLocation(sourceLoc);
+ List<FromTerm> fromTerms = new ArrayList<FromTerm>(1);
+ VariableExpr fromVar = new VariableExpr(new VarIdentifier("#0"));
+ fromVar.setSourceLocation(sourceLoc);
+ fromTerms.add(new FromTerm(listExpr, fromVar, null, new ArrayList<AbstractBinaryCorrelateClause>()));
+ fromClause = new FromClause(fromTerms);
+ }
+ ( whereClause = WhereClause() )?
+ )
+ )?
+ )
+ |
+ (
+ ( LOOKAHEAD(2)
+ fromGraphClause = FromGraphClause() { startSrcLoc = fromGraphClause.getSourceLocation(); }
+ | fromClause = FromClause() { startSrcLoc = fromClause.getSourceLocation(); }
+ )
+ ( fromLetClauses = LetClause() )?
+ ( whereClause = WhereClause() )?
+ ( groupbyClause = GroupbyClause()
+ ( gbyLetClauses = LetClause() )?
+ ( havingClause = HavingClause() )? )?
+ selectClause = SelectClause()
+ )
+ )
+ {
+ if (fromLetClauses != null) {
+ fromLetWhereClauses.addAll(fromLetClauses);
+ }
+ if (whereClause != null) {
+ fromLetWhereClauses.add(whereClause);
+ }
+ if (gbyLetClauses != null) {
+ gbyLetHavingClauses.addAll(gbyLetClauses);
+ }
+ if (havingClause != null) {
+ gbyLetHavingClauses.add(havingClause);
+ }
+
+ if (fromClause != null) {
+ SelectBlock selectBlock = new SelectBlock(selectClause, fromClause, fromLetWhereClauses,
+ groupbyClause, gbyLetHavingClauses);
+ selectBlock.setSourceLocation(startSrcLoc);
+ return selectBlock;
+
+ } else {
+ GraphSelectBlock selectBlock = new GraphSelectBlock(selectClause, fromGraphClause,
+ fromLetWhereClauses, groupbyClause, gbyLetHavingClauses);
+ selectBlock.setSourceLocation(startSrcLoc);
+ return selectBlock;
+ }
+ }
+}
+
@merge
Statement CreateStatement() throws ParseException:
{
@@ -176,36 +285,36 @@
@new
GraphConstructor GraphConstructor(Token startStmtToken) throws ParseException:
{
- List<GraphConstructor.VertexElement> vertexElements = new ArrayList<GraphConstructor.VertexElement>();
- List<GraphConstructor.EdgeElement> edgeElements = new ArrayList<GraphConstructor.EdgeElement>();
- GraphConstructor.VertexElement vertexElement = null;
- GraphConstructor.EdgeElement edgeElement = null;
+ List<GraphConstructor.VertexConstructor> vertexConstructors = new ArrayList<GraphConstructor.VertexConstructor>();
+ List<GraphConstructor.EdgeConstructor> edgeConstructors = new ArrayList<GraphConstructor.EdgeConstructor>();
+ GraphConstructor.VertexConstructor vertexConstructor = null;
+ GraphConstructor.EdgeConstructor edgeConstructor = null;
}
{
- vertexElement = GraphVertexSpecification(startStmtToken) { vertexElements.add(vertexElement); }
+ vertexConstructor = GraphVertexSpecification(startStmtToken) { vertexConstructors.add(vertexConstructor); }
( <COMMA>
(
- ( vertexElement = GraphVertexSpecification(token) { vertexElements.add(vertexElement); } )
- | ( edgeElement = GraphEdgeSpecification(token) { edgeElements.add(edgeElement); } )
+ ( vertexConstructor = GraphVertexSpecification(token) { vertexConstructors.add(vertexConstructor); } )
+ | ( edgeConstructor = GraphEdgeSpecification(token) { edgeConstructors.add(edgeConstructor); } )
)
)*
{
- GraphConstructor graphConstructor = new GraphConstructor(vertexElements, edgeElements);
+ GraphConstructor graphConstructor = new GraphConstructor(vertexConstructors, edgeConstructors);
return addSourceLocation(graphConstructor, startStmtToken);
}
}
@new
-GraphConstructor.VertexElement GraphVertexSpecification(Token startStmtToken) throws ParseException:
+GraphConstructor.VertexConstructor GraphVertexSpecification(Token startStmtToken) throws ParseException:
{
Pair<List<Integer>, List<List<String>>> primaryKeyFields;
Token beginPos = null, endPos = null;
Expression vertexDefinitionExpr;
- String vertexName;
+ ElementLabel vertexLabel;
}
{
<VERTEX>
- vertexName = GraphVertexDefinitionPattern()
+ vertexLabel = GraphVertexDefinitionPattern()
<PRIMARY> <KEY> <LEFTPAREN> primaryKeyFields = KeyFields() <RIGHTPAREN>
<AS>
{
@@ -213,91 +322,74 @@
createNewScope();
}
(
- vertexDefinitionExpr = ViewBody()
- | <LEFTPAREN> vertexDefinitionExpr = ViewBody() <RIGHTPAREN>
+ vertexDefinitionExpr = ViewBody() { endPos = token; }
+ | <LEFTPAREN> { beginPos = token; } vertexDefinitionExpr = ViewBody() { endPos = token; } <RIGHTPAREN>
)
{
- endPos = token;
String vDef = extractFragment(beginPos.beginLine, beginPos.beginColumn + 1, endPos.endLine, endPos.endColumn + 1);
removeCurrentScope();
- GraphConstructor.VertexElement vertexElement = new GraphConstructor.VertexElement(vertexName,
+ GraphConstructor.VertexConstructor vertexConstructor = new GraphConstructor.VertexConstructor(vertexLabel,
primaryKeyFields.second, primaryKeyFields.first, vertexDefinitionExpr, vDef);
- return addSourceLocation(vertexElement, startStmtToken);
+ return addSourceLocation(vertexConstructor, startStmtToken);
}
}
@new
-String GraphVertexDefinitionPattern() throws ParseException:
+ElementLabel GraphVertexDefinitionPattern() throws ParseException:
{
String vertexName;
}
{
<LEFTPAREN> <COLON> vertexName = Identifier() <RIGHTPAREN>
{
- return vertexName;
+ return new ElementLabel(vertexName);
}
}
@new
-GraphConstructor.EdgeElement GraphEdgeSpecification(Token startStmtToken) throws ParseException:
+GraphConstructor.EdgeConstructor GraphEdgeSpecification(Token startStmtToken) throws ParseException:
{
- Pair<Triple<String, String, String>, Boolean> edgeDefinitionPattern;
+ Pair<Triple<ElementLabel, ElementLabel, ElementLabel>, Boolean> edgeDefinitionPattern;
Pair<List<Integer>, List<List<String>>> keyFields;
Token beginPos = null, endPos = null;
Expression edgeDefinitionExpr = null;
List<Integer> destinationKeySourceIndicators = null;
List<Integer> sourceKeySourceIndicators = null;
- List<Integer> primaryKeySourceIndicators = null;
List<List<String>> destinationKeyFields = null;
List<List<String>> sourceKeyFields = null;
- List<List<String>> primaryKeyFields = null;
}
{
<EDGE>
edgeDefinitionPattern = GraphEdgeDefinitionPattern()
(
+ <SOURCE> <KEY> <LEFTPAREN> keyFields = KeyFields() <RIGHTPAREN>
+ {
+ sourceKeyFields = keyFields.second;
+ sourceKeySourceIndicators = keyFields.first;
+ }
+ <DESTINATION> <KEY> <LEFTPAREN> keyFields = KeyFields() <RIGHTPAREN>
+ {
+ destinationKeyFields = keyFields.second;
+ destinationKeySourceIndicators = keyFields.first;
+ }
+ <AS>
+ {
+ beginPos = token;
+ createNewScope();
+ }
(
- <PRIMARY> <KEY> <LEFTPAREN> keyFields = KeyFields() <RIGHTPAREN>
- {
- primaryKeyFields = keyFields.second;
- primaryKeySourceIndicators = keyFields.first;
- }
- <SOURCE> <KEY> <LEFTPAREN> keyFields = KeyFields() <RIGHTPAREN>
- {
- sourceKeyFields = keyFields.second;
- sourceKeySourceIndicators = keyFields.first;
- }
- <DESTINATION> <KEY> <LEFTPAREN> keyFields = KeyFields() <RIGHTPAREN>
- {
- destinationKeyFields = keyFields.second;
- destinationKeySourceIndicators = keyFields.first;
- }
- <AS>
- {
- beginPos = token;
- createNewScope();
- }
- (
- edgeDefinitionExpr = ViewBody()
- | <LEFTPAREN> edgeDefinitionExpr = ViewBody() <RIGHTPAREN>
- )
- )
- |
- (
- <DESTINATION> <KEY> <LEFTPAREN> keyFields = KeyFields() <RIGHTPAREN>
- {
- destinationKeyFields = keyFields.second;
- destinationKeySourceIndicators = keyFields.first;
- }
+ edgeDefinitionExpr = SelectExpression(true) { endPos = token; }
+ | <LEFTPAREN> { beginPos = token; } edgeDefinitionExpr = SelectExpression(true) { endPos = token; } <RIGHTPAREN>
)
)
{
- String destinationLabel, edgeLabel, sourceLabel;
+ ElementLabel destinationLabel, edgeLabel, sourceLabel;
if (edgeDefinitionPattern.second) { // isDirectedLeft
sourceLabel = edgeDefinitionPattern.first.third;
edgeLabel = edgeDefinitionPattern.first.second;
destinationLabel = edgeDefinitionPattern.first.first;
+
} else {
sourceLabel = edgeDefinitionPattern.first.first;
edgeLabel = edgeDefinitionPattern.first.second;
@@ -306,32 +398,285 @@
String eDef = null;
if (edgeDefinitionExpr != null) {
- endPos = token;
eDef = extractFragment(beginPos.beginLine, beginPos.beginColumn + 1, endPos.endLine, endPos.endColumn + 1);
removeCurrentScope();
}
- GraphConstructor.EdgeElement edgeElement = new GraphConstructor.EdgeElement(edgeLabel, destinationLabel,
- sourceLabel, primaryKeyFields, primaryKeySourceIndicators, destinationKeyFields, destinationKeySourceIndicators,
- sourceKeyFields, sourceKeySourceIndicators, edgeDefinitionExpr, eDef);
- return addSourceLocation(edgeElement, startStmtToken);
+ GraphConstructor.EdgeConstructor edgeConstructor = new GraphConstructor.EdgeConstructor(edgeLabel, destinationLabel,
+ sourceLabel, destinationKeyFields, destinationKeySourceIndicators, sourceKeyFields, sourceKeySourceIndicators,
+ edgeDefinitionExpr, eDef);
+ return addSourceLocation(edgeConstructor, startStmtToken);
}
}
@new
-Pair<Triple<String, String, String>, Boolean> GraphEdgeDefinitionPattern() throws ParseException:
+Pair<Triple<ElementLabel, ElementLabel, ElementLabel>, Boolean> GraphEdgeDefinitionPattern() throws ParseException:
{
- String leftVertexName, edgeName, rightVertexName;
+ ElementLabel leftVertexLabel, rightVertexLabel;
boolean isDirectedLeft;
+ String edgeName;
}
{
- leftVertexName = GraphVertexDefinitionPattern()
+ leftVertexLabel = GraphVertexDefinitionPattern()
( <MINUS> <LEFTBRACKET> <COLON> edgeName = Identifier() <RIGHTBRACKET> <MINUS> <GT> { isDirectedLeft = false; }
| <LT> <MINUS> <LEFTBRACKET> <COLON> edgeName = Identifier() <RIGHTBRACKET> <MINUS> { isDirectedLeft = true; } )
- rightVertexName = GraphVertexDefinitionPattern()
+ rightVertexLabel = GraphVertexDefinitionPattern()
{
- Triple<String, String, String> t = new Triple<String, String, String>(leftVertexName, edgeName, rightVertexName);
- return new Pair<Triple<String, String, String>, Boolean>(t, isDirectedLeft);
+ Triple<ElementLabel, ElementLabel, ElementLabel> t = new Triple<ElementLabel, ElementLabel, ElementLabel>(
+ leftVertexLabel, new ElementLabel(edgeName), rightVertexLabel);
+ return new Pair<Triple<ElementLabel, ElementLabel, ElementLabel>, Boolean>(t, isDirectedLeft);
+ }
+}
+
+@new
+FromGraphClause FromGraphClause() throws ParseException:
+{
+ Token startToken = null;
+ GraphConstructor graphConstructor = null;
+ Pair<DataverseName, Identifier> nameComponents = null;
+ AbstractBinaryCorrelateClause correlateClause = null;
+
+ List<MatchClause> matchClauses = new ArrayList<MatchClause>();
+ List<PathPatternExpr> pathPatternExpressions = null;
+ List<AbstractBinaryCorrelateClause> correlateClauses = new ArrayList<AbstractBinaryCorrelateClause>();
+}
+{
+ <FROM> <GRAPH> { startToken = token; }
+ (
+ graphConstructor = GraphConstructor(token)
+ | nameComponents = QualifiedName()
+ )
+ <MATCH> pathPatternExpressions = PathPatternExpressions()
+ { matchClauses.add(new MatchClause(pathPatternExpressions, MatchType.LEADING)); }
+ ( LOOKAHEAD(3) // We want to avoid getting confused with the correlated clauses below.
+ (
+ <LEFT> ( <OUTER> )? <MATCH> pathPatternExpressions = PathPatternExpressions()
+ { matchClauses.add(new MatchClause(pathPatternExpressions, MatchType.LEFTOUTER)); }
+ |
+ ( <INNER> )? <MATCH> pathPatternExpressions = PathPatternExpressions()
+ { matchClauses.add(new MatchClause(pathPatternExpressions, MatchType.INNER)); }
+ )
+ )*
+ (
+ (
+ correlateClause = JoinOrUnnestClause(JoinType.INNER, UnnestType.INNER)
+ | ( <INNER> correlateClause = JoinOrUnnestClause(JoinType.INNER, UnnestType.INNER) )
+ | ( <LEFT> ( <OUTER> )? correlateClause = JoinOrUnnestClause(JoinType.LEFTOUTER, UnnestType.LEFTOUTER) )
+ | ( <RIGHT> ( <OUTER> )? correlateClause = JoinClause(JoinType.RIGHTOUTER) )
+ | ( <CROSS> correlateClause = CrossJoinClause() )
+ )
+ {
+ correlateClauses.add(correlateClause);
+ }
+ )*
+ {
+ FromGraphClause fromGraphClause;
+ if (graphConstructor == null) {
+ fromGraphClause = new FromGraphClause(nameComponents.first, nameComponents.second,
+ matchClauses, correlateClauses);
+
+ } else {
+ fromGraphClause = new FromGraphClause(graphConstructor, matchClauses, correlateClauses);
+ }
+ return addSourceLocation(fromGraphClause, startToken);
+ }
+}
+
+@new
+List<PathPatternExpr> PathPatternExpressions() throws ParseException:
+{
+ List<PathPatternExpr> pathPatternExpressions = new ArrayList<PathPatternExpr>();
+ PathPatternExpr pathPattern = null;
+ VariableExpr variableExpr = null;
+}
+{
+ pathPattern = PathPatternExpression() { pathPatternExpressions.add(pathPattern); }
+ (
+ ( <AS> )? variableExpr = Variable()
+ {
+ int index = pathPatternExpressions.size() - 1;
+ pathPatternExpressions.get(index).setVariableExpr(variableExpr);
+ }
+ )?
+ ( LOOKAHEAD(2)
+ <COMMA> pathPattern = PathPatternExpression() { pathPatternExpressions.add(pathPattern); }
+ (
+ ( <AS> )? variableExpr = Variable()
+ {
+ int index = pathPatternExpressions.size() - 1;
+ pathPatternExpressions.get(index).setVariableExpr(variableExpr);
+ }
+ )?
+ )*
+ {
+ return pathPatternExpressions;
+ }
+}
+
+@new
+PathPatternExpr PathPatternExpression() throws ParseException:
+{
+ List<VertexPatternExpr> orderedVertexExpressions = new ArrayList<VertexPatternExpr>();
+ List<EdgePatternExpr> orderedEdgeExpressions = new ArrayList<EdgePatternExpr>();
+
+ Token startToken = null, edgeStartToken = null;
+ VertexPatternExpr vertexExpr = null;
+ EdgeDescriptor edgeDescriptor = null;
+}
+{
+ vertexExpr = VertexPatternExpression()
+ {
+ startToken = token;
+ orderedVertexExpressions.add(vertexExpr);
+ }
+ (
+ edgeDescriptor = EdgeDescriptor() { edgeStartToken = token; }
+ vertexExpr = VertexPatternExpression()
+ {
+ VertexPatternExpr leftVertex = orderedVertexExpressions.get(orderedVertexExpressions.size() - 1);
+ EdgePatternExpr edgePattern = new EdgePatternExpr(leftVertex, vertexExpr, edgeDescriptor);
+ orderedEdgeExpressions.add(addSourceLocation(edgePattern, edgeStartToken));
+ orderedVertexExpressions.add(vertexExpr);
+ }
+ )*
+ {
+ PathPatternExpr pathPattern = new PathPatternExpr(orderedVertexExpressions, orderedEdgeExpressions, null);
+ return addSourceLocation(pathPattern, startToken);
+ }
+}
+
+@new
+VertexPatternExpr VertexPatternExpression() throws ParseException:
+{
+ Set<ElementLabel> vertexLabels = new HashSet<ElementLabel>();
+ VariableExpr variableExpr = null;
+ Token startToken = null;
+ String vertexLabelName;
+}
+{
+ <LEFTPAREN> { startToken = token; }
+ (
+ variableExpr = Variable()
+ )?
+ (
+ <COLON> vertexLabelName = Identifier() { vertexLabels.add(new ElementLabel(vertexLabelName)); }
+ ( <BAR> vertexLabelName = Identifier() { vertexLabels.add(new ElementLabel(vertexLabelName)); } )*
+ )?
+ <RIGHTPAREN>
+ {
+ VertexPatternExpr vertexExpression = new VertexPatternExpr(variableExpr, vertexLabels);
+ return addSourceLocation(vertexExpression, startToken);
+ }
+}
+
+@new
+EdgeDescriptor EdgeDescriptor() throws ParseException:
+{
+ Pair<Set<ElementLabel>, Pair<Integer, Integer>> edgeDetail = null;
+ Token startToken = null;
+ VariableExpr edgeVariable = null;
+
+ // We default to undirected edges.
+ EdgeDescriptor.EdgeType edgeType = EdgeDescriptor.EdgeType.UNDIRECTED;
+}
+{
+ (
+ <MINUS> { startToken = token; }
+ (
+ <LEFTBRACKET>
+ (
+ edgeVariable = Variable()
+ )?
+ ( <COLON> edgeDetail = EdgeDetail() )?
+ <RIGHTBRACKET> <MINUS>
+ )?
+ ( <GT> { edgeType = EdgeDescriptor.EdgeType.LEFT_TO_RIGHT; } )?
+ |
+ <LT> {
+ startToken = token;
+ edgeType = EdgeDescriptor.EdgeType.RIGHT_TO_LEFT;
+ }
+ <MINUS>
+ (
+ <LEFTBRACKET>
+ (
+ edgeVariable = Variable()
+ )?
+ ( <COLON> edgeDetail = EdgeDetail() )?
+ <RIGHTBRACKET> <MINUS>
+ )?
+ )
+ {
+ // Edges (by default) are of class EDGE_PATTERN and are not sub-paths.
+ IGraphExpr.GraphExprKind edgeClass = IGraphExpr.GraphExprKind.EDGE_PATTERN;
+ Integer hopCountMin = 1;
+ Integer hopCountMax = 1;
+
+ Set<ElementLabel> labels = new HashSet<ElementLabel>();
+ if (edgeDetail != null) {
+ labels = edgeDetail.first;
+
+ // We have explicitly specified "{" and "}". Use sub-path semantics.
+ if (edgeDetail.second != null) {
+ edgeClass = IGraphExpr.GraphExprKind.PATH_PATTERN;
+ hopCountMin = edgeDetail.second.first;
+ hopCountMax = edgeDetail.second.second;
+ }
+ }
+
+ return new EdgeDescriptor(edgeType, edgeClass, labels, edgeVariable, hopCountMin, hopCountMax);
+ }
+}
+
+@new
+Pair<Set<ElementLabel>, Pair<Integer, Integer>> EdgeDetail() throws ParseException:
+{
+ Set<ElementLabel> edgeLabels = new HashSet<ElementLabel>();
+ Pair<Integer, Integer> repetitionQuantifier = null;
+ String labelName = null;
+}
+{
+ (
+ // Note: we want to forbid LABEL_1|LABEL_2{...}.
+ LOOKAHEAD(2, <BAR>)
+ (
+ labelName = Identifier() { edgeLabels.add(new ElementLabel(labelName)); }
+ <BAR> labelName = Identifier() { edgeLabels.add(new ElementLabel(labelName)); }
+ ( <BAR> labelName = Identifier() { edgeLabels.add(new ElementLabel(labelName)); } )*
+ )
+ |
+ (
+ labelName = Identifier() { edgeLabels.add(new ElementLabel(labelName)); }
+ |
+ <LEFTPAREN>
+ labelName = Identifier() { edgeLabels.add(new ElementLabel(labelName)); }
+ ( <BAR> labelName = Identifier() { edgeLabels.add(new ElementLabel(labelName)); } )*
+ <RIGHTPAREN>
+ )
+ ( repetitionQuantifier = EdgeRepetitionQuantifier() )?
+ |
+ ( repetitionQuantifier = EdgeRepetitionQuantifier() )
+ )
+ {
+ return new Pair<Set<ElementLabel>, Pair<Integer, Integer>> (edgeLabels, repetitionQuantifier);
+ }
+}
+
+@new
+Pair<Integer, Integer> EdgeRepetitionQuantifier() throws ParseException:
+{
+ Integer hopCountMin = null;
+ Integer hopCountMax = null;
+}
+{
+ <LEFTBRACE>
+ ( // Note: we forbid unbounded edge repetition.
+ ( <INTEGER_LITERAL> { hopCountMin = Integer.valueOf(token.image); } )?
+ <COMMA> <INTEGER_LITERAL> { hopCountMax = Integer.valueOf(token.image); }
+ )
+ <RIGHTBRACE>
+ {
+ return new Pair<Integer, Integer>(hopCountMin, hopCountMax);
}
}
@@ -344,6 +689,7 @@
| <GRAPH: "graph">
| <SOURCE: "source">
| <VERTEX: "vertex">
+ | <MATCH: "match">
}
@new_at_the_end