private void TryParsePredicateObjectList(SparqlQueryParserContext context, GraphPattern p, int expectedCount) { PatternItem subj, pred, obj; //Subject is first thing on the Stack subj = this.TryCreatePatternItem(context, context.LocalTokens.Pop()); //Start grabbing other stuff off the Stack and Parsing IToken next, lit, temp; ISparqlPath path; do { //Peek at the Next Token next = context.Tokens.Peek(); switch (next.TokenType) { case Token.COMMENT: //Ignore Comments context.Tokens.Dequeue(); break; case Token.VARIABLE: context.LocalTokens.Push(next); context.Query.AddVariable(next.Value); context.Tokens.Dequeue(); break; case Token.URI: case Token.QNAME: case Token.LITERAL: case Token.LONGLITERAL: case Token.PLAINLITERAL: case Token.KEYWORDA: context.LocalTokens.Push(next); context.Tokens.Dequeue(); break; case Token.HAT: case Token.DIVIDE: case Token.BITWISEOR: case Token.MULTIPLY: case Token.PLUS: case Token.QUESTION: case Token.NEGATION: //If we see any of these Tokens then it's a Property Path if (context.SyntaxMode == SparqlQuerySyntax.Sparql_1_0) throw new RdfParseException("Property Paths are not permitted in SPARQL 1.0"); if (context.LocalTokens.Count == expectedCount - 1) { path = context.PathParser.Parse(context, context.LocalTokens.Pop()); PathToken pathToken = new PathToken(path); context.LocalTokens.Push(pathToken); } else if ((next.TokenType == Token.HAT || next.TokenType == Token.NEGATION) && context.LocalTokens.Count == expectedCount - 2) { // ^ and ! may be used to start a pattern context.Tokens.Dequeue(); path = context.PathParser.Parse(context, next); PathToken pathToken = new PathToken(path); context.LocalTokens.Push(pathToken); } else { throw ParserHelper.Error("Encountered a '" + next.GetType().ToString() + "' Token which is valid only after a Predicate to indicate Path Cardinality", next); } break; case Token.BLANKNODE: case Token.BLANKNODEWITHID: //Generate a new Blank Node ID if required if (next.TokenType == Token.BLANKNODE) { next = new BlankNodeWithIDToken(context.GetNewBlankNodeID(), 0, 0, 0); } //Check list of Blank Node usages if (context.BlankNodeIDUsages.ContainsKey(next.Value)) { if (context.BlankNodeIDUsages[next.Value] != context.GraphPatternID) { throw ParserHelper.Error("Invalid use of Blank Node Label '" + next.Value + "', this Label has already been used in a different Graph Pattern", next); } } else { context.BlankNodeIDUsages.Add(next.Value, context.GraphPatternID); } context.LocalTokens.Push(next); context.Tokens.Dequeue(); break; case Token.HATHAT: //Get the next Token which should be a Datatype Token context.Tokens.Dequeue(); next = context.Tokens.Peek(); if (next.TokenType == Token.DATATYPE) { //Get the previous Token off the Stack and ensure it's a Literal lit = context.LocalTokens.Pop(); if (lit.TokenType == Token.LITERAL || lit.TokenType == Token.LONGLITERAL) { //Create a DataTyped Literal context.LocalTokens.Push(new LiteralWithDataTypeToken(lit, (DataTypeToken)next)); } else { throw ParserHelper.Error("Unexpected Datatype Token, a Datatype may only be specified after a quoted Literal/Long Literal", lit); } } else { throw ParserHelper.Error("Unexpected Token '" + next.GetType().ToString() + "' encountered, expected a Datatype Token to follow a ^^ Token to specify the Datatype of a previous Literal Token", next); } context.Tokens.Dequeue(); break; case Token.LEFTSQBRACKET: //Start of Blank Node Collection //Create a new Blank Node Token BlankNodeWithIDToken bnode = new BlankNodeWithIDToken(context.GetNewBlankNodeID(), 0, 0, 0); //Push twice, once for Object of the current Triple context.LocalTokens.Push(bnode); context.Tokens.Dequeue(); next = context.Tokens.Peek(); if (next.TokenType == Token.RIGHTSQBRACKET) { //Single anonymous blank node context.Tokens.Dequeue(); break; } //Blank Node Collection //Push again for Subject of new Triple context.LocalTokens.Push(bnode); //Recursively call self to parse the new Triple list this.TryParsePredicateObjectList(context, p, expectedCount + 2); break; case Token.RIGHTSQBRACKET: //End of Blank Node Collection //Allow for trailing semicolon if (context.LocalTokens.Count == expectedCount - 2 && context.Tokens.LastTokenType == Token.SEMICOLON) { context.Tokens.Dequeue(); return; } //Check length of Stack if (context.LocalTokens.Count < expectedCount) { throw ParserHelper.Error("Encountered a Right Square Bracket Token to terminate a Blank Node Collection within a Triple Pattern but there are not enough Tokens to form a valid Triple Pattern", next); } else if (context.LocalTokens.Count > expectedCount) { throw ParserHelper.Error("Encountered a Right Square Bracket Token to terminate a Blank Node Collection within a Triple Pattern but there are too many Tokens to form a valid Triple Pattern", next); } obj = this.TryCreatePatternItem(context, context.LocalTokens.Pop()); pred = this.TryCreatePatternItem(context, context.LocalTokens.Pop()); //Add Pattern to the Graph Pattern p.AddTriplePattern(new TriplePattern(subj, pred, obj)); context.Tokens.Dequeue(); return; case Token.LEFTBRACKET: //Property Path if it's the Predicate or Collection if it's the Object if (context.LocalTokens.Count == expectedCount - 2) { //Property Path if (context.SyntaxMode == SparqlQuerySyntax.Sparql_1_0) throw new RdfParseException("Property Paths are not permitted in SPARQL 1.0"); path = context.PathParser.Parse(context, context.Tokens.Dequeue()); PathToken pathToken = new PathToken(path); context.LocalTokens.Push(pathToken); } else { //Collection context.Tokens.Dequeue(); this.TryParseCollection(context, p, false); } break; case Token.LANGSPEC: //Get the previous Token off the Stack and ensure it's a Literal lit = context.LocalTokens.Pop(); if (lit.TokenType == Token.LITERAL || lit.TokenType == Token.LONGLITERAL) { //Create a Language Specified Literal context.LocalTokens.Push(new LiteralWithLanguageSpecifierToken(lit, (LanguageSpecifierToken)next)); } else { throw ParserHelper.Error("Unexpected Language Specifier Token, a Language Specifier may only be specified after a quoted Literal/Long Literal", lit); } context.Tokens.Dequeue(); break; case Token.COMMA: //End of a Triple Pattern //Check length of stack if (context.LocalTokens.Count < expectedCount) { throw ParserHelper.Error("Encountered a Comma Token to terminate a Triple Pattern but there are not enough Tokens to form a valid Triple Pattern", next); } else if (context.LocalTokens.Count > expectedCount) { throw ParserHelper.Error("Encountered a Comma Token to terminate a Triple Pattern but there are too many Tokens to form a valid Triple Pattern", next); } obj = this.TryCreatePatternItem(context, context.LocalTokens.Pop()); temp = context.LocalTokens.Pop(); if (temp.TokenType == Token.PATH) { path = ((PathToken)temp).Path; p.AddTriplePattern(new PropertyPathPattern(subj, path, obj)); } else { pred = this.TryCreatePatternItem(context, temp); //Add Pattern to the Graph Pattern p.AddTriplePattern(new TriplePattern(subj, pred, obj)); } //Push Predicate back on Stack context.LocalTokens.Push(temp); context.Tokens.Dequeue(); break; case Token.SEMICOLON: //End of a Triple Pattern //Check length of stack if (context.LocalTokens.Count < expectedCount) { throw ParserHelper.Error("Encountered a Semicolon Token to terminate a Triple Pattern but there are not enough Tokens to form a valid Triple Pattern", next); } else if (context.LocalTokens.Count > expectedCount) { throw ParserHelper.Error("Encountered a Semicolon Token to terminate a Triple Pattern but there are too many Tokens to form a valid Triple Pattern", next); } obj = this.TryCreatePatternItem(context, context.LocalTokens.Pop()); temp = context.LocalTokens.Pop(); if (temp.TokenType == Token.PATH) { path = ((PathToken)temp).Path; p.AddTriplePattern(new PropertyPathPattern(subj, path, obj)); } else { pred = this.TryCreatePatternItem(context, temp); //Add Pattern to the Graph Pattern p.AddTriplePattern(new TriplePattern(subj, pred, obj)); } context.Tokens.Dequeue(); break; case Token.DOT: //End of the Triple Patterns //Allow for trailing semicolon and Blank Node Collection lists if (context.LocalTokens.Count == expectedCount - 2 && (context.Tokens.LastTokenType == Token.SEMICOLON || (context.Tokens.LastTokenType == Token.RIGHTSQBRACKET && p.TriplePatterns.Count > 0))) { if (context.Tokens.LastTokenType == Token.RIGHTSQBRACKET) { context.Tokens.Dequeue(); } return; } //Check length of Stack if (context.LocalTokens.Count < expectedCount) { throw ParserHelper.Error("Encountered a DOT Token to terminate a Triple Pattern but there are not enough Tokens to form a valid Triple Pattern", next); } else if (context.LocalTokens.Count > expectedCount) { throw ParserHelper.Error("Encountered a DOT Token to terminate a Triple Pattern but there are too many Tokens to form a valid Triple Pattern", next); } obj = this.TryCreatePatternItem(context, context.LocalTokens.Pop()); temp = context.LocalTokens.Pop(); if (temp.TokenType == Token.PATH) { path = ((PathToken)temp).Path; p.AddTriplePattern(new PropertyPathPattern(subj, path, obj)); } else { pred = this.TryCreatePatternItem(context, temp); //Add Pattern to the Graph Pattern p.AddTriplePattern(new TriplePattern(subj, pred, obj)); } context.Tokens.Dequeue(); return; case Token.LEFTCURLYBRACKET: case Token.RIGHTCURLYBRACKET: case Token.OPTIONAL: case Token.EXISTS: case Token.NOTEXISTS: case Token.UNSAID: case Token.MINUS_P: case Token.SERVICE: case Token.GRAPH: case Token.FILTER: //End of the Triple Patterns //Allow for trailing semicolon and Blank Node Collection lists if (context.LocalTokens.Count == expectedCount - 2 && (context.Tokens.LastTokenType == Token.SEMICOLON || ((context.Tokens.LastTokenType == Token.RIGHTSQBRACKET || context.Tokens.LastTokenType == Token.RIGHTBRACKET) && p.TriplePatterns.Count > 0))) { return; } //Check length of Stack if (context.LocalTokens.Count < expectedCount) { temp = context.LocalTokens.Peek(); if (next.TokenType == Token.LEFTCURLYBRACKET && context.SyntaxMode != SparqlQuerySyntax.Sparql_1_0 && context.LocalTokens.Count == expectedCount - 1 && (temp.TokenType == Token.QNAME || temp.TokenType == Token.URI || temp.TokenType == Token.KEYWORDA)) { //In this case this should be a Cardinality Modifier on a path (we hope) path = context.PathParser.Parse(context, context.LocalTokens.Pop()); IToken pathToken = new PathToken(path); context.LocalTokens.Push(pathToken); continue; } else { throw ParserHelper.Error("Encountered a Token which terminates a Triple Pattern but there are not enough Tokens to form a valid Triple Pattern", next); } } else if (context.LocalTokens.Count > expectedCount) { throw ParserHelper.Error("Encountered a Token which terminates a Triple Pattern but there are too many Tokens to form a valid Triple Pattern", next); } obj = this.TryCreatePatternItem(context, context.LocalTokens.Pop()); temp = context.LocalTokens.Pop(); if (temp.TokenType == Token.PATH) { path = ((PathToken)temp).Path; p.AddTriplePattern(new PropertyPathPattern(subj, path, obj)); } else { pred = this.TryCreatePatternItem(context, temp); //Add Pattern to the Graph Pattern p.AddTriplePattern(new TriplePattern(subj, pred, obj)); } return; default: throw ParserHelper.Error("Unexpected Token '" + next.GetType().ToString() + "' while trying to Parse Triple Patterns", next); } } while (true); }
private void TryParseCollection(SparqlQueryParserContext context, GraphPattern p, bool nested) { //Check the next Token IToken next = context.Tokens.Peek(); if (next.TokenType == Token.RIGHTBRACKET) { //Empty Collection context.Tokens.Dequeue(); if (!nested) { //Push an rdf:nil Uri on the Stack context.LocalTokens.Push(new UriToken("<" + NamespaceMapper.RDF + "nil>", next.StartLine, next.StartPosition, next.EndPosition)); } } else { //Push a Blank Node Token onto the stack for the start of the collection BlankNodeWithIDToken blank; if (!nested) { blank = new BlankNodeWithIDToken(context.GetNewBlankNodeID(), next.StartLine, next.StartPosition, next.EndPosition); context.LocalTokens.Push(blank); } else { blank = new BlankNodeWithIDToken("_:sparql-autos" + context.BlankNodeID, next.StartLine, next.StartPosition, next.EndPosition); } bool first = true; IUriNode rdfFirst, rdfRest, rdfNil; rdfFirst = new UriNode(null, new Uri(NamespaceMapper.RDF + "first")); rdfRest = new UriNode(null, new Uri(NamespaceMapper.RDF + "rest")); rdfNil = new UriNode(null, new Uri(NamespaceMapper.RDF + "nil")); do { next = context.Tokens.Peek(); switch (next.TokenType) { case Token.BLANKNODE: case Token.BLANKNODEWITHID: case Token.KEYWORDA: case Token.LITERAL: case Token.LONGLITERAL: case Token.PLAINLITERAL: case Token.QNAME: case Token.URI: case Token.VARIABLE: //Create the Triple pattern if (first) { //rdf:first Pattern p.AddTriplePattern(new TriplePattern(this.TryCreatePatternItem(context, blank), new NodeMatchPattern(rdfFirst), this.TryCreatePatternItem(context, next))); first = false; } else { //Get new Blank Node ID BlankNodeWithIDToken blank2 = new BlankNodeWithIDToken(context.GetNewBlankNodeID(), next.StartLine,next.StartPosition,next.EndPosition); //rdf:rest Pattern p.AddTriplePattern(new TriplePattern(this.TryCreatePatternItem(context, blank), new NodeMatchPattern(rdfRest), this.TryCreatePatternItem(context, blank2))); blank = blank2; //rdf:first Pattern p.AddTriplePattern(new TriplePattern(this.TryCreatePatternItem(context, blank), new NodeMatchPattern(rdfFirst), this.TryCreatePatternItem(context, next))); } break; case Token.LEFTSQBRACKET: //Is the next token a Right Square Bracket? //ie. a [] for an anonymous blank node context.Tokens.Dequeue(); next = context.Tokens.Peek(); if (next.TokenType == Token.RIGHTSQBRACKET) { BlankNodeWithIDToken anon = new BlankNodeWithIDToken(context.GetNewBlankNodeID(), next.StartLine, next.StartPosition, next.EndPosition); if (first) { //rdf:first Pattern p.AddTriplePattern(new TriplePattern(this.TryCreatePatternItem(context, blank), new NodeMatchPattern(rdfFirst), this.TryCreatePatternItem(context, anon))); first = false; } else { //Get new Blank Node ID BlankNodeWithIDToken blank2 = new BlankNodeWithIDToken(context.GetNewBlankNodeID(), next.StartLine, next.StartPosition, next.EndPosition); //rdf:rest Pattern p.AddTriplePattern(new TriplePattern(this.TryCreatePatternItem(context, blank), new NodeMatchPattern(rdfRest), this.TryCreatePatternItem(context, blank2))); blank = blank2; //rdf:first Pattern p.AddTriplePattern(new TriplePattern(this.TryCreatePatternItem(context, blank), new NodeMatchPattern(rdfFirst), this.TryCreatePatternItem(context, anon))); } } else { BlankNodeWithIDToken anon = new BlankNodeWithIDToken(context.GetNewBlankNodeID(), next.StartLine, next.StartPosition, next.EndPosition); if (first) { //rdf:first Pattern p.AddTriplePattern(new TriplePattern(this.TryCreatePatternItem(context, blank), new NodeMatchPattern(rdfFirst), this.TryCreatePatternItem(context, anon))); first = false; } else { //Get new Blank Node ID BlankNodeWithIDToken blank2 = new BlankNodeWithIDToken(context.GetNewBlankNodeID(), next.StartLine, next.StartPosition, next.EndPosition); //rdf:rest Pattern p.AddTriplePattern(new TriplePattern(this.TryCreatePatternItem(context, blank), new NodeMatchPattern(rdfRest), this.TryCreatePatternItem(context, blank2))); blank = blank2; //rdf:first Pattern p.AddTriplePattern(new TriplePattern(this.TryCreatePatternItem(context, blank), new NodeMatchPattern(rdfFirst), this.TryCreatePatternItem(context, anon))); } //Parse the Blank Node Collection context.LocalTokens.Push(anon); this.TryParsePredicateObjectList(context, p, context.LocalTokens.Count + 1); continue; } break; case Token.LEFTBRACKET: BlankNodeWithIDToken innerCollection = new BlankNodeWithIDToken(context.GetNewBlankNodeID(), next.StartLine, next.StartPosition, next.EndPosition); if (first) { //rdf:first Pattern p.AddTriplePattern(new TriplePattern(this.TryCreatePatternItem(context, blank), new NodeMatchPattern(rdfFirst), this.TryCreatePatternItem(context, innerCollection))); first = false; } else { //Get new Blank Node ID BlankNodeWithIDToken blank2 = new BlankNodeWithIDToken(context.GetNewBlankNodeID(), next.StartLine, next.StartPosition, next.EndPosition); //rdf:rest Pattern p.AddTriplePattern(new TriplePattern(this.TryCreatePatternItem(context, blank), new NodeMatchPattern(rdfRest), this.TryCreatePatternItem(context, blank2))); blank = blank2; //rdf:first Pattern p.AddTriplePattern(new TriplePattern(this.TryCreatePatternItem(context, blank), new NodeMatchPattern(rdfFirst), this.TryCreatePatternItem(context, innerCollection))); } context.Tokens.Dequeue(); this.TryParseCollection(context, p, true); continue; case Token.RIGHTBRACKET: //End of Collection //rdf:rest Pattern p.AddTriplePattern(new TriplePattern(this.TryCreatePatternItem(context, blank), new NodeMatchPattern(rdfRest), new NodeMatchPattern(rdfNil))); break; default: throw ParserHelper.Error("Unexpected Token '" + next.GetType().ToString() + "' encountered while trying to parse a Collection", next); } context.Tokens.Dequeue(); } while (next.TokenType != Token.RIGHTBRACKET); } }
private void TryParseTriplePatterns(SparqlQueryParserContext context, GraphPattern p) { int lasttoken = context.Tokens.LastTokenType; IToken next = context.Tokens.Dequeue(); //Allowed a Variable/RDF Term/Collection //OR we might go straight to a OPTIONAL/GRAPH/UNION/FILTER/EXISTS/NOT EXISTS/LET switch (next.TokenType) { case Token.COMMENT: //Comments are discardable this.TryParseTriplePatterns(context, p); break; case Token.VARIABLE: //Variable context.LocalTokens.Push(next); context.Query.AddVariable(next.Value); this.TryParsePredicateObjectList(context, p,2); break; case Token.URI: case Token.QNAME: case Token.LITERAL: case Token.LONGLITERAL: case Token.PLAINLITERAL: //Must then be followed be a non-empty Property List context.LocalTokens.Push(next); this.TryParsePredicateObjectList(context, p,2); break; case Token.BLANKNODE: case Token.BLANKNODEWITHID: //Check list of Blank Node usages if (context.BlankNodeIDUsages.ContainsKey(next.Value)) { if (context.BlankNodeIDUsages[next.Value] != context.GraphPatternID) { throw ParserHelper.Error("Invalid use of Blank Node Label '" + next.Value + "', this Label has already been used in a different Graph Pattern", next); } } else { context.BlankNodeIDUsages.Add(next.Value, context.GraphPatternID); } //Must then be followed be a non-empty Property List context.LocalTokens.Push(next); this.TryParsePredicateObjectList(context, p, 2); break; case Token.LET: //LET assignment this.TryParseLetAssignment(context, p); break; case Token.BIND: //BIND assignment this.TryParseBindAssignment(context, p); break; case Token.LEFTSQBRACKET: //Start of Blank Node Collection //Create a new Blank Node Token BlankNodeWithIDToken bnode = new BlankNodeWithIDToken(context.GetNewBlankNodeID(), 0, 0, 0); //Push twice, once for Subject of Collection context.LocalTokens.Push(bnode); next = context.Tokens.Peek(); if (next.TokenType == Token.RIGHTSQBRACKET) { //Single anonymous blank node context.Tokens.Dequeue(); //Parse as Subject of Triples this.TryParsePredicateObjectList(context, p, 2); } else { //Parse the Collection this.TryParsePredicateObjectList(context, p, 2); //Push again for subject of Triples context.LocalTokens.Push(bnode); this.TryParsePredicateObjectList(context, p, 2); } break; case Token.LEFTBRACKET: //Collection this.TryParseCollection(context, p, false); this.TryParsePredicateObjectList(context, p, 2); break; case Token.FILTER: //FILTER Pattern this.TryParseFilterClause(context, p); break; case Token.OPTIONAL: //OPTIONAL Clause this.TryParseOptionalClause(context, p); break; case Token.EXISTS: case Token.NOTEXISTS: case Token.UNSAID: //EXISTS/NOT EXISTS clause if (next.TokenType == Token.UNSAID && context.SyntaxMode != SparqlQuerySyntax.Extended) throw new RdfParseException("The UNSAID Keyword is only supported when syntax is set to Extended. It is an alias for NOT EXISTS which can be used when the syntax is set to SPARQL 1.1/Extended"); this.TryParseExistsClause(context, p, (next.TokenType == Token.EXISTS)); break; case Token.MINUS_P: //MINUS clause this.TryParseMinusClause(context, p); break; case Token.SERVICE: //SERVICE clause this.TryParseServiceClause(context, p); break; case Token.SELECT: //Sub-query this.TryParseSubquery(context, p); break; case Token.GRAPH: //GRAPH Clause this.TryParseGraphClause(context, p); break; case Token.UNION: //UNION Clause this.TryParseUnionClause(context, p); break; case Token.LEFTCURLYBRACKET: //Nested Graph Pattern p.AddGraphPattern(this.TryParseGraphPattern(context, false)); //Simplify Subqueries if (p.ChildGraphPatterns.Last().IsSubQuery) { GraphPattern temp = p.LastChildPattern(); p.AddTriplePattern(temp.TriplePatterns.First()); } break; case Token.DOT: //Can Discard this if last character was the end of a nested Graph pattern if (lasttoken == Token.RIGHTCURLYBRACKET || lasttoken == Token.RIGHTBRACKET) { //Can Discard this if the next character is not another DOT next = context.Tokens.Peek(); if (next.TokenType != Token.DOT) { if (next.TokenType != Token.RIGHTCURLYBRACKET) { this.TryParseTriplePatterns(context, p); } else { return; } } else { throw ParserHelper.Error("A DOT Token cannot follow another DOT Token within a Graph Pattern", next); } } else if (lasttoken == Token.SEMICOLON) { //Allow Trailing Semicolon return; } else { throw ParserHelper.Error("A DOT Token can only be used to terminate a Triple Pattern or a Nested Graph Pattern", next); } break; default: throw ParserHelper.Error("Unexpected Token '" + next.GetType().ToString() + "' encountered when the start of a Triple Pattern was expected", next); } }