Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1839 lines (1838 sloc)
50.4 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
// ARQ/SPARQL 1.1 Grammar - native syntax for the query engine | |
// UPDATE : SPARQL 1.1. Update | |
// ARQ_UPDATE : extensions to SPARQL 1.1 Update to cover SPARQL/Update (W3C Submission) | |
options | |
{ | |
// \ u processed in the input stream | |
// SPARQL 1.0 | |
JAVA_UNICODE_ESCAPE = true ; | |
UNICODE_INPUT = false ; | |
// // \ u processed after parsing. | |
// // strings, prefix names, IRIs | |
// JAVA_UNICODE_ESCAPE = false ; | |
// UNICODE_INPUT = true ; | |
STATIC = false ; | |
// DEBUG_PARSER = true ; | |
// DEBUG_TOKEN_MANAGER = true ; | |
} | |
PARSER_BEGIN(SPARQLParser11) | |
/** | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.hp.hpl.jena.sparql.lang.sparql_11 ; | |
import com.hp.hpl.jena.graph.* ; | |
import com.hp.hpl.jena.query.* ; | |
import com.hp.hpl.jena.sparql.core.Var ; | |
import com.hp.hpl.jena.sparql.syntax.* ; | |
import com.hp.hpl.jena.sparql.expr.* ; | |
import com.hp.hpl.jena.sparql.path.* ; | |
import com.hp.hpl.jena.sparql.expr.aggregate.* ; | |
import com.hp.hpl.jena.update.* ; | |
import com.hp.hpl.jena.sparql.modify.request.* ; | |
public class SPARQLParser11 extends SPARQLParser11Base | |
{ | |
boolean allowAggregatesInExpressions = false ; | |
} | |
PARSER_END(SPARQLParser11) | |
// // Common top for single entry point. | |
// void Top(): {} | |
// { | |
// ( Query() | Update() ) | |
// <EOF> | |
// } | |
// Query only entry point | |
void QueryUnit(): { } | |
{ | |
{ startQuery() ; } | |
Query() <EOF> | |
{ finishQuery() ; } | |
} | |
void Query() : { } | |
{ | |
Prologue() | |
( SelectQuery() | ConstructQuery() | DescribeQuery() | AskQuery() | |
// #ifdef ARQ | |
// | JsonTemplateQuery() | |
// #endif | |
) | |
ValuesClause() | |
} | |
void UpdateUnit() : {} | |
{ | |
{ startUpdateRequest() ; } | |
Update() | |
<EOF> | |
{ finishUpdateRequest() ; } | |
} | |
void Prologue() : {} | |
{ | |
( BaseDecl() | PrefixDecl() )* | |
} | |
void BaseDecl() : { String iri ; } | |
{ | |
<BASE> iri = IRIREF() | |
{ getPrologue().setBaseURI(iri) ; } | |
} | |
void PrefixDecl() : { Token t ; String iri ; } | |
{ | |
<PREFIX> t = <PNAME_NS> iri = IRIREF() | |
{ String s = fixupPrefix(t.image, t.beginLine, t.beginColumn) ; | |
getPrologue().setPrefix(s, iri) ; } | |
} | |
// ---- Query type clauses | |
void SelectQuery() : { } | |
{ | |
SelectClause() | |
( DatasetClause() )* | |
WhereClause() | |
SolutionModifier() | |
} | |
void SubSelect() :{ } | |
{ | |
SelectClause() | |
WhereClause() | |
SolutionModifier() | |
ValuesClause() | |
} | |
void SelectClause() : { Var v ; Expr expr ; Node n ; } | |
{ | |
<SELECT> | |
{ getQuery().setQuerySelectType() ; } | |
( <DISTINCT> { getQuery().setDistinct(true);} | |
| <REDUCED> { getQuery().setReduced(true); } | |
)? | |
{ allowAggregatesInExpressions = true ; } | |
( | |
( | |
v = Var() { getQuery().addResultVar(v) ; } | |
| | |
// Expressions with () | |
( | |
{ v = null ; } | |
<LPAREN> | |
expr = Expression() | |
<AS> v = Var() | |
<RPAREN> | |
{ getQuery().addResultVar(v, expr) ; } | |
) | |
{ getQuery().setQueryResultStar(false) ; } | |
)+ | |
| | |
<STAR> { getQuery().setQueryResultStar(true) ; } | |
) | |
{ allowAggregatesInExpressions = false ; } | |
} | |
void ConstructQuery() : { Template t ; | |
TripleCollectorBGP acc = new TripleCollectorBGP() ; } | |
{ | |
// #ifndef ARQ | |
// <CONSTRUCT> | |
// { getQuery().setQueryConstructType() ; } | |
// t = ConstructTemplate() | |
// { getQuery().setConstructTemplate(t) ; } | |
// ( DatasetClause() )* | |
// WhereClause() | |
// SolutionModifier() | |
// #else | |
<CONSTRUCT> | |
{ getQuery().setQueryConstructType() ; } | |
( | |
// Full form. | |
t = ConstructTemplate() | |
{ getQuery().setConstructTemplate(t) ; } | |
( DatasetClause() )* | |
WhereClause() | |
SolutionModifier() | |
| | |
// Abbreviated CONSTRUCT WHERE {} | |
( DatasetClause() )* | |
<WHERE> | |
// Should have been "ConstructTemplate()" | |
<LBRACE> | |
(TriplesTemplate(acc))? | |
<RBRACE> | |
SolutionModifier() | |
{ | |
t = new Template(acc.getBGP()) ; | |
getQuery().setConstructTemplate(t) ; | |
// Create a query in the same shape as the query created by writing out in full. | |
ElementPathBlock epb = new ElementPathBlock(acc.getBGP()) ; | |
ElementGroup elg = new ElementGroup() ; | |
elg.addElement(epb) ; | |
getQuery().setQueryPattern(elg) ; | |
} | |
) | |
//#endif | |
} | |
void DescribeQuery() : { Node n ; } | |
{ | |
<DESCRIBE> | |
{ getQuery().setQueryDescribeType() ; } | |
( | |
( n = VarOrIri() { getQuery().addDescribeNode(n) ; } )+ | |
{ getQuery().setQueryResultStar(false) ; } | |
| | |
<STAR> | |
{ getQuery().setQueryResultStar(true) ; } | |
) | |
( DatasetClause() )* | |
( WhereClause() )? | |
SolutionModifier() | |
} | |
void AskQuery() : {} | |
{ | |
<ASK> { getQuery().setQueryAskType() ; } | |
( DatasetClause() )* | |
WhereClause() | |
SolutionModifier() | |
} | |
// ---- | |
void DatasetClause() : {} | |
{ | |
<FROM> | |
( DefaultGraphClause() | NamedGraphClause() ) | |
} | |
void DefaultGraphClause() : { String iri ; } | |
{ | |
iri = SourceSelector() | |
{ | |
// This checks for duplicates | |
getQuery().addGraphURI(iri) ; | |
} | |
} | |
void NamedGraphClause() : { String iri ; } | |
{ | |
<NAMED> | |
iri = SourceSelector() | |
{ | |
// This checks for duplicates | |
getQuery().addNamedGraphURI(iri) ; | |
} | |
} | |
String SourceSelector() : { String iri ; } | |
{ | |
iri = iri() { return iri ; } | |
} | |
void WhereClause() : { Element el ; } | |
{ | |
(<WHERE>)? | |
{ startWherePattern() ; } | |
el = GroupGraphPattern() { getQuery().setQueryPattern(el) ; } | |
{ finishWherePattern() ; } | |
} | |
void SolutionModifier() : { } | |
{ | |
( GroupClause() )? | |
( HavingClause() )? | |
( OrderClause() )? | |
( LimitOffsetClauses() )? | |
} | |
void GroupClause() : { } | |
{ | |
<GROUP> <BY> ( GroupCondition() )+ | |
} | |
void GroupCondition() : { Var v = null ; Expr expr = null ; } | |
{ | |
( expr = BuiltInCall() { getQuery().addGroupBy((Var)null, expr) ; } | |
| expr = FunctionCall() { getQuery().addGroupBy((Var)null, expr) ; } | |
| | |
<LPAREN> | |
expr = Expression() | |
( <AS> v = Var() )? | |
<RPAREN> | |
{ getQuery().addGroupBy(v ,expr) ; } | |
| v = Var() | |
{ getQuery().addGroupBy(v) ; } | |
) | |
} | |
void HavingClause() : { } | |
{ | |
{ allowAggregatesInExpressions = true ; } | |
<HAVING> (HavingCondition())+ | |
{ allowAggregatesInExpressions = false ; } | |
} | |
void HavingCondition() : { Expr c ; } | |
{ | |
c = Constraint() | |
{ getQuery().addHavingCondition(c) ; } | |
} | |
void OrderClause() : { } | |
{ | |
{ allowAggregatesInExpressions = true ; } | |
<ORDER> <BY> ( OrderCondition() )+ | |
{ allowAggregatesInExpressions = false ; } | |
} | |
void OrderCondition() : | |
{ int direction = 0 ; Expr expr = null ; Node v = null ; } | |
{ | |
{ direction = Query.ORDER_DEFAULT ; } | |
( | |
( // These are for clarity in the HTML | |
( <ASC> { direction = Query.ORDER_ASCENDING ; } | |
| <DESC> { direction = Query.ORDER_DESCENDING ; } ) | |
expr = BrackettedExpression() | |
) | |
| | |
( expr = Constraint() | |
| v = Var() //{ expr = asExpr(v) ; } | |
) | |
) | |
{ if ( v == null ) | |
getQuery().addOrderBy(expr, direction) ; | |
else | |
getQuery().addOrderBy(v, direction) ; } | |
} | |
void LimitOffsetClauses() : { } | |
{ | |
// SPARQL does not care about the order here. | |
// SQL (where implemented) does (it's LIMIT then OFFSET generally) | |
// But that is counter intuitive as it's applied the other way round | |
( | |
LimitClause() (OffsetClause())? | |
| | |
OffsetClause() (LimitClause())? | |
) | |
} | |
void LimitClause() : { Token t ; } | |
{ | |
<LIMIT> t = <INTEGER> | |
{ getQuery().setLimit(integerValue(t.image)) ; } | |
} | |
void OffsetClause() : { Token t ; } | |
{ | |
<OFFSET> t = <INTEGER> | |
{ getQuery().setOffset(integerValue(t.image)) ; } | |
} | |
void ValuesClause() : { Token t ; } | |
{ | |
( | |
t = <VALUES> | |
{ startValuesClause(t.beginLine, t.beginColumn) ; } | |
DataBlock() | |
{ finishValuesClause(t.beginLine, t.beginColumn) ; } | |
)? | |
} | |
// SPARQL Update + transitional extensions for SPARQL/Update (the W3C submission) | |
// Update only entry point | |
// Strict SPARQL 1.1 : mandatory separator, optional terminator. | |
void Update() : { } | |
{ | |
Prologue() | |
(Update1() ( <SEMICOLON> Update() )? )? | |
} | |
void Update1() : { Update up ; } | |
{ | |
{ startUpdateOperation() ; } | |
( up = Load() | |
| up = Clear() | |
| up = Drop() | |
| up = Add() | |
| up = Move() | |
| up = Copy() | |
| up = Create() | |
| up = InsertData() | |
| up = DeleteData() | |
| up = DeleteWhere() | |
| up = Modify() | |
) | |
{ | |
emitUpdate(up) ; | |
finishUpdateOperation() ; | |
} | |
} | |
Update Load() : { String url ; Node dest = null ; boolean silent = false ; } | |
{ | |
// <LOAD> ( iri() )+ | |
<LOAD> (<SILENT> { silent = true ; })? url = iri() | |
(<INTO> dest = GraphRef() )? | |
{ return new UpdateLoad(url, dest, silent) ; } | |
} | |
Update Clear() : { boolean silent = false ; Target target ; } | |
{ | |
<CLEAR> (<SILENT> { silent = true ; })? target = GraphRefAll() | |
{ return new UpdateClear(target, silent) ; } | |
} | |
Update Drop() : { boolean silent = false ; Target target ; } | |
{ | |
<DROP> (<SILENT> { silent = true ; })? target = GraphRefAll() | |
{ return new UpdateDrop(target, silent) ; } | |
} | |
Update Create() : { Node iri ; boolean silent = false ; } | |
{ | |
<CREATE> (<SILENT> { silent=true ; } )? iri = GraphRef() | |
{ return new UpdateCreate(iri, silent) ; } | |
} | |
Update Add() : { Target src ; Target dest ; boolean silent = false ; } | |
{ | |
<ADD> (<SILENT> { silent=true ; } )? src = GraphOrDefault() <TO> dest = GraphOrDefault() | |
{ return new UpdateAdd(src, dest, silent) ; } | |
} | |
Update Move() : { Target src ; Target dest ; boolean silent = false ; } | |
{ | |
<MOVE> (<SILENT> { silent=true ; } )? src = GraphOrDefault() <TO> dest = GraphOrDefault() | |
{ return new UpdateMove(src, dest, silent) ; } | |
} | |
Update Copy() : { Target src ; Target dest ; boolean silent = false ; } | |
{ | |
<COPY> (<SILENT> { silent=true ; } )? src = GraphOrDefault() <TO> dest = GraphOrDefault() | |
{ return new UpdateCopy(src, dest, silent) ; } | |
} | |
// #ifdef ARQ | |
// void Meta() : { QuadDataAcc qd = new QuadDataAcc() ; } | |
// { | |
// <META> | |
// QuadData(qd) | |
// } | |
// #endif | |
Update InsertData() : { QuadDataAcc qd = new QuadDataAcc() ; Token t ; } | |
{ | |
t = <INSERT_DATA> | |
{ startDataInsert(qd, t.beginLine, t.beginColumn) ; } | |
QuadData(qd) | |
{ | |
finishDataInsert(qd, t.beginLine, t.beginColumn) ; | |
return new UpdateDataInsert(qd) ; | |
} | |
} | |
Update DeleteData() : { QuadDataAcc qd = new QuadDataAcc() ; Token t ; } | |
{ | |
t = <DELETE_DATA> | |
{ startDataDelete(qd, t.beginLine, t.beginColumn) ; } | |
QuadData(qd) | |
{ | |
finishDataDelete(qd, t.beginLine, t.beginColumn) ; | |
return new UpdateDataDelete(qd) ; | |
} | |
} | |
Update DeleteWhere() : { QuadAcc qp = new QuadAcc() ; Token t ; } | |
{ | |
t = <DELETE_WHERE> | |
{ startDeleteTemplate(qp, t.beginLine, t.beginColumn) ; } | |
QuadPattern(qp) | |
{ finishDeleteTemplate(qp, t.beginLine, t.beginColumn) ; } | |
{ return new UpdateDeleteWhere(qp) ; } | |
} | |
Update Modify() : { Element el ; String iri = null ; | |
UpdateModify up = new UpdateModify() ; } | |
{ | |
{ startModifyUpdate() ; } | |
( <WITH> iri = iri() { Node n = createNode(iri) ; up.setWithIRI(n) ; } )? | |
( DeleteClause(up) ( InsertClause(up) )? | |
| InsertClause(up) | |
) | |
(UsingClause(up))* | |
// WHERE is mandatory here. | |
<WHERE> | |
{ startWherePattern() ; } | |
el = GroupGraphPattern() { up.setElement(el) ; } | |
{ finishWherePattern() ; } | |
{ finishModifyUpdate() ; } | |
{ return up ; } | |
} | |
void DeleteClause(UpdateModify up) : { QuadAcc qp = up.getDeleteAcc() ; Token t ;} | |
{ | |
t = <DELETE> | |
{ startDeleteTemplate(qp, t.beginLine, t.beginColumn) ; } | |
QuadPattern(qp) | |
{ finishDeleteTemplate(qp, t.beginLine, t.beginColumn) ; } | |
{ up.setHasDeleteClause(true) ; } | |
} | |
void InsertClause(UpdateModify up) : { QuadAcc qp = up.getInsertAcc() ; Token t ; } | |
{ | |
t = <INSERT> | |
{ startInsertTemplate(qp, t.beginLine, t.beginColumn) ; } | |
QuadPattern(qp) | |
{ finishInsertTemplate(qp, t.beginLine, t.beginColumn) ; } | |
{ up.setHasInsertClause(true) ; } | |
} | |
void UsingClause(UpdateWithUsing update) : { String iri ; Node n ; } | |
{ | |
<USING> | |
( iri = iri() | |
{ n = createNode(iri) ; update.addUsing(n) ; } | |
| <NAMED> iri = iri() | |
{ n = createNode(iri) ; update.addUsingNamed(n) ; } | |
) | |
} | |
Target GraphOrDefault() : { String iri ; } | |
{ | |
( <DFT> { return Target.DEFAULT ; } | |
| (<GRAPH>)? | |
iri = iri() | |
{ return Target.create(createNode(iri)) ; } | |
) | |
} | |
Node GraphRef() : { String iri ; } | |
{ | |
<GRAPH> iri = iri() | |
{ return createNode(iri) ; } | |
} | |
Target GraphRefAll() : { Node iri ; } | |
{ | |
( iri = GraphRef() | |
{ return Target.create(iri) ; } | |
| <DFT> { return Target.DEFAULT ; } | |
| <NAMED> { return Target.NAMED ; } | |
| <ALL> { return Target.ALL ; } | |
) | |
} | |
void QuadPattern(QuadAcc acc) : { } | |
{ | |
<LBRACE> | |
Quads(acc) | |
<RBRACE> | |
} | |
//Ground data : As QuadPattern but don't allow variables. | |
void QuadData(QuadDataAcc acc) : { } | |
{ | |
<LBRACE> | |
Quads(acc) | |
<RBRACE> | |
} | |
void Quads(QuadAcc acc) : { } | |
{ | |
(TriplesTemplate(acc))? | |
( | |
QuadsNotTriples(acc) | |
(<DOT>)? | |
(TriplesTemplate(acc))? | |
)* | |
} | |
void QuadsNotTriples(QuadAcc acc) : {Node gn ; Node prev = acc.getGraph() ; } | |
{ | |
<GRAPH> gn = VarOrIri() | |
{ setAccGraph(acc, gn) ; } | |
<LBRACE> | |
(TriplesTemplate(acc))? | |
<RBRACE> | |
{ setAccGraph(acc, prev) ; } | |
} | |
void TriplesTemplate(TripleCollector acc) : { } | |
{ // same as ConstructTriples | |
// Version for the spec. | |
TriplesSameSubject(acc) | |
(<DOT> (TriplesTemplate(acc))?)? | |
} | |
// ---- General Graph Pattern | |
Element GroupGraphPattern() : { Element el = null ; Token t ; } | |
{ | |
t = <LBRACE> | |
( | |
{ startSubSelect(t.beginLine, t.beginColumn) ; } | |
SubSelect() | |
{ | |
Query q = endSubSelect(t.beginLine, t.beginColumn) ; | |
el = new ElementSubQuery(q) ; | |
} | |
| el = GroupGraphPatternSub() | |
) | |
<RBRACE> | |
{ return el ; } | |
} | |
Element GroupGraphPatternSub() : { Element el = null ; } | |
{ | |
{ ElementGroup elg = new ElementGroup() ; } | |
{ startGroup(elg) ; } | |
// Ensure two BGP's can't be next to each other | |
// Done by seeing if there is a non-BGP and recursing | |
// if there is an intermediate | |
( | |
{ startTriplesBlock() ; } | |
el = TriplesBlock(null) | |
{ endTriplesBlock() ; | |
elg.addElement(el) ; } | |
)? | |
( | |
el = GraphPatternNotTriples() | |
{ elg.addElement(el) ; } | |
(<DOT>)? | |
( | |
{ startTriplesBlock() ; } | |
el = TriplesBlock(null) | |
{ endTriplesBlock() ; | |
elg.addElement(el) ; } | |
)? | |
)* | |
{ endGroup(elg) ; } | |
{ return elg ; } | |
} | |
Element TriplesBlock(ElementPathBlock acc) : { } | |
{ | |
{ if ( acc == null ) | |
acc = new ElementPathBlock() ; | |
} | |
TriplesSameSubjectPath(acc) | |
( <DOT> (TriplesBlock(acc))? )? | |
{ return acc ; } | |
} | |
// ----- | |
Element GraphPatternNotTriples() : { Element el = null ; } | |
{ | |
( | |
// el = GroupGraphPattern() | |
// | | |
// el = UnionGraphPattern() | |
// | | |
el = GroupOrUnionGraphPattern() | |
| | |
el = OptionalGraphPattern() | |
| | |
el = MinusGraphPattern() | |
| | |
el = GraphGraphPattern() | |
| | |
el = ServiceGraphPattern() | |
| | |
el = Filter() | |
| | |
el = Bind() | |
| | |
el = InlineData() | |
) | |
{ return el ; } | |
} | |
// ---- Definitions of each pattern element | |
Element OptionalGraphPattern() : { Element el ; } | |
{ <OPTIONAL> el = GroupGraphPattern() | |
{ return new ElementOptional(el) ; } | |
} | |
Element GraphGraphPattern() : { Element el ; Node n ;} | |
{ | |
<GRAPH> n = VarOrIri() el = GroupGraphPattern() | |
{ return new ElementNamedGraph(n, el) ; } | |
} | |
Element ServiceGraphPattern() : { Element el ; Node n ; boolean silent = false ; } | |
{ | |
<SERVICE> | |
(<SILENT> | |
{ silent=true; } | |
)? | |
n = VarOrIri() | |
el = GroupGraphPattern() | |
{ return new ElementService(n, el, silent) ; } | |
} | |
Element Bind() : { Var v ; Expr expr ; } | |
{ | |
<BIND> | |
<LPAREN> | |
expr = Expression() | |
<AS> | |
v = Var() | |
<RPAREN> | |
{ return new ElementBind(v, expr) ; } | |
} | |
Element InlineData() : { ElementData el ; Token t ; } | |
{ | |
t = <VALUES> | |
{ el = new ElementData() ; | |
startInlineData(el.getVars(), el.getRows(), t.beginLine, t.beginColumn) ; } | |
DataBlock() | |
{ finishInlineData(t.beginLine, t.beginColumn) ; | |
return el ; } | |
} | |
void DataBlock() : { } | |
{ | |
( InlineDataOneVar() | InlineDataFull() ) | |
} | |
void InlineDataOneVar() : { Var v ; Node n ; Token t ; } | |
{ | |
v = Var() | |
{ emitDataBlockVariable(v) ; } | |
t = <LBRACE> | |
( | |
n = DataBlockValue() | |
{ startDataBlockValueRow(-1, -1) ; | |
emitDataBlockValue(n, -1, -1) ; | |
finishDataBlockValueRow(-1, -1) ; | |
} | |
)* | |
t = <RBRACE> | |
} | |
void InlineDataFull() : { Var v ; Node n ; Token t ; } | |
{ | |
( | |
<NIL> | |
| | |
t = <LPAREN> | |
(v = Var() { emitDataBlockVariable(v) ; })* | |
t = <RPAREN> | |
) | |
t = <LBRACE> | |
( | |
t = <LPAREN> | |
{ startDataBlockValueRow(t.beginLine, t.beginColumn) ; } | |
(n = DataBlockValue() | |
{ emitDataBlockValue(n, t.beginLine, t.beginColumn) ; } | |
) * | |
t = <RPAREN> | |
{ finishDataBlockValueRow(t.beginLine, t.beginColumn) ; } | |
| | |
t = <NIL> | |
{ startDataBlockValueRow(t.beginLine, t.beginColumn) ; } | |
{ finishDataBlockValueRow(t.beginLine, t.beginColumn) ; } | |
)* | |
t = <RBRACE> | |
} | |
Node DataBlockValue() : { Node n ; String iri ; } | |
{ | |
iri = iri() { return createNode(iri) ; } | |
| n = RDFLiteral() { return n ; } | |
| n = NumericLiteral() { return n ; } | |
| n = BooleanLiteral() { return n ; } | |
| <UNDEF> { return null ; } | |
} | |
Element MinusGraphPattern() : { Element el ; } | |
{ | |
<MINUS_P> | |
el = GroupGraphPattern() | |
{ return new ElementMinus(el) ; } | |
} | |
// Element UnionGraphPattern() : { Element el ; } | |
// { | |
// <UNION> | |
// el = GroupGraphPattern() | |
// { return new ElementUnion(el) ; } | |
// } | |
// SPARQL 1.0: {pattern} UNION {pattern} UNION {pattern} ... :: | |
// SPARQL 1.1 may introduce: { pattern UNION pattern UNION ... } | |
// G (union G)* can be a single group pattern | |
// or a group pattern as part of an union. | |
Element GroupOrUnionGraphPattern() : | |
{ Element el = null ; ElementUnion el2 = null ; } | |
{ | |
el = GroupGraphPattern() | |
( <UNION> | |
{ if ( el2 == null ) | |
{ | |
el2 = new ElementUnion() ; | |
el2.addElement(el) ; | |
} | |
} | |
el = GroupGraphPattern() | |
{ el2.addElement(el) ; } | |
)* | |
{ return (el2==null)? el : el2 ; } | |
} | |
Element Filter() : { Expr c ; } | |
{ | |
<FILTER> c = Constraint() | |
{ return new ElementFilter(c) ; } | |
} | |
Expr Constraint() : { Expr c ; } | |
{ | |
( c = BrackettedExpression() | |
| c = BuiltInCall() | |
| c = FunctionCall() | |
) | |
{ return c ; } | |
} | |
Expr FunctionCall() : { String fname ; ExprList a ; } | |
{ | |
fname = iri() | |
a = ArgList() | |
{ return new E_Function(fname, a) ; } | |
} | |
ExprList ArgList() : { Expr expr ; boolean distinct = false ; | |
ExprList args = new ExprList() ; Token t ; } | |
{ | |
( | |
<NIL> | |
| | |
<LPAREN> | |
(t = <DISTINCT> { distinct = true ; } | |
{ | |
if ( ! allowAggregatesInExpressions ) | |
throwParseException("Aggregate expression not legal at this point", | |
t.beginLine, t.beginColumn) ; | |
} | |
)? | |
expr = Expression() { args.add(expr) ; } | |
(<COMMA> expr = Expression() { args.add(expr) ; } )* | |
<RPAREN> | |
) | |
{ return args ; } | |
} | |
ExprList ExpressionList() : { Expr expr = null ; ExprList args = new ExprList() ;} | |
{ | |
( | |
<NIL> | |
| | |
<LPAREN> | |
expr = Expression() { args.add(expr) ; } | |
(<COMMA> expr = Expression() { args.add(expr) ; } )* | |
<RPAREN> | |
) | |
{ return args ; } | |
} | |
// -------- Construct patterns | |
Template ConstructTemplate() : { TripleCollectorBGP acc = new TripleCollectorBGP(); | |
Template t = new Template(acc.getBGP()) ; } | |
{ | |
{ setInConstructTemplate(true) ; } | |
<LBRACE> | |
(ConstructTriples(acc))? | |
<RBRACE> | |
{ setInConstructTemplate(false) ; | |
return t ; } | |
} | |
void ConstructTriples(TripleCollector acc) : { } | |
{ // Same as TriplesTemplate, but retain for 1.0 legacy | |
// SPARQL - recursion - does not scale for SPARQL/Update | |
TriplesSameSubject(acc) | |
(<DOT> (ConstructTriples(acc))? )? | |
} | |
// -------- Triple lists with property and object lists | |
// -------- Without paths: entry: TriplesSameSubject | |
void TriplesSameSubject(TripleCollector acc) : { Node s ; } | |
{ | |
s = VarOrTerm() | |
PropertyListNotEmpty(s, acc) | |
| | |
// Any of the triple generating syntax elements | |
s = TriplesNode(acc) | |
PropertyList(s, acc) | |
} | |
void PropertyList(Node s, TripleCollector acc) : { } | |
{ | |
( PropertyListNotEmpty(s, acc) ) ? | |
} | |
void PropertyListNotEmpty(Node s, TripleCollector acc) : | |
{ Node p = null ; } | |
{ | |
p = Verb() | |
ObjectList(s, p, null, acc) | |
( <SEMICOLON> | |
( | |
p = Verb() | |
ObjectList(s, p, null, acc) | |
)? | |
)* | |
} | |
Node Verb() : { Node p ;} | |
{ | |
// Blank nodes as predicates | |
// ( p = VarOrBlankNodeOriri() | <KW_A> { p = nRDFtype ; } ) | |
( p = VarOrIri() | <KW_A> { p = nRDFtype ; } ) | |
{ return p ; } | |
} | |
void ObjectList(Node s, Node p, Path path, TripleCollector acc): { Node o ; } | |
{ | |
Object(s, p, path, acc) | |
( <COMMA> Object(s, p, path, acc) )* | |
} | |
void Object(Node s, Node p, Path path, TripleCollector acc): { Node o ; } | |
{ | |
{ int mark = acc.mark() ; } | |
o = GraphNode(acc) | |
{ insert(acc, mark, s, p, path, o) ; } | |
} | |
// -------- BGPs with paths. | |
// -------- Entry point: TriplesSameSubjectPath | |
void TriplesSameSubjectPath(TripleCollector acc) : { Node s ; } | |
{ | |
s = VarOrTerm() | |
PropertyListPathNotEmpty(s, acc) | |
| | |
// Any of the triple generating syntax elements | |
s = TriplesNodePath(acc) | |
PropertyListPath(s, acc) | |
} | |
void PropertyListPath(Node s, TripleCollector acc) : { } | |
{ | |
( PropertyListPathNotEmpty(s, acc) ) ? | |
} | |
void PropertyListPathNotEmpty(Node s, TripleCollector acc) : | |
{ Path path = null ; Node p = null ; } | |
{ | |
( path = VerbPath() | |
| p = VerbSimple() | |
) | |
ObjectListPath(s, p, path, acc) | |
( <SEMICOLON> | |
{ path = null ; p = null ; } | |
( | |
( path = VerbPath() | |
| p = VerbSimple() | |
) | |
ObjectList(s, p, path, acc) | |
)? | |
)* | |
} | |
Path VerbPath() : {Node p ; Path path ; } | |
{ | |
path = Path() { return path ; } | |
} | |
Node VerbSimple() : { Node p ; } | |
{ | |
// "a" now allowed in paths. | |
//( p = Var() | <KW_A> { p = nRDFtype ; } ) | |
p = Var() | |
{ return p ; } | |
} | |
void ObjectListPath(Node s, Node p, Path path, TripleCollector acc): { Node o ; } | |
{ | |
ObjectPath(s, p, path, acc) | |
( <COMMA> ObjectPath(s, p, path, acc) )* | |
} | |
void ObjectPath(Node s, Node p, Path path, TripleCollector acc): { Node o ; } | |
{ | |
{ int mark = acc.mark() ; } | |
o = GraphNodePath(acc) | |
{ insert(acc, mark, s, p, path, o) ; } | |
} | |
// End paths stuff. | |
// -------- Paths | |
// Weakest outermost | |
Path Path() : { Path p ; } | |
{ | |
p = PathAlternative() { return p ; } | |
} | |
Path PathAlternative() : { Path p1 , p2 ; } | |
{ | |
p1 = PathSequence() | |
( | |
<VBAR> p2 = PathSequence() | |
{ p1 = PathFactory.pathAlt(p1, p2) ; } | |
)* | |
{ return p1 ; } | |
} | |
Path PathSequence() : { Path p1 , p2 ; } | |
{ | |
p1 = PathEltOrInverse() | |
( <SLASH> p2 = PathEltOrInverse() | |
{ p1 = PathFactory.pathSeq(p1, p2) ; } | |
)* | |
{ return p1; } | |
} | |
// Path unit element, no inverse | |
Path PathElt() : { String str ; Node n ; Path p ; } | |
{ | |
p = PathPrimary() | |
( p = PathMod(p) )? | |
{ return p ; } | |
} | |
// Path unit element, including inverse. | |
Path PathEltOrInverse() : { String str ; Node n ; Path p ; } | |
{ | |
( p = PathElt() | |
| <CARAT> | |
p = PathElt() | |
{ p = PathFactory.pathInverse(p) ; } | |
) | |
{ return p ; } | |
} | |
Path PathMod(Path p) : { long i1 ; long i2 ; } | |
{ | |
( <QMARK> { return PathFactory.pathZeroOrOne(p) ; } | |
| <STAR> { return PathFactory.pathZeroOrMore1(p) ; } | |
| <PLUS> { return PathFactory.pathOneOrMore1(p) ; } | |
) | |
} | |
Path PathPrimary() : { String str ; Path p ; Node n ; } | |
{ | |
( | |
str = iri() | |
{ n = createNode(str) ; p = PathFactory.pathLink(n) ; } | |
| <KW_A> | |
{ p = PathFactory.pathLink(nRDFtype) ; } | |
| <BANG> p = PathNegatedPropertySet() | |
| <LPAREN> p = Path() <RPAREN> | |
) | |
{ return p ; } | |
} | |
Path PathNegatedPropertySet() : { P_Path0 p ; P_NegPropSet pNegSet ; } | |
{ | |
{ pNegSet = new P_NegPropSet() ; } | |
( p = PathOneInPropertySet() | |
{ pNegSet.add(p) ; } | |
| <LPAREN> | |
( p = PathOneInPropertySet() { pNegSet.add(p) ; } | |
(<VBAR> p = PathOneInPropertySet() { pNegSet.add(p) ; }) * | |
)? | |
<RPAREN> | |
) | |
{ return pNegSet ; } | |
} | |
P_Path0 PathOneInPropertySet() : { String str ; Node n ; } | |
{ | |
( str = iri() { n = createNode(str) ; return new P_Link(n) ; } | |
| <KW_A> { return new P_Link(nRDFtype) ; } | |
// This is the !(^:property) form. | |
| <CARAT> | |
( str = iri() { n = createNode(str) ; return new P_ReverseLink(n) ; } | |
| <KW_A> { return new P_ReverseLink(nRDFtype) ; } | |
) | |
) | |
} | |
long Integer() : {Token t ;} | |
{ | |
t = <INTEGER> | |
{ return integerValue(t.image) ; } | |
} | |
// -------- Triple expansions | |
// Anything that can stand in a node slot and which is | |
// a number of triples | |
Node TriplesNode(TripleCollector acc) : { Node n ; } | |
{ | |
n = Collection(acc) { return n ; } | |
| | |
n = BlankNodePropertyList(acc) { return n ; } | |
} | |
Node BlankNodePropertyList(TripleCollector acc) : { Token t ; } | |
{ | |
t = <LBRACKET> | |
{ Node n = createBNode( t.beginLine, t.beginColumn) ; } | |
PropertyListNotEmpty(n, acc) | |
<RBRACKET> | |
{ return n ; } | |
} | |
Node TriplesNodePath(TripleCollector acc) : { Node n ; } | |
{ | |
n = CollectionPath(acc) { return n ; } | |
| | |
n = BlankNodePropertyListPath(acc) { return n ; } | |
} | |
Node BlankNodePropertyListPath(TripleCollector acc) : { Token t ; } | |
{ | |
t = <LBRACKET> | |
{ Node n = createBNode( t.beginLine, t.beginColumn) ; } | |
PropertyListPathNotEmpty(n, acc) | |
<RBRACKET> | |
{ return n ; } | |
} | |
// ------- RDF collections | |
Node Collection(TripleCollector acc) : | |
{ Node listHead = nRDFnil ; Node lastCell = null ; int mark ; Node n ; Token t ; } | |
{ | |
t = <LPAREN> | |
( | |
{ Node cell = createListNode( t.beginLine, t.beginColumn) ; | |
if ( listHead == nRDFnil ) | |
listHead = cell ; | |
if ( lastCell != null ) | |
insert(acc, lastCell, nRDFrest, cell) ; | |
mark = acc.mark() ; | |
} | |
n = GraphNode(acc) | |
{ | |
insert(acc, mark, cell, nRDFfirst, n) ; | |
lastCell = cell ; | |
} | |
) + | |
// Not * here - "()" is handled separately. | |
<RPAREN> | |
{ if ( lastCell != null ) | |
insert(acc, lastCell, nRDFrest, nRDFnil) ; | |
return listHead ; } | |
} | |
Node CollectionPath(TripleCollector acc) : | |
{ Node listHead = nRDFnil ; Node lastCell = null ; int mark ; Node n ; Token t ; } | |
{ | |
t = <LPAREN> | |
( | |
{ Node cell = createListNode( t.beginLine, t.beginColumn) ; | |
if ( listHead == nRDFnil ) | |
listHead = cell ; | |
if ( lastCell != null ) | |
insert(acc, lastCell, nRDFrest, cell) ; | |
mark = acc.mark() ; | |
} | |
n = GraphNodePath(acc) | |
{ | |
insert(acc, mark, cell, nRDFfirst, n) ; | |
lastCell = cell ; | |
} | |
) + | |
// Not * here - "()" is handled separately. | |
<RPAREN> | |
{ if ( lastCell != null ) | |
insert(acc, lastCell, nRDFrest, nRDFnil) ; | |
return listHead ; } | |
} | |
// -------- Nodes in a graph pattern or template | |
Node GraphNode(TripleCollector acc) : { Node n ; } | |
{ | |
n = VarOrTerm() { return n ; } | |
| | |
n = TriplesNode(acc) { return n ; } | |
} | |
Node GraphNodePath(TripleCollector acc) : { Node n ; } | |
{ | |
n = VarOrTerm() { return n ; } | |
| | |
n = TriplesNodePath(acc) { return n ; } | |
} | |
Node VarOrTerm() : {Node n = null ; } | |
{ | |
( n = Var() | n = GraphTerm() ) | |
{ return n ; } | |
} | |
// Property (if no bNodes) + DESCRIBE | |
Node VarOrIri() : {Node n = null ; String iri ; } | |
{ | |
( n = Var() | iri = iri() { n = createNode(iri) ; } ) | |
{ return n ; } | |
} | |
Var Var() : { Token t ;} | |
{ | |
( t = <VAR1> | t = <VAR2> ) | |
{ return createVariable(t.image, t.beginLine, t.beginColumn) ; } | |
} | |
Node GraphTerm() : { Node n ; String iri ; } | |
{ | |
iri = iri() { return createNode(iri) ; } | |
| n = RDFLiteral() { return n ; } | |
| n = NumericLiteral() { return n ; } | |
| n = BooleanLiteral() { return n ; } | |
| n = BlankNode() { return n ; } | |
// <LPAREN> <RPAREN> { return nRDFnil ; } | |
| <NIL> { return nRDFnil ; } | |
} | |
// -------- Constraint syntax | |
Expr Expression() : { Expr expr ; } | |
{ | |
expr = ConditionalOrExpression() | |
{ return expr ; } | |
} | |
Expr ConditionalOrExpression() : { Expr expr1, expr2 ; } | |
{ | |
expr1 = ConditionalAndExpression() | |
( <SC_OR> expr2 = ConditionalAndExpression() | |
{ expr1 = new E_LogicalOr(expr1, expr2) ; } | |
)* | |
{ return expr1 ; } | |
} | |
Expr ConditionalAndExpression() : { Expr expr1, expr2 ;} | |
{ | |
expr1 = ValueLogical() | |
( <SC_AND> expr2 = ValueLogical() | |
{ expr1 = new E_LogicalAnd(expr1, expr2) ; } | |
)* | |
{ return expr1 ; } | |
} | |
Expr ValueLogical() : { Expr expr ; } | |
{ | |
expr = RelationalExpression() | |
{ return expr ; } | |
} | |
Expr RelationalExpression() : { Expr expr1, expr2 ; ExprList a ; } | |
{ | |
expr1 = NumericExpression() | |
( | |
<EQ> expr2 = NumericExpression() | |
{ expr1 = new E_Equals(expr1, expr2) ; } | |
| <NE> expr2 = NumericExpression() | |
{ expr1 = new E_NotEquals(expr1, expr2) ; } | |
| <LT> expr2 = NumericExpression() | |
{ expr1 = new E_LessThan(expr1, expr2) ; } | |
| <GT> expr2 = NumericExpression() | |
{ expr1 = new E_GreaterThan(expr1, expr2) ; } | |
| <LE> expr2 = NumericExpression() | |
{ expr1 = new E_LessThanOrEqual(expr1, expr2) ; } | |
| <GE> expr2 = NumericExpression() | |
{ expr1 = new E_GreaterThanOrEqual(expr1, expr2) ; } | |
| <IN> a = ExpressionList() | |
{ expr1 = new E_OneOf(expr1, a) ; } | |
| <NOT> <IN> a = ExpressionList() | |
{ expr1 = new E_NotOneOf(expr1, a) ; } | |
)? | |
{ return expr1 ; } | |
} | |
Expr NumericExpression () : { Expr expr ; } | |
{ | |
expr = AdditiveExpression() | |
{ return expr ; } | |
} | |
Expr AdditiveExpression() : { Expr expr1, expr2, expr3 ; boolean addition ; Node n ; } | |
{ | |
expr1 = MultiplicativeExpression() | |
( <PLUS> expr2 = MultiplicativeExpression() | |
{ expr1 = new E_Add(expr1, expr2) ; } | |
| <MINUS> expr2 = MultiplicativeExpression() | |
{ expr1 = new E_Subtract(expr1, expr2) ; } | |
| | |
( | |
n = NumericLiteralPositive() | |
{ | |
n = stripSign(n) ; | |
expr2 = asExpr(n) ; | |
addition = true ; | |
} | |
| | |
n = NumericLiteralNegative() | |
{ | |
n = stripSign(n) ; | |
expr2 = asExpr(n) ; | |
addition = false ; | |
} | |
) | |
( | |
( <STAR> expr3 = UnaryExpression() { expr2 = new E_Multiply(expr2, expr3) ; } ) | |
| | |
( <SLASH> expr3 = UnaryExpression() { expr2 = new E_Divide(expr2, expr3) ; } ) | |
)* | |
{ if ( addition ) | |
expr1 = new E_Add(expr1, expr2) ; | |
else | |
expr1 = new E_Subtract(expr1, expr2) ; | |
} | |
)* | |
{ return expr1 ; } | |
} | |
Expr MultiplicativeExpression() : { Expr expr1, expr2 ; } | |
{ | |
expr1 = UnaryExpression() | |
( <STAR> expr2 = UnaryExpression() | |
{ expr1 = new E_Multiply(expr1, expr2) ; } | |
| <SLASH> expr2 = UnaryExpression() | |
{ expr1 = new E_Divide(expr1, expr2) ; } | |
// | <REM> expr2 = UnaryExpression() | |
// { expr1 = new E_Modulus(expr1, expr2) ; } | |
)* | |
{ return expr1 ; } | |
} | |
Expr UnaryExpression() : { Expr expr ; } | |
{ | |
<BANG> expr = PrimaryExpression() | |
{ return new E_LogicalNot(expr) ; } | |
| <PLUS> expr = PrimaryExpression() { return new E_UnaryPlus(expr) ; } | |
| <MINUS> expr = PrimaryExpression() { return new E_UnaryMinus(expr) ; } | |
| expr = PrimaryExpression() { return expr ; } | |
} | |
Expr PrimaryExpression() : { Expr expr ; Node gn ; } | |
{ | |
( expr = BrackettedExpression() { return expr ; } | |
| expr = BuiltInCall() { return expr ; } | |
| expr = iriOrFunction() { return expr ; } | |
// NOT | gn = VarOrTerm() { return asExpr(gn) ; } | |
// Because of iriOrFunction | |
| gn = RDFLiteral() { return asExpr(gn) ; } | |
| gn = NumericLiteral() { return asExpr(gn) ; } | |
| gn = BooleanLiteral() { return asExpr(gn) ; } | |
| gn = Var() { return asExpr(gn) ; } | |
) | |
} | |
Expr BrackettedExpression() : { Expr expr ; } | |
{ | |
<LPAREN> expr = Expression() <RPAREN> { return expr ; } | |
} | |
Expr BuiltInCall() : { Expr expr ; Expr expr1 = null ; Expr expr2 = null ; | |
Node gn ; Token t ; ExprList a ; } | |
{ | |
expr = Aggregate() { return expr ; } | |
| | |
<STR> <LPAREN> expr = Expression() <RPAREN> | |
{ return new E_Str(expr) ; } | |
| <LANG> <LPAREN> expr = Expression() <RPAREN> | |
{ return new E_Lang(expr) ; } | |
| <LANGMATCHES> | |
<LPAREN> expr1 = Expression() <COMMA> expr2 = Expression() <RPAREN> | |
{ return new E_LangMatches(expr1, expr2) ; } | |
| <DTYPE> <LPAREN> expr = Expression() <RPAREN> | |
{ return new E_Datatype(expr) ; } | |
| <BOUND> <LPAREN> gn = Var() <RPAREN> | |
{ return new E_Bound(new ExprVar(gn)) ; } | |
| <IRI> <LPAREN> expr = Expression() <RPAREN> | |
{ return new E_IRI(expr) ; } | |
| <URI> <LPAREN> expr = Expression() <RPAREN> | |
{ return new E_URI(expr) ; } | |
| <BNODE> | |
( <LPAREN> expr1 = Expression() <RPAREN> | |
{ return new E_BNode(expr1) ; } | |
| | |
<NIL> { return new E_BNode() ; } | |
) | |
| <RAND> <NIL> { return new E_Random() ; } | |
| <ABS> <LPAREN> expr1 = Expression() <RPAREN> { return new E_NumAbs(expr1) ; } | |
| <CEIL> <LPAREN> expr1 = Expression() <RPAREN> { return new E_NumCeiling(expr1) ; } | |
| <FLOOR> <LPAREN> expr1 = Expression() <RPAREN> { return new E_NumFloor(expr1) ; } | |
| <ROUND> <LPAREN> expr1 = Expression() <RPAREN> { return new E_NumRound(expr1) ; } | |
| <CONCAT> a = ExpressionList() { return new E_StrConcat(a) ; } | |
| expr = SubstringExpression() { return expr ; } | |
| <STRLEN> <LPAREN> expr1 = Expression() <RPAREN> { return new E_StrLength(expr1) ; } | |
| expr = StrReplaceExpression() { return expr ; } | |
| <UCASE> <LPAREN> expr1 = Expression() <RPAREN> { return new E_StrUpperCase(expr1) ; } | |
| <LCASE> <LPAREN> expr1 = Expression() <RPAREN> { return new E_StrLowerCase(expr1) ; } | |
| <ENCODE_FOR_URI> <LPAREN> expr1 = Expression() <RPAREN> { return new E_StrEncodeForURI(expr1) ; } | |
| <CONTAINS> <LPAREN> expr1 = Expression() <COMMA> expr2 = Expression() <RPAREN> | |
{ return new E_StrContains(expr1, expr2) ; } | |
| <STRSTARTS> <LPAREN> expr1 = Expression() <COMMA> expr2 = Expression() <RPAREN> | |
{ return new E_StrStartsWith(expr1, expr2) ; } | |
| <STRENDS> <LPAREN> expr1 = Expression() <COMMA> expr2 = Expression() <RPAREN> | |
{ return new E_StrEndsWith(expr1, expr2) ; } | |
| <STRBEFORE> <LPAREN> expr1 = Expression() <COMMA> expr2 = Expression() <RPAREN> | |
{ return new E_StrBefore(expr1, expr2) ; } | |
| <STRAFTER> <LPAREN> expr1 = Expression() <COMMA> expr2 = Expression() <RPAREN> | |
{ return new E_StrAfter(expr1, expr2) ; } | |
| <YEAR> <LPAREN> expr1 = Expression() <RPAREN> { return new E_DateTimeYear(expr1) ; } | |
| <MONTH> <LPAREN> expr1 = Expression() <RPAREN> { return new E_DateTimeMonth(expr1) ; } | |
| <DAY> <LPAREN> expr1 = Expression() <RPAREN> { return new E_DateTimeDay(expr1) ; } | |
| <HOURS> <LPAREN> expr1 = Expression() <RPAREN> { return new E_DateTimeHours(expr1) ; } | |
| <MINUTES> <LPAREN> expr1 = Expression() <RPAREN> { return new E_DateTimeMinutes(expr1) ; } | |
| <SECONDS> <LPAREN> expr1 = Expression() <RPAREN> { return new E_DateTimeSeconds(expr1) ; } | |
| <TIMEZONE> <LPAREN> expr1 = Expression() <RPAREN> { return new E_DateTimeTimezone(expr1) ; } | |
| <TZ> <LPAREN> expr1 = Expression() <RPAREN> { return new E_DateTimeTZ(expr1) ; } | |
| <NOW> <NIL> { return new E_Now() ; } | |
| <UUID> <NIL> { return new E_UUID() ; } | |
| <STRUUID> <NIL> { return new E_StrUUID() ; } | |
| <MD5> <LPAREN> expr1 = Expression() <RPAREN> { return new E_MD5(expr1) ; } | |
| <SHA1> <LPAREN> expr1 = Expression() <RPAREN> { return new E_SHA1(expr1) ; } | |
| <SHA256> <LPAREN> expr1 = Expression() <RPAREN> { return new E_SHA256(expr1) ; } | |
| <SHA384> <LPAREN> expr1 = Expression() <RPAREN> { return new E_SHA384(expr1) ; } | |
| <SHA512> <LPAREN> expr1 = Expression() <RPAREN> { return new E_SHA512(expr1) ; } | |
| <COALESCE> a = ExpressionList() | |
{ return new E_Coalesce(a) ; } | |
| <IF> <LPAREN> expr = Expression() <COMMA> | |
expr1 = Expression() <COMMA> | |
expr2 = Expression() <RPAREN> | |
{ return new E_Conditional(expr, expr1, expr2) ; } | |
| <STRLANG> <LPAREN> expr1 = Expression() <COMMA> expr2 = Expression() <RPAREN> | |
{ return new E_StrLang(expr1, expr2) ; } | |
| <STRDT> <LPAREN> expr1 = Expression() <COMMA> expr2 = Expression() <RPAREN> | |
{ return new E_StrDatatype(expr1, expr2) ; } | |
| <SAME_TERM> <LPAREN> expr1 = Expression() <COMMA> expr2 = Expression() <RPAREN> | |
{ return new E_SameTerm(expr1, expr2) ; } | |
| t = <IS_IRI> <LPAREN> expr = Expression() <RPAREN> | |
{ return new E_IsIRI(expr) ; } | |
| t = <IS_URI> <LPAREN> expr = Expression() <RPAREN> | |
{ return new E_IsURI(expr) ; } | |
| <IS_BLANK> <LPAREN> expr = Expression() <RPAREN> | |
{ return new E_IsBlank(expr) ; } | |
| <IS_LITERAL> <LPAREN> expr = Expression() <RPAREN> | |
{ return new E_IsLiteral(expr) ; } | |
| <IS_NUMERIC> <LPAREN> expr = Expression() <RPAREN> | |
{ return new E_IsNumeric(expr) ; } | |
| // Regular expression matcher | |
expr = RegexExpression() { return expr ; } | |
| expr = ExistsFunc() { return expr ; } | |
| expr = NotExistsFunc() { return expr ; } | |
} | |
Expr RegexExpression() : | |
{ Expr expr ; Expr patExpr = null ; Expr flagsExpr = null ; } | |
{ | |
<REGEX> | |
<LPAREN> | |
expr = Expression() | |
<COMMA> | |
patExpr = Expression() | |
( <COMMA> flagsExpr = Expression() ) ? | |
<RPAREN> | |
{ return new E_Regex(expr, patExpr, flagsExpr) ; } | |
} | |
Expr SubstringExpression() : | |
{ Expr expr1 ; Expr expr2 = null ; Expr expr3 = null ; } | |
{ | |
<SUBSTR> | |
<LPAREN> | |
expr1 = Expression() | |
<COMMA> | |
expr2 = Expression() | |
( <COMMA> expr3 = Expression() ) ? | |
<RPAREN> | |
{ return new E_StrSubstring(expr1, expr2, expr3) ; } | |
} | |
Expr StrReplaceExpression() : | |
{ Expr expr1 ; Expr expr2 = null ; Expr expr3 = null ; Expr expr4 = null ;} | |
{ | |
<REPLACE> | |
<LPAREN> | |
expr1 = Expression() | |
<COMMA> expr2 = Expression() | |
<COMMA> expr3 = Expression() | |
( <COMMA> expr4 = Expression() ) ? | |
<RPAREN> | |
{ return new E_StrReplace(expr1,expr2,expr3,expr4) ; } | |
} | |
Expr ExistsFunc() : { Element el ; } | |
{ | |
<EXISTS> | |
el = GroupGraphPattern() | |
{ return createExprExists(el) ; } | |
} | |
Expr NotExistsFunc() : { Element el ; } | |
{ | |
<NOT> <EXISTS> | |
el = GroupGraphPattern() | |
{ return createExprNotExists(el) ; } | |
} | |
Expr Aggregate() : { Aggregator agg = null ; String sep = null ; | |
boolean distinct = false ; | |
Expr expr = null ; Expr expr2 = null ; | |
ExprList a = new ExprList() ; | |
ExprList ordered = new ExprList() ; | |
Token t ; } | |
{ | |
// Count is special because of COUNT(*) | |
// GROUP_CONCAT is special because of separator= | |
( t = <COUNT> <LPAREN> | |
( <DISTINCT> { distinct = true ; } )? | |
( <STAR> | expr = Expression() ) | |
<RPAREN> | |
{ if ( expr == null ) { agg = AggregatorFactory.createCount(distinct) ; } | |
if ( expr != null ) { agg = AggregatorFactory.createCountExpr(distinct, expr) ; } | |
} | |
| t = <SUM> <LPAREN> ( <DISTINCT> { distinct = true ; } )? expr = Expression() <RPAREN> | |
{ agg = AggregatorFactory.createSum(distinct, expr) ; } | |
| t = <MIN> <LPAREN> ( <DISTINCT> { distinct = true ; } )? expr = Expression() <RPAREN> | |
{ agg = AggregatorFactory.createMin(distinct, expr) ; } | |
| t = <MAX> <LPAREN> ( <DISTINCT> { distinct = true ; } )? expr = Expression() <RPAREN> | |
{ agg = AggregatorFactory.createMax(distinct, expr) ; } | |
| t = <AVG> <LPAREN> ( <DISTINCT> { distinct = true ; } )? expr = Expression() <RPAREN> | |
{ agg = AggregatorFactory.createAvg(distinct, expr) ; } | |
| t = <SAMPLE> <LPAREN> ( <DISTINCT> { distinct = true ; } )? expr = Expression() <RPAREN> | |
{ agg = AggregatorFactory.createSample(distinct, expr) ; } | |
| t = <GROUP_CONCAT> | |
<LPAREN> | |
(t = <DISTINCT> { distinct = true ; })? | |
expr = Expression() { a.add(expr) ; } | |
// Single arg version | |
(<SEMICOLON> <SEPARATOR> <EQ> sep=String())? | |
<RPAREN> | |
{ agg = AggregatorFactory.createGroupConcat(distinct, expr, sep, ordered) ; } | |
) | |
{ | |
if ( ! allowAggregatesInExpressions ) | |
throwParseException("Aggregate expression not legal at this point", | |
t.beginLine, t.beginColumn) ; | |
} | |
{ Expr exprAgg = getQuery().allocAggregate(agg) ; | |
return exprAgg ; } | |
} | |
// See also FunctionCall. | |
// The case of "q:name()" or "q:agg()" or just "q:name" | |
// by expanding out FunctionCall() | |
Expr iriOrFunction() : { String iri ; ExprList a = null ; | |
ExprList params = null ; | |
boolean distinct = false ; } | |
{ | |
iri = iri() | |
(a = ArgList())? | |
{ if ( a == null ) | |
return asExpr(createNode(iri)) ; | |
return new E_Function(iri, a) ; | |
} | |
} | |
Node RDFLiteral() : { Token t ; String lex = null ; } | |
{ | |
lex = String() | |
// Optional lang tag and datatype. | |
{ String lang = null ; String uri = null ; } | |
( | |
( t = <LANGTAG> { lang = stripChars(t.image, 1) ; } ) | |
| | |
( <DATATYPE> uri = iri() ) | |
)? | |
{ return createLiteral(lex, lang, uri) ; } | |
} | |
Node NumericLiteral() : { Node n ; } | |
{ | |
( | |
n = NumericLiteralUnsigned() | |
| n = NumericLiteralPositive() | |
| n = NumericLiteralNegative() | |
) | |
{ return n ; } | |
} | |
Node NumericLiteralUnsigned() : { Token t ; } | |
{ | |
t = <INTEGER> { return createLiteralInteger(t.image) ; } | |
| t = <DECIMAL> { return createLiteralDecimal(t.image) ; } | |
| t = <DOUBLE> { return createLiteralDouble(t.image) ; } | |
} | |
Node NumericLiteralPositive() : { Token t ; } | |
{ | |
t = <INTEGER_POSITIVE> { return createLiteralInteger(t.image) ; } | |
| t = <DECIMAL_POSITIVE> { return createLiteralDecimal(t.image) ; } | |
| t = <DOUBLE_POSITIVE> { return createLiteralDouble(t.image) ; } | |
} | |
Node NumericLiteralNegative() : { Token t ; } | |
{ | |
t = <INTEGER_NEGATIVE> { return createLiteralInteger(t.image) ; } | |
| t = <DECIMAL_NEGATIVE> { return createLiteralDecimal(t.image) ; } | |
| t = <DOUBLE_NEGATIVE> { return createLiteralDouble(t.image) ; } | |
} | |
Node BooleanLiteral() : {} | |
{ | |
<TRUE> { return XSD_TRUE ; } | |
| | |
<FALSE> { return XSD_FALSE ; } | |
} | |
String String() : { Token t ; String lex ; } | |
{ | |
( t = <STRING_LITERAL1> { lex = stripQuotes(t.image) ; } | |
| t = <STRING_LITERAL2> { lex = stripQuotes(t.image) ; } | |
| t = <STRING_LITERAL_LONG1> { lex = stripQuotes3(t.image) ; } | |
| t = <STRING_LITERAL_LONG2> { lex = stripQuotes3(t.image) ; } | |
) | |
{ | |
lex = unescapeStr(lex, t.beginLine, t.beginColumn) ; | |
return lex ; | |
} | |
} | |
String iri() : { String iri ; } | |
{ | |
iri = IRIREF() { return iri ; } | |
| | |
iri = PrefixedName() { return iri ; } | |
} | |
String PrefixedName() : { Token t ; } | |
{ | |
( t = <PNAME_LN> | |
{ return resolvePName(t.image, t.beginLine, t.beginColumn) ; } | |
| | |
t = <PNAME_NS> | |
{ return resolvePName(t.image, t.beginLine, t.beginColumn) ; } | |
) | |
} | |
Node BlankNode() : { Token t = null ; } | |
{ | |
t = <BLANK_NODE_LABEL> | |
{ return createBNode(t.image, t.beginLine, t.beginColumn) ; } | |
| | |
// <LBRACKET> <RBRACKET> { return createBNode(t.beginLine, t.beginColumn) ; } | |
t = <ANON> { return createBNode(t.beginLine, t.beginColumn) ; } | |
} | |
String IRIREF() : { Token t ; } | |
{ | |
t = <IRIref> | |
{ return resolveQuotedIRI(t.image, t.beginLine, t.beginColumn) ; } | |
} | |
// ------------------------------------------ | |
// Tokens | |
// Comments and whitespace | |
SKIP : { " " | "\t" | "\n" | "\r" | "\f" } | |
SPECIAL_TOKEN : | |
{ <SINGLE_LINE_COMMENT: "#" (~["\n","\r"])* ("\n"|"\r"|"\r\n")? > } | |
TOKEN: { | |
<#WS: " " | "\t" | "\n" | "\r" | "\f"> | |
| | |
// Whitespace or comment. | |
<#WSC: <WS> | <SINGLE_LINE_COMMENT> > | |
} | |
// Main tokens */ | |
TOKEN: | |
{ | |
// Includes # for relative URIs | |
<IRIref: "<" (~[ ">","<", "\"", "{", "}", "^", "\\", "|", "`", | |
"\u0000"-"\u0020"])* ">" > | |
| <PNAME_NS: (<PN_PREFIX>)? ":" > | |
| <PNAME_LN: <PNAME_NS> <PN_LOCAL> > | |
| <BLANK_NODE_LABEL: "_:" (<PN_CHARS_U> | ["0"-"9"]) ((<PN_CHARS>|".")* <PN_CHARS>)? > | |
| <VAR1: "?" <VARNAME> > | |
| <VAR2: "$" <VARNAME> > | |
| <LANGTAG: <AT> (<A2Z>)+("-" (<A2ZN>)+)* > | |
| <#A2Z: ["a"-"z","A"-"Z"]> | |
| <#A2ZN: ["a"-"z","A"-"Z","0"-"9"]> | |
} | |
// ------------------------------------------------- | |
// Keyworks : includes operators that are words and should be | |
// before general things like IDENTIFIER which swallow almost | |
// anything | |
TOKEN : { <KW_A: "a" > } | |
TOKEN [IGNORE_CASE] : | |
{ | |
// Prologue | |
< BASE: "base" > | |
| < PREFIX: "prefix" > | |
// Result forms | |
| < SELECT: "select" > | |
| < DISTINCT: "distinct" > | |
| < REDUCED: "reduced" > | |
| < DESCRIBE: "describe" > | |
| < CONSTRUCT: "construct" > | |
| < ASK: "ask" > | |
| < LIMIT: "limit" > | |
| < OFFSET: "offset" > | |
| < ORDER: "order" > | |
| < BY: "by" > | |
| < VALUES: "values" > | |
| < UNDEF: "undef" > | |
| < ASC: "asc" > | |
| < DESC: "desc" > | |
// Dataset | |
| < NAMED: "named" > | |
| < FROM: "from" > | |
// Graph pattern operators | |
| < WHERE: "where" > | |
| < AND: "and" > | |
| < GRAPH: "graph" > | |
| < OPTIONAL: "optional" > | |
| < UNION: "union" > | |
| < MINUS_P: "minus" > | |
| < BIND: "bind" > | |
| < SERVICE: "service" > | |
| < EXISTS: "exists" > | |
| < NOT: "not" > | |
//| < UNSAID: "unsaid" > | |
| < AS: "as" > | |
| < GROUP: "group" > | |
| < HAVING: "having" > | |
| < SEPARATOR: "separator" > | |
| < AGG: "agg" > | |
| < COUNT: "count" > | |
| < MIN: "min" > | |
| < MAX: "max" > | |
| < SUM: "sum" > | |
| < AVG: "avg" > | |
| < STDDEV: "stdev" > | |
| < SAMPLE: "sample" > | |
| < GROUP_CONCAT: "group_concat" > | |
| < FILTER: "filter" > | |
// Expression operators | |
| < BOUND: "bound" > | |
| < COALESCE: "coalesce" > | |
| < IN: "in" > | |
| < IF: "if" > | |
| < BNODE: "bnode" > | |
| < IRI: "iri" > | |
| < URI: "uri" > | |
| < STR: "str" > | |
| < STRLANG: "strlang" > | |
| < STRDT: "strdt" > | |
| < DTYPE: "datatype" > | |
| < LANG: "lang" > | |
| < LANGMATCHES: "langmatches" > | |
| < IS_URI: "isURI" > | |
| < IS_IRI: "isIRI" > | |
| < IS_BLANK: "isBlank" > | |
| < IS_LITERAL: "isLiteral" > | |
| < IS_NUMERIC: "isNumeric" > | |
| < REGEX: "regex" > | |
| < SAME_TERM: "sameTerm" > | |
| < RAND: "RAND" > | |
| < ABS: "ABS" > | |
| < CEIL: "CEIL" > | |
| < FLOOR: "FLOOR" > | |
| < ROUND: "ROUND" > | |
| < CONCAT: "CONCAT" > | |
| < SUBSTR: "SUBSTR" > | |
| < STRLEN: "STRLEN" > | |
| < REPLACE: "REPLACE" > | |
| < UCASE: "UCASE" > | |
| < LCASE: "LCASE" > | |
| < ENCODE_FOR_URI: "ENCODE_FOR_URI" > | |
| < CONTAINS: "CONTAINS" > | |
| < STRSTARTS: "STRSTARTS" > | |
| < STRENDS: "STRENDS" > | |
| < STRBEFORE: "STRBEFORE" > | |
| < STRAFTER : "STRAFTER" > | |
| < YEAR: "YEAR" > | |
| < MONTH: "MONTH" > | |
| < DAY: "DAY" > | |
| < HOURS: "HOURS" > | |
| < MINUTES: "MINUTES" > | |
| < SECONDS: "SECONDS" > | |
| < TIMEZONE: "TIMEZONE" > | |
| < TZ: "TZ" > | |
| < NOW: "NOW" > | |
| < UUID: "UUID" > | |
| < STRUUID: "STRUUID" > | |
| < MD5: "MD5" > | |
| < SHA1: "SHA1" > | |
| < SHA224: "SHA224" > | |
| < SHA256: "SHA256" > | |
| < SHA384: "SHA384" > | |
| < SHA512: "SHA512" > | |
| < TRUE: "true" > | |
| < FALSE: "false" > | |
} | |
// SPARQL/Update parts. | |
TOKEN [IGNORE_CASE] : | |
{ | |
< DATA: "data" > | |
| < INSERT: "insert"> | |
| < DELETE: "delete" > | |
| < INSERT_DATA: <INSERT> (<WSC>)* <DATA> > | |
| < DELETE_DATA: <DELETE> (<WSC>)* <DATA> > | |
| < DELETE_WHERE: <DELETE> (<WSC>)* <WHERE> > | |
| < LOAD: "load" > | |
| < CLEAR: "clear" > | |
| < CREATE: "create" > | |
| < ADD: "add" > | |
| < MOVE: "move" > | |
| < COPY: "copy" > | |
| < META: "meta" > | |
| < SILENT: "silent" > | |
| < DROP: "drop" > | |
| < INTO: "into" > | |
| < TO: "to" > | |
| < DFT: "default" > | |
//| < NAMED: "named" > | |
| < ALL: "all" > | |
| < WITH: "with" > | |
| < USING: "using" > | |
//| < BEGIN: "begin" > | |
//| < COMMIT: "commit" > | |
//| < ABORT: "abort" > | |
} | |
// ------------------------------------------------- | |
TOKEN : | |
{ | |
< #DIGITS: (["0"-"9"])+> | |
| < INTEGER: <DIGITS> > | |
//| < DECIMAL: ( <DIGITS> "." (<DIGITS>)* | "." <DIGITS> ) > | |
| < DECIMAL: (<DIGITS>)? "." <DIGITS> > | |
| < DOUBLE: // Required exponent. | |
( | |
(["0"-"9"])+ "." (["0"-"9"])* <EXPONENT> | |
| "." (["0"-"9"])+ (<EXPONENT>) | |
| (["0"-"9"])+ <EXPONENT> | |
) | |
> | |
| < INTEGER_POSITIVE: <PLUS> <INTEGER> > | |
| < DECIMAL_POSITIVE: <PLUS> <DECIMAL> > | |
| < DOUBLE_POSITIVE: <PLUS> <DOUBLE> > | |
| < INTEGER_NEGATIVE: <MINUS> <INTEGER> > | |
| < DECIMAL_NEGATIVE: <MINUS> <DECIMAL> > | |
| < DOUBLE_NEGATIVE: <MINUS> <DOUBLE> > | |
| < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ > | |
| < #QUOTE_3D: "\"\"\""> | |
| < #QUOTE_3S: "'''"> | |
| <ECHAR: "\\" ( "t"|"b"|"n"|"r"|"f"|"\\"|"\""|"'") > | |
| < STRING_LITERAL1: | |
// Single quoted string | |
"'" ( (~["'","\\","\n","\r"]) | <ECHAR> )* "'" > | |
| < STRING_LITERAL2: | |
// Double quoted string | |
"\"" ( (~["\"","\\","\n","\r"]) | <ECHAR> )* "\"" > | |
| < STRING_LITERAL_LONG1: | |
<QUOTE_3S> | |
( ("'" | "''")? (~["'","\\"] | <ECHAR> ))* | |
<QUOTE_3S> > | |
| < STRING_LITERAL_LONG2: | |
<QUOTE_3D> | |
( ("\"" | "\"\"")? (~["\"","\\"] | <ECHAR> ))* | |
<QUOTE_3D> > | |
} | |
TOKEN : | |
{ | |
< LPAREN: "(" > | |
| < RPAREN: ")" > | |
// All the stuff for NUL is needed just to make a | |
// single list "() ." as a triple pattern illegal. | |
// It leads to a lot of extra work. | |
// Similarly []. | |
| <NIL: <LPAREN> (<WSC>)* <RPAREN> > | |
| < LBRACE: "{" > | |
| < RBRACE: "}" > | |
| < LBRACKET: "[" > | |
| < RBRACKET: "]" > | |
| < ANON: <LBRACKET> (<WSC>)* <RBRACKET> > | |
| < SEMICOLON: ";" > | |
| < COMMA: "," > | |
| < DOT: "." > | |
| < EQ: "=" > | |
| < NE: "!=" > | |
| < GT: ">" > | |
| < LT: "<" > | |
| < LE: "<=" > // Maybe: | "=>" > | |
| < GE: ">=" > // Maybe: | "=<" > | |
| < BANG: "!" > | |
| < TILDE: "~" > | |
| < COLON: ":" > | |
| < SC_OR: "||" > | |
| < SC_AND: "&&" > | |
| < PLUS: "+" > | |
| < MINUS: "-" > | |
| < STAR: "*" > | |
| < SLASH: "/" > | |
//| < AMP: "&" > | |
//| < REM: "%" > | |
| < DATATYPE: "^^"> | |
| < AT: "@"> | |
// Path related | |
| < VBAR: "|" > | |
| < CARAT: "^" > | |
| < FPATH: "->" > | |
| < RPATH: "<-" > | |
| < QMARK: "?" > | |
} | |
// See XML chars.txt for notes | |
TOKEN: | |
{ | |
// XML 1.1 NCNameStartChar without "_" | |
<#PN_CHARS_BASE: | |
["A"-"Z"] | ["a"-"z"] | | |
["\u00C0"-"\u00D6"] | ["\u00D8"-"\u00F6"] | ["\u00F8"-"\u02FF"] | | |
["\u0370"-"\u037D"] | ["\u037F"-"\u1FFF"] | | |
["\u200C"-"\u200D"] | ["\u2070"-"\u218F"] | ["\u2C00"-"\u2FEF"] | | |
["\u3001"-"\uD7FF"] | ["\uF900"-"\uFFFD"] | |
> | |
// [#x10000-#xEFFFF] | |
| | |
// With underscore | |
<#PN_CHARS_U: <PN_CHARS_BASE> | "_" > | |
| | |
<#PN_CHARS: (<PN_CHARS_U> | "-" | ["0"-"9"] | "\u00B7" | | |
["\u0300"-"\u036F"] | ["\u203F"-"\u2040"] ) > | |
| | |
// No leading "_", no trailing ".", can have dot inside prefix name. | |
<#PN_PREFIX: <PN_CHARS_BASE> ((<PN_CHARS>|".")* <PN_CHARS>)? > | |
| | |
// Local part. | |
<#PN_LOCAL: (<PN_CHARS_U> | ":" | ["0"-"9"] | <PLX> ) | |
( (<PN_CHARS> | "." |":" | <PLX> )* | |
(<PN_CHARS> | ":" | <PLX>) )? > | |
| | |
<#VARNAME: ( <PN_CHARS_U> | ["0"-"9"] ) | |
( <PN_CHARS_U> | ["0"-"9"] | "\u00B7" | | |
["\u0300"-"\u036F"] | ["\u203F"-"\u2040"] )* > | |
| | |
// Align with QueryParseBase unescapePName. | |
< #PN_LOCAL_ESC: "\\" | |
( "_" | | |
"~" | "." | "-" | "!" | "$" | "&" | "'" | | |
"(" | ")" | "*" | "+" | "," | ";" | "=" | | |
"/" | "?" | "#" | "@" | "%" ) > | |
| | |
<#PLX: <PERCENT> | <PN_LOCAL_ESC> > | |
| | |
< #HEX: ["0"-"9"] | ["A"-"F"] | ["a"-"f"] > | |
| | |
< #PERCENT: "%" <HEX> <HEX> > | |
} | |
// Catch-all tokens. Must be last. | |
// Any non-whitespace. Causes a parser exception, rather than a | |
// token manager error (which hides the line numbers). | |
TOKEN: | |
{ | |
<#UNKNOWN: (~[" ","\t","\n","\r","\f" ])+ > | |
} | |
/* | |
# Local Variables: | |
# tab-width: 4 | |
# indent-tabs-mode: nil | |
# comment-default-style: "//" | |
# End: | |
*/ |