private void TryParseUnionClause(SparqlQueryParserContext context, GraphPattern p)
        {
            //Create a new Pattern which will hold the UNION
            GraphPattern union = new GraphPattern();
            union.IsUnion = true;

            //Add the Last Child Pattern of the Parent as that is the start of the UNION
            GraphPattern lastchild = p.LastChildPattern();
            if (lastchild.IsSimplifiable)
            {
                union.AddGraphPattern(lastchild.LastChildPattern());
            }
            else
            {
                union.AddGraphPattern(lastchild);
            }

            GraphPattern child = this.TryParseGraphPattern(context, true);
            union.AddGraphPattern(child);

            //Check for multiple
            IToken next = context.Tokens.Peek();
            while (next.TokenType == Token.UNION)
            {
                context.Tokens.Dequeue();
                union.AddGraphPattern(this.TryParseGraphPattern(context, true));
                next = context.Tokens.Peek();
            }

            p.AddGraphPattern(union);
        }
        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);
            }
        }