"""
SPARQL 1.1 Parser
based on pyparsing
"""
from __future__ import annotations # noqa: I001
import re
import sys
from typing import Any, BinaryIO, List
from typing import Optional as OptionalType
from typing import TextIO, Tuple, Union
from pyparsing import CaselessKeyword as Keyword # watch out :)
from pyparsing import (
Combine,
Forward,
Group,
Literal,
OneOrMore,
Optional,
ParseResults,
Regex,
Suppress,
ZeroOrMore,
delimitedList,
restOfLine,
)
import rdflib
from rdflib.compat import decodeUnicodeEscape
from . import operators as op
from .parserutils import Comp, CompValue, Param, ParamList
# from pyparsing import Keyword as CaseSensitiveKeyword
DEBUG = False
# ---------------- ACTIONS
[docs]def neg(literal: rdflib.Literal) -> rdflib.Literal:
return rdflib.Literal(-literal, datatype=literal.datatype)
[docs]def setLanguage(terms: Tuple[Any, OptionalType[str]]) -> rdflib.Literal:
return rdflib.Literal(terms[0], lang=terms[1])
[docs]def setDataType(terms: Tuple[Any, OptionalType[str]]) -> rdflib.Literal:
return rdflib.Literal(terms[0], datatype=terms[1])
[docs]def expandTriples(terms: ParseResults) -> List[Any]:
"""
Expand ; and , syntax for repeat predicates, subjects
"""
# import pdb; pdb.set_trace()
try:
res: List[Any] = []
if DEBUG:
print("Terms", terms)
l_ = len(terms)
for i, t in enumerate(terms):
if t == ",":
res.extend([res[-3], res[-2]])
elif t == ";":
if i + 1 == len(terms) or terms[i + 1] == ";" or terms[i + 1] == ".":
continue # this semicolon is spurious
res.append(res[0])
elif isinstance(t, list):
# BlankNodePropertyList
# is this bnode the object of previous triples?
if (len(res) % 3) == 2:
res.append(t[0])
# is this a single [] ?
if len(t) > 1:
res += t
# is this bnode the subject of more triples?
if i + 1 < l_ and terms[i + 1] not in ".,;":
res.append(t[0])
elif isinstance(t, ParseResults):
res += t.asList()
elif t != ".":
res.append(t)
if DEBUG:
print(len(res), t)
if DEBUG:
import json
print(json.dumps(res, indent=2))
return res
# print res
# assert len(res)%3 == 0, \
# "Length of triple-list is not divisible by 3: %d!"%len(res)
# return [tuple(res[i:i+3]) for i in range(len(res)/3)]
except:
if DEBUG:
import traceback
traceback.print_exc()
raise
[docs]def expandBNodeTriples(terms: ParseResults) -> List[Any]:
"""
expand [ ?p ?o ] syntax for implicit bnodes
"""
# import pdb; pdb.set_trace()
try:
if DEBUG:
print("Bnode terms", terms)
print("1", terms[0])
print("2", [rdflib.BNode()] + terms.asList()[0])
return [expandTriples([rdflib.BNode()] + terms.asList()[0])]
except Exception as e:
if DEBUG:
print(">>>>>>>>", e)
raise
[docs]def expandCollection(terms: ParseResults) -> List[List[Any]]:
"""
expand ( 1 2 3 ) notation for collections
"""
if DEBUG:
print("Collection: ", terms)
res: List[Any] = []
other = []
for x in terms:
if isinstance(x, list): # is this a [ .. ] ?
other += x
x = x[0]
b = rdflib.BNode()
if res:
res += [res[-3], rdflib.RDF.rest, b, b, rdflib.RDF.first, x]
else:
res += [b, rdflib.RDF.first, x]
res += [b, rdflib.RDF.rest, rdflib.RDF.nil]
res += other
if DEBUG:
print("CollectionOut", res)
return [res]
# SPARQL Grammar from http://www.w3.org/TR/sparql11-query/#grammar
# ------ TERMINALS --------------
# [139] IRIREF ::= '<' ([^<>"{}|^`\]-[#x00-#x20])* '>'
IRIREF = Combine(
Suppress("<")
+ Regex(r'[^<>"{}|^`\\%s]*' % "".join("\\x%02X" % i for i in range(33)))
+ Suppress(">")
)
IRIREF.setParseAction(lambda x: rdflib.URIRef(x[0]))
# [164] P_CHARS_BASE ::= [A-Z] | [a-z] | [#x00C0-#x00D6] | [#x00D8-#x00F6] | [#x00F8-#x02FF] | [#x0370-#x037D] | [#x037F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
if sys.maxunicode == 0xFFFF:
# this is narrow python build (default on windows/osx)
# this means that unicode code points over 0xffff are stored
# as several characters, which in turn means that regex character
# ranges with these characters do not work.
# See
# * http://bugs.python.org/issue12729
# * http://bugs.python.org/issue12749
# * http://bugs.python.org/issue3665
#
# Here we simple skip the [#x10000-#xEFFFF] part
# this means that some SPARQL queries will not parse :(
# We *could* generate a new regex with \U00010000|\U00010001 ...
# but it would be quite long :)
#
# in py3.3 this is fixed
PN_CHARS_BASE_re = "A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD"
else:
# wide python build
PN_CHARS_BASE_re = "A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\U00010000-\U000EFFFF"
# [165] PN_CHARS_U ::= PN_CHARS_BASE | '_'
PN_CHARS_U_re = "_" + PN_CHARS_BASE_re
# [167] PN_CHARS ::= PN_CHARS_U | '-' | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040]
PN_CHARS_re = "\\-0-9\u00B7\u0300-\u036F\u203F-\u2040" + PN_CHARS_U_re
# PN_CHARS = Regex(u'[%s]'%PN_CHARS_re, flags=re.U)
# [168] PN_PREFIX ::= PN_CHARS_BASE ((PN_CHARS|'.')* PN_CHARS)?
PN_PREFIX = Regex(
"[%s](?:[%s\\.]*[%s])?" % (PN_CHARS_BASE_re, PN_CHARS_re, PN_CHARS_re), flags=re.U
)
# [140] PNAME_NS ::= PN_PREFIX? ':'
PNAME_NS = Optional(Param("prefix", PN_PREFIX)) + Suppress(":").leaveWhitespace()
# [173] PN_LOCAL_ESC ::= '\' ( '_' | '~' | '.' | '-' | '!' | '$' | '&' | "'" | '(' | ')' | '*' | '+' | ',' | ';' | '=' | '/' | '?' | '#' | '@' | '%' )
PN_LOCAL_ESC_re = "\\\\[_~\\.\\-!$&\"'()*+,;=/?#@%]"
# PN_LOCAL_ESC = Regex(PN_LOCAL_ESC_re) # regex'd
# PN_LOCAL_ESC.setParseAction(lambda x: x[0][1:])
# [172] HEX ::= [0-9] | [A-F] | [a-f]
# HEX = Regex('[0-9A-Fa-f]') # not needed
# [171] PERCENT ::= '%' HEX HEX
PERCENT_re = "%[0-9a-fA-F]{2}"
# PERCENT = Regex(PERCENT_re) # regex'd
# PERCENT.setParseAction(lambda x: chr(int(x[0][1:], 16)))
# [170] PLX ::= PERCENT | PN_LOCAL_ESC
PLX_re = "(%s|%s)" % (PN_LOCAL_ESC_re, PERCENT_re)
# PLX = PERCENT | PN_LOCAL_ESC # regex'd
# [169] PN_LOCAL ::= (PN_CHARS_U | ':' | [0-9] | PLX ) ((PN_CHARS | '.' | ':' | PLX)* (PN_CHARS | ':' | PLX) )?
PN_LOCAL = Regex(
"""([%(PN_CHARS_U)s:0-9]|%(PLX)s)
(([%(PN_CHARS)s\\.:]|%(PLX)s)*
([%(PN_CHARS)s:]|%(PLX)s) )?"""
% dict(PN_CHARS_U=PN_CHARS_U_re, PN_CHARS=PN_CHARS_re, PLX=PLX_re),
flags=re.X | re.UNICODE,
)
# [141] PNAME_LN ::= PNAME_NS PN_LOCAL
PNAME_LN = PNAME_NS + Param("localname", PN_LOCAL.leaveWhitespace())
# [142] BLANK_NODE_LABEL ::= '_:' ( PN_CHARS_U | [0-9] ) ((PN_CHARS|'.')* PN_CHARS)?
BLANK_NODE_LABEL = Regex(
"_:[0-9%s](?:[\\.%s]*[%s])?" % (PN_CHARS_U_re, PN_CHARS_re, PN_CHARS_re),
flags=re.U,
)
BLANK_NODE_LABEL.setParseAction(lambda x: rdflib.BNode(x[0][2:]))
# [166] VARNAME ::= ( PN_CHARS_U | [0-9] ) ( PN_CHARS_U | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040] )*
VARNAME = Regex(
"[%s0-9][%s0-9\u00B7\u0300-\u036F\u203F-\u2040]*" % (PN_CHARS_U_re, PN_CHARS_U_re),
flags=re.U,
)
# [143] VAR1 ::= '?' VARNAME
VAR1 = Combine(Suppress("?") + VARNAME)
# [144] VAR2 ::= '$' VARNAME
VAR2 = Combine(Suppress("$") + VARNAME)
# [145] LANGTAG ::= '@' [a-zA-Z]+ ('-' [a-zA-Z0-9]+)*
LANGTAG = Combine(Suppress("@") + Regex("[a-zA-Z]+(?:-[a-zA-Z0-9]+)*"))
# [146] INTEGER ::= [0-9]+
INTEGER = Regex(r"[0-9]+")
# INTEGER.setResultsName('integer')
INTEGER.setParseAction(lambda x: rdflib.Literal(x[0], datatype=rdflib.XSD.integer))
# [155] EXPONENT ::= [eE] [+-]? [0-9]+
EXPONENT_re = "[eE][+-]?[0-9]+"
# [147] DECIMAL ::= [0-9]* '.' [0-9]+
DECIMAL = Regex(r"[0-9]*\.[0-9]+") # (?![eE])
# DECIMAL.setResultsName('decimal')
DECIMAL.setParseAction(lambda x: rdflib.Literal(x[0], datatype=rdflib.XSD.decimal))
# [148] DOUBLE ::= [0-9]+ '.' [0-9]* EXPONENT | '.' ([0-9])+ EXPONENT | ([0-9])+ EXPONENT
DOUBLE = Regex(r"[0-9]+\.[0-9]*%(e)s|\.([0-9])+%(e)s|[0-9]+%(e)s" % {"e": EXPONENT_re})
# DOUBLE.setResultsName('double')
DOUBLE.setParseAction(lambda x: rdflib.Literal(x[0], datatype=rdflib.XSD.double))
# [149] INTEGER_POSITIVE ::= '+' INTEGER
INTEGER_POSITIVE = Suppress("+") + INTEGER.copy().leaveWhitespace()
INTEGER_POSITIVE.setParseAction(
lambda x: rdflib.Literal("+" + x[0], datatype=rdflib.XSD.integer)
)
# [150] DECIMAL_POSITIVE ::= '+' DECIMAL
DECIMAL_POSITIVE = Suppress("+") + DECIMAL.copy().leaveWhitespace()
# [151] DOUBLE_POSITIVE ::= '+' DOUBLE
DOUBLE_POSITIVE = Suppress("+") + DOUBLE.copy().leaveWhitespace()
# [152] INTEGER_NEGATIVE ::= '-' INTEGER
INTEGER_NEGATIVE = Suppress("-") + INTEGER.copy().leaveWhitespace()
INTEGER_NEGATIVE.setParseAction(lambda x: neg(x[0]))
# [153] DECIMAL_NEGATIVE ::= '-' DECIMAL
DECIMAL_NEGATIVE = Suppress("-") + DECIMAL.copy().leaveWhitespace()
DECIMAL_NEGATIVE.setParseAction(lambda x: neg(x[0]))
# [154] DOUBLE_NEGATIVE ::= '-' DOUBLE
DOUBLE_NEGATIVE = Suppress("-") + DOUBLE.copy().leaveWhitespace()
DOUBLE_NEGATIVE.setParseAction(lambda x: neg(x[0]))
# [160] ECHAR ::= '\' [tbnrf\"']
# ECHAR = Regex('\\\\[tbnrf"\']')
# [158] STRING_LITERAL_LONG1 ::= "'''" ( ( "'" | "''" )? ( [^'\] | ECHAR ) )* "'''"
# STRING_LITERAL_LONG1 = Literal("'''") + ( Optional( Literal("'") | "''"
# ) + ZeroOrMore( ~ Literal("'\\") | ECHAR ) ) + "'''"
STRING_LITERAL_LONG1 = Regex("'''((?:'|'')?(?:[^'\\\\]|\\\\['ntbrf\\\\]))*'''")
STRING_LITERAL_LONG1.setParseAction(
lambda x: rdflib.Literal(decodeUnicodeEscape(x[0][3:-3]))
)
# [159] STRING_LITERAL_LONG2 ::= '"""' ( ( '"' | '""' )? ( [^"\] | ECHAR ) )* '"""'
# STRING_LITERAL_LONG2 = Literal('"""') + ( Optional( Literal('"') | '""'
# ) + ZeroOrMore( ~ Literal('"\\') | ECHAR ) ) + '"""'
STRING_LITERAL_LONG2 = Regex('"""(?:(?:"|"")?(?:[^"\\\\]|\\\\["ntbrf\\\\]))*"""')
STRING_LITERAL_LONG2.setParseAction(
lambda x: rdflib.Literal(decodeUnicodeEscape(x[0][3:-3]))
)
# [156] STRING_LITERAL1 ::= "'" ( ([^#x27#x5C#xA#xD]) | ECHAR )* "'"
# STRING_LITERAL1 = Literal("'") + ZeroOrMore(
# Regex(u'[^\u0027\u005C\u000A\u000D]',flags=re.U) | ECHAR ) + "'"
STRING_LITERAL1 = Regex("'(?:[^'\\n\\r\\\\]|\\\\['ntbrf\\\\])*'(?!')", flags=re.U)
STRING_LITERAL1.setParseAction(
lambda x: rdflib.Literal(decodeUnicodeEscape(x[0][1:-1]))
)
# [157] STRING_LITERAL2 ::= '"' ( ([^#x22#x5C#xA#xD]) | ECHAR )* '"'
# STRING_LITERAL2 = Literal('"') + ZeroOrMore (
# Regex(u'[^\u0022\u005C\u000A\u000D]',flags=re.U) | ECHAR ) + '"'
STRING_LITERAL2 = Regex('"(?:[^"\\n\\r\\\\]|\\\\["ntbrf\\\\])*"(?!")', flags=re.U)
STRING_LITERAL2.setParseAction(
lambda x: rdflib.Literal(decodeUnicodeEscape(x[0][1:-1]))
)
# [161] NIL ::= '(' WS* ')'
NIL = Literal("(") + ")"
NIL.setParseAction(lambda x: rdflib.RDF.nil)
# [162] WS ::= #x20 | #x9 | #xD | #xA
# Not needed?
# WS = #x20 | #x9 | #xD | #xA
# [163] ANON ::= '[' WS* ']'
ANON = Literal("[") + "]"
ANON.setParseAction(lambda x: rdflib.BNode())
# A = CaseSensitiveKeyword('a')
A = Literal("a")
A.setParseAction(lambda x: rdflib.RDF.type)
# ------ NON-TERMINALS --------------
# [5] BaseDecl ::= 'BASE' IRIREF
BaseDecl = Comp("Base", Keyword("BASE") + Param("iri", IRIREF))
# [6] PrefixDecl ::= 'PREFIX' PNAME_NS IRIREF
PrefixDecl = Comp("PrefixDecl", Keyword("PREFIX") + PNAME_NS + Param("iri", IRIREF))
# [4] Prologue ::= ( BaseDecl | PrefixDecl )*
Prologue = Group(ZeroOrMore(BaseDecl | PrefixDecl))
# [108] Var ::= VAR1 | VAR2
Var = VAR1 | VAR2
Var.setParseAction(lambda x: rdflib.term.Variable(x[0]))
# [137] PrefixedName ::= PNAME_LN | PNAME_NS
PrefixedName = Comp("pname", PNAME_LN | PNAME_NS)
# [136] iri ::= IRIREF | PrefixedName
iri = IRIREF | PrefixedName
# [135] String ::= STRING_LITERAL1 | STRING_LITERAL2 | STRING_LITERAL_LONG1 | STRING_LITERAL_LONG2
String = STRING_LITERAL_LONG1 | STRING_LITERAL_LONG2 | STRING_LITERAL1 | STRING_LITERAL2
# [129] RDFLiteral ::= String ( LANGTAG | ( '^^' iri ) )?
RDFLiteral = Comp(
"literal",
Param("string", String)
+ Optional(
Param("lang", LANGTAG.leaveWhitespace())
| Literal("^^").leaveWhitespace() + Param("datatype", iri).leaveWhitespace()
),
)
# [132] NumericLiteralPositive ::= INTEGER_POSITIVE | DECIMAL_POSITIVE | DOUBLE_POSITIVE
NumericLiteralPositive = DOUBLE_POSITIVE | DECIMAL_POSITIVE | INTEGER_POSITIVE
# [133] NumericLiteralNegative ::= INTEGER_NEGATIVE | DECIMAL_NEGATIVE | DOUBLE_NEGATIVE
NumericLiteralNegative = DOUBLE_NEGATIVE | DECIMAL_NEGATIVE | INTEGER_NEGATIVE
# [131] NumericLiteralUnsigned ::= INTEGER | DECIMAL | DOUBLE
NumericLiteralUnsigned = DOUBLE | DECIMAL | INTEGER
# [130] NumericLiteral ::= NumericLiteralUnsigned | NumericLiteralPositive | NumericLiteralNegative
NumericLiteral = (
NumericLiteralUnsigned | NumericLiteralPositive | NumericLiteralNegative
)
# [134] BooleanLiteral ::= 'true' | 'false'
BooleanLiteral = Keyword("true").setParseAction(lambda: rdflib.Literal(True)) | Keyword(
"false"
).setParseAction(lambda: rdflib.Literal(False))
# [138] BlankNode ::= BLANK_NODE_LABEL | ANON
BlankNode = BLANK_NODE_LABEL | ANON
# [109] GraphTerm ::= iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL
GraphTerm = iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL
# [106] VarOrTerm ::= Var | GraphTerm
VarOrTerm = Var | GraphTerm
# [107] VarOrIri ::= Var | iri
VarOrIri = Var | iri
# [46] GraphRef ::= 'GRAPH' iri
GraphRef = Keyword("GRAPH") + Param("graphiri", iri)
# [47] GraphRefAll ::= GraphRef | 'DEFAULT' | 'NAMED' | 'ALL'
GraphRefAll = (
GraphRef
| Param("graphiri", Keyword("DEFAULT"))
| Param("graphiri", Keyword("NAMED"))
| Param("graphiri", Keyword("ALL"))
)
# [45] GraphOrDefault ::= 'DEFAULT' | 'GRAPH'? iri
GraphOrDefault = ParamList("graph", Keyword("DEFAULT")) | Optional(
Keyword("GRAPH")
) + ParamList("graph", iri)
# [65] DataBlockValue ::= iri | RDFLiteral | NumericLiteral | BooleanLiteral | 'UNDEF'
DataBlockValue = iri | RDFLiteral | NumericLiteral | BooleanLiteral | Keyword("UNDEF")
# [78] Verb ::= VarOrIri | A
Verb = VarOrIri | A
# [85] VerbSimple ::= Var
VerbSimple = Var
# [97] Integer ::= INTEGER
Integer = INTEGER
TriplesNode = Forward()
TriplesNodePath = Forward()
# [104] GraphNode ::= VarOrTerm | TriplesNode
GraphNode = VarOrTerm | TriplesNode
# [105] GraphNodePath ::= VarOrTerm | TriplesNodePath
GraphNodePath = VarOrTerm | TriplesNodePath
# [93] PathMod ::= '?' | '*' | '+'
PathMod = Literal("?") | "*" | "+"
# [96] PathOneInPropertySet ::= iri | A | '^' ( iri | A )
PathOneInPropertySet = iri | A | Comp("InversePath", "^" + (iri | A))
Path = Forward()
# [95] PathNegatedPropertySet ::= PathOneInPropertySet | '(' ( PathOneInPropertySet ( '|' PathOneInPropertySet )* )? ')'
PathNegatedPropertySet = Comp(
"PathNegatedPropertySet",
ParamList("part", PathOneInPropertySet)
| "("
+ Optional(
ParamList("part", PathOneInPropertySet)
+ ZeroOrMore("|" + ParamList("part", PathOneInPropertySet))
)
+ ")",
)
# [94] PathPrimary ::= iri | A | '!' PathNegatedPropertySet | '(' Path ')' | 'DISTINCT' '(' Path ')'
PathPrimary = (
iri
| A
| Suppress("!") + PathNegatedPropertySet
| Suppress("(") + Path + Suppress(")")
| Comp("DistinctPath", Keyword("DISTINCT") + "(" + Param("part", Path) + ")")
)
# [91] PathElt ::= PathPrimary Optional(PathMod)
PathElt = Comp(
"PathElt",
Param("part", PathPrimary) + Optional(Param("mod", PathMod.leaveWhitespace())),
)
# [92] PathEltOrInverse ::= PathElt | '^' PathElt
PathEltOrInverse = PathElt | Suppress("^") + Comp(
"PathEltOrInverse", Param("part", PathElt)
)
# [90] PathSequence ::= PathEltOrInverse ( '/' PathEltOrInverse )*
PathSequence = Comp(
"PathSequence",
ParamList("part", PathEltOrInverse)
+ ZeroOrMore("/" + ParamList("part", PathEltOrInverse)),
)
# [89] PathAlternative ::= PathSequence ( '|' PathSequence )*
PathAlternative = Comp(
"PathAlternative",
ParamList("part", PathSequence) + ZeroOrMore("|" + ParamList("part", PathSequence)),
)
# [88] Path ::= PathAlternative
Path <<= PathAlternative
# [84] VerbPath ::= Path
VerbPath = Path
# [87] ObjectPath ::= GraphNodePath
ObjectPath = GraphNodePath
# [86] ObjectListPath ::= ObjectPath ( ',' ObjectPath )*
ObjectListPath = ObjectPath + ZeroOrMore("," + ObjectPath)
GroupGraphPattern = Forward()
# [102] Collection ::= '(' OneOrMore(GraphNode) ')'
Collection = Suppress("(") + OneOrMore(GraphNode) + Suppress(")")
Collection.setParseAction(expandCollection)
# [103] CollectionPath ::= '(' OneOrMore(GraphNodePath) ')'
CollectionPath = Suppress("(") + OneOrMore(GraphNodePath) + Suppress(")")
CollectionPath.setParseAction(expandCollection)
# [80] Object ::= GraphNode
Object = GraphNode
# [79] ObjectList ::= Object ( ',' Object )*
ObjectList = Object + ZeroOrMore("," + Object)
# [83] PropertyListPathNotEmpty ::= ( VerbPath | VerbSimple ) ObjectListPath ( ';' ( ( VerbPath | VerbSimple ) ObjectList )? )*
PropertyListPathNotEmpty = (
(VerbPath | VerbSimple)
+ ObjectListPath
+ ZeroOrMore(";" + Optional((VerbPath | VerbSimple) + ObjectListPath))
)
# [82] PropertyListPath ::= Optional(PropertyListPathNotEmpty)
PropertyListPath = Optional(PropertyListPathNotEmpty)
# [77] PropertyListNotEmpty ::= Verb ObjectList ( ';' ( Verb ObjectList )? )*
PropertyListNotEmpty = Verb + ObjectList + ZeroOrMore(";" + Optional(Verb + ObjectList))
# [76] PropertyList ::= Optional(PropertyListNotEmpty)
PropertyList = Optional(PropertyListNotEmpty)
# [99] BlankNodePropertyList ::= '[' PropertyListNotEmpty ']'
BlankNodePropertyList = Group(Suppress("[") + PropertyListNotEmpty + Suppress("]"))
BlankNodePropertyList.setParseAction(expandBNodeTriples)
# [101] BlankNodePropertyListPath ::= '[' PropertyListPathNotEmpty ']'
BlankNodePropertyListPath = Group(
Suppress("[") + PropertyListPathNotEmpty + Suppress("]")
)
BlankNodePropertyListPath.setParseAction(expandBNodeTriples)
# [98] TriplesNode ::= Collection | BlankNodePropertyList
TriplesNode <<= Collection | BlankNodePropertyList
# [100] TriplesNodePath ::= CollectionPath | BlankNodePropertyListPath
TriplesNodePath <<= CollectionPath | BlankNodePropertyListPath
# [75] TriplesSameSubject ::= VarOrTerm PropertyListNotEmpty | TriplesNode PropertyList
TriplesSameSubject = VarOrTerm + PropertyListNotEmpty | TriplesNode + PropertyList
TriplesSameSubject.setParseAction(expandTriples)
# [52] TriplesTemplate ::= TriplesSameSubject ( '.' TriplesTemplate? )?
# NOTE: pyparsing.py handling of recursive rules is limited by python's recursion
# limit.
# (https://docs.python.org/3/library/sys.html#sys.setrecursionlimit)
# To accommodate arbitrary amounts of triples this rule is rewritten to not be
# recursive:
# [52*] TriplesTemplate ::= TriplesSameSubject ( '.' TriplesSameSubject? )*
TriplesTemplate = ParamList("triples", TriplesSameSubject) + ZeroOrMore(
Suppress(".") + Optional(ParamList("triples", TriplesSameSubject))
)
# [51] QuadsNotTriples ::= 'GRAPH' VarOrIri '{' Optional(TriplesTemplate) '}'
QuadsNotTriples = Comp(
"QuadsNotTriples",
Keyword("GRAPH") + Param("term", VarOrIri) + "{" + Optional(TriplesTemplate) + "}",
)
# [50] Quads ::= Optional(TriplesTemplate) ( QuadsNotTriples '.'? Optional(TriplesTemplate) )*
Quads = Comp(
"Quads",
Optional(TriplesTemplate)
+ ZeroOrMore(
ParamList("quadsNotTriples", QuadsNotTriples)
+ Optional(Suppress("."))
+ Optional(TriplesTemplate)
),
)
# [48] QuadPattern ::= '{' Quads '}'
QuadPattern = "{" + Param("quads", Quads) + "}"
# [49] QuadData ::= '{' Quads '}'
QuadData = "{" + Param("quads", Quads) + "}"
# [81] TriplesSameSubjectPath ::= VarOrTerm PropertyListPathNotEmpty | TriplesNodePath PropertyListPath
TriplesSameSubjectPath = (
VarOrTerm + PropertyListPathNotEmpty | TriplesNodePath + PropertyListPath
)
TriplesSameSubjectPath.setParseAction(expandTriples)
# [55] TriplesBlock ::= TriplesSameSubjectPath ( '.' Optional(TriplesBlock) )?
TriplesBlock = Forward()
TriplesBlock <<= ParamList("triples", TriplesSameSubjectPath) + Optional(
Suppress(".") + Optional(TriplesBlock)
)
# [66] MinusGraphPattern ::= 'MINUS' GroupGraphPattern
MinusGraphPattern = Comp(
"MinusGraphPattern", Keyword("MINUS") + Param("graph", GroupGraphPattern)
)
# [67] GroupOrUnionGraphPattern ::= GroupGraphPattern ( 'UNION' GroupGraphPattern )*
GroupOrUnionGraphPattern = Comp(
"GroupOrUnionGraphPattern",
ParamList("graph", GroupGraphPattern)
+ ZeroOrMore(Keyword("UNION") + ParamList("graph", GroupGraphPattern)),
)
Expression = Forward()
# [72] ExpressionList ::= NIL | '(' Expression ( ',' Expression )* ')'
ExpressionList = NIL | Group(Suppress("(") + delimitedList(Expression) + Suppress(")"))
# [122] RegexExpression ::= 'REGEX' '(' Expression ',' Expression ( ',' Expression )? ')'
RegexExpression = Comp(
"Builtin_REGEX",
Keyword("REGEX")
+ "("
+ Param("text", Expression)
+ ","
+ Param("pattern", Expression)
+ Optional("," + Param("flags", Expression))
+ ")",
)
RegexExpression.setEvalFn(op.Builtin_REGEX)
# [123] SubstringExpression ::= 'SUBSTR' '(' Expression ',' Expression ( ',' Expression )? ')'
SubstringExpression = Comp(
"Builtin_SUBSTR",
Keyword("SUBSTR")
+ "("
+ Param("arg", Expression)
+ ","
+ Param("start", Expression)
+ Optional("," + Param("length", Expression))
+ ")",
).setEvalFn(op.Builtin_SUBSTR)
# [124] StrReplaceExpression ::= 'REPLACE' '(' Expression ',' Expression ',' Expression ( ',' Expression )? ')'
StrReplaceExpression = Comp(
"Builtin_REPLACE",
Keyword("REPLACE")
+ "("
+ Param("arg", Expression)
+ ","
+ Param("pattern", Expression)
+ ","
+ Param("replacement", Expression)
+ Optional("," + Param("flags", Expression))
+ ")",
).setEvalFn(op.Builtin_REPLACE)
# [125] ExistsFunc ::= 'EXISTS' GroupGraphPattern
ExistsFunc = Comp(
"Builtin_EXISTS", Keyword("EXISTS") + Param("graph", GroupGraphPattern)
).setEvalFn(op.Builtin_EXISTS)
# [126] NotExistsFunc ::= 'NOT' 'EXISTS' GroupGraphPattern
NotExistsFunc = Comp(
"Builtin_NOTEXISTS",
Keyword("NOT") + Keyword("EXISTS") + Param("graph", GroupGraphPattern),
).setEvalFn(op.Builtin_EXISTS)
# [127] Aggregate ::= 'COUNT' '(' 'DISTINCT'? ( '*' | Expression ) ')'
# | 'SUM' '(' Optional('DISTINCT') Expression ')'
# | 'MIN' '(' Optional('DISTINCT') Expression ')'
# | 'MAX' '(' Optional('DISTINCT') Expression ')'
# | 'AVG' '(' Optional('DISTINCT') Expression ')'
# | 'SAMPLE' '(' Optional('DISTINCT') Expression ')'
# | 'GROUP_CONCAT' '(' Optional('DISTINCT') Expression ( ';' 'SEPARATOR' '=' String )? ')'
_Distinct = Optional(Keyword("DISTINCT"))
_AggregateParams = "(" + Param("distinct", _Distinct) + Param("vars", Expression) + ")"
Aggregate = (
Comp(
"Aggregate_Count",
Keyword("COUNT")
+ "("
+ Param("distinct", _Distinct)
+ Param("vars", "*" | Expression)
+ ")",
)
| Comp("Aggregate_Sum", Keyword("SUM") + _AggregateParams)
| Comp("Aggregate_Min", Keyword("MIN") + _AggregateParams)
| Comp("Aggregate_Max", Keyword("MAX") + _AggregateParams)
| Comp("Aggregate_Avg", Keyword("AVG") + _AggregateParams)
| Comp("Aggregate_Sample", Keyword("SAMPLE") + _AggregateParams)
| Comp(
"Aggregate_GroupConcat",
Keyword("GROUP_CONCAT")
+ "("
+ Param("distinct", _Distinct)
+ Param("vars", Expression)
+ Optional(";" + Keyword("SEPARATOR") + "=" + Param("separator", String))
+ ")",
)
)
# [121] BuiltInCall ::= Aggregate
# | 'STR' '(' + Expression + ')'
# | 'LANG' '(' + Expression + ')'
# | 'LANGMATCHES' '(' + Expression + ',' + Expression + ')'
# | 'DATATYPE' '(' + Expression + ')'
# | 'BOUND' '(' Var ')'
# | 'IRI' '(' + Expression + ')'
# | 'URI' '(' + Expression + ')'
# | 'BNODE' ( '(' + Expression + ')' | NIL )
# | 'RAND' NIL
# | 'ABS' '(' + Expression + ')'
# | 'CEIL' '(' + Expression + ')'
# | 'FLOOR' '(' + Expression + ')'
# | 'ROUND' '(' + Expression + ')'
# | 'CONCAT' ExpressionList
# | SubstringExpression
# | 'STRLEN' '(' + Expression + ')'
# | StrReplaceExpression
# | 'UCASE' '(' + Expression + ')'
# | 'LCASE' '(' + Expression + ')'
# | 'ENCODE_FOR_URI' '(' + Expression + ')'
# | 'CONTAINS' '(' + Expression + ',' + Expression + ')'
# | 'STRSTARTS' '(' + Expression + ',' + Expression + ')'
# | 'STRENDS' '(' + Expression + ',' + Expression + ')'
# | 'STRBEFORE' '(' + Expression + ',' + Expression + ')'
# | 'STRAFTER' '(' + Expression + ',' + Expression + ')'
# | 'YEAR' '(' + Expression + ')'
# | 'MONTH' '(' + Expression + ')'
# | 'DAY' '(' + Expression + ')'
# | 'HOURS' '(' + Expression + ')'
# | 'MINUTES' '(' + Expression + ')'
# | 'SECONDS' '(' + Expression + ')'
# | 'TIMEZONE' '(' + Expression + ')'
# | 'TZ' '(' + Expression + ')'
# | 'NOW' NIL
# | 'UUID' NIL
# | 'STRUUID' NIL
# | 'MD5' '(' + Expression + ')'
# | 'SHA1' '(' + Expression + ')'
# | 'SHA256' '(' + Expression + ')'
# | 'SHA384' '(' + Expression + ')'
# | 'SHA512' '(' + Expression + ')'
# | 'COALESCE' ExpressionList
# | 'IF' '(' Expression ',' Expression ',' Expression ')'
# | 'STRLANG' '(' + Expression + ',' + Expression + ')'
# | 'STRDT' '(' + Expression + ',' + Expression + ')'
# | 'sameTerm' '(' + Expression + ',' + Expression + ')'
# | 'isIRI' '(' + Expression + ')'
# | 'isURI' '(' + Expression + ')'
# | 'isBLANK' '(' + Expression + ')'
# | 'isLITERAL' '(' + Expression + ')'
# | 'isNUMERIC' '(' + Expression + ')'
# | RegexExpression
# | ExistsFunc
# | NotExistsFunc
BuiltInCall = (
Aggregate
| Comp(
"Builtin_STR", Keyword("STR") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_STR)
| Comp(
"Builtin_LANG", Keyword("LANG") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_LANG)
| Comp(
"Builtin_LANGMATCHES",
Keyword("LANGMATCHES")
+ "("
+ Param("arg1", Expression)
+ ","
+ Param("arg2", Expression)
+ ")",
).setEvalFn(op.Builtin_LANGMATCHES)
| Comp(
"Builtin_DATATYPE", Keyword("DATATYPE") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_DATATYPE)
| Comp("Builtin_BOUND", Keyword("BOUND") + "(" + Param("arg", Var) + ")").setEvalFn(
op.Builtin_BOUND
)
| Comp(
"Builtin_IRI", Keyword("IRI") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_IRI)
| Comp(
"Builtin_URI", Keyword("URI") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_IRI)
| Comp(
"Builtin_BNODE", Keyword("BNODE") + ("(" + Param("arg", Expression) + ")" | NIL)
).setEvalFn(op.Builtin_BNODE)
| Comp("Builtin_RAND", Keyword("RAND") + NIL).setEvalFn(op.Builtin_RAND)
| Comp(
"Builtin_ABS", Keyword("ABS") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_ABS)
| Comp(
"Builtin_CEIL", Keyword("CEIL") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_CEIL)
| Comp(
"Builtin_FLOOR", Keyword("FLOOR") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_FLOOR)
| Comp(
"Builtin_ROUND", Keyword("ROUND") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_ROUND)
| Comp(
"Builtin_CONCAT", Keyword("CONCAT") + Param("arg", ExpressionList)
).setEvalFn(op.Builtin_CONCAT)
| SubstringExpression
| Comp(
"Builtin_STRLEN", Keyword("STRLEN") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_STRLEN)
| StrReplaceExpression
| Comp(
"Builtin_UCASE", Keyword("UCASE") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_UCASE)
| Comp(
"Builtin_LCASE", Keyword("LCASE") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_LCASE)
| Comp(
"Builtin_ENCODE_FOR_URI",
Keyword("ENCODE_FOR_URI") + "(" + Param("arg", Expression) + ")",
).setEvalFn(op.Builtin_ENCODE_FOR_URI)
| Comp(
"Builtin_CONTAINS",
Keyword("CONTAINS")
+ "("
+ Param("arg1", Expression)
+ ","
+ Param("arg2", Expression)
+ ")",
).setEvalFn(op.Builtin_CONTAINS)
| Comp(
"Builtin_STRSTARTS",
Keyword("STRSTARTS")
+ "("
+ Param("arg1", Expression)
+ ","
+ Param("arg2", Expression)
+ ")",
).setEvalFn(op.Builtin_STRSTARTS)
| Comp(
"Builtin_STRENDS",
Keyword("STRENDS")
+ "("
+ Param("arg1", Expression)
+ ","
+ Param("arg2", Expression)
+ ")",
).setEvalFn(op.Builtin_STRENDS)
| Comp(
"Builtin_STRBEFORE",
Keyword("STRBEFORE")
+ "("
+ Param("arg1", Expression)
+ ","
+ Param("arg2", Expression)
+ ")",
).setEvalFn(op.Builtin_STRBEFORE)
| Comp(
"Builtin_STRAFTER",
Keyword("STRAFTER")
+ "("
+ Param("arg1", Expression)
+ ","
+ Param("arg2", Expression)
+ ")",
).setEvalFn(op.Builtin_STRAFTER)
| Comp(
"Builtin_YEAR", Keyword("YEAR") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_YEAR)
| Comp(
"Builtin_MONTH", Keyword("MONTH") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_MONTH)
| Comp(
"Builtin_DAY", Keyword("DAY") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_DAY)
| Comp(
"Builtin_HOURS", Keyword("HOURS") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_HOURS)
| Comp(
"Builtin_MINUTES", Keyword("MINUTES") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_MINUTES)
| Comp(
"Builtin_SECONDS", Keyword("SECONDS") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_SECONDS)
| Comp(
"Builtin_TIMEZONE", Keyword("TIMEZONE") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_TIMEZONE)
| Comp(
"Builtin_TZ", Keyword("TZ") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_TZ)
| Comp("Builtin_NOW", Keyword("NOW") + NIL).setEvalFn(op.Builtin_NOW)
| Comp("Builtin_UUID", Keyword("UUID") + NIL).setEvalFn(op.Builtin_UUID)
| Comp("Builtin_STRUUID", Keyword("STRUUID") + NIL).setEvalFn(op.Builtin_STRUUID)
| Comp(
"Builtin_MD5", Keyword("MD5") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_MD5)
| Comp(
"Builtin_SHA1", Keyword("SHA1") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_SHA1)
| Comp(
"Builtin_SHA256", Keyword("SHA256") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_SHA256)
| Comp(
"Builtin_SHA384", Keyword("SHA384") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_SHA384)
| Comp(
"Builtin_SHA512", Keyword("SHA512") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_SHA512)
| Comp(
"Builtin_COALESCE", Keyword("COALESCE") + Param("arg", ExpressionList)
).setEvalFn(op.Builtin_COALESCE)
| Comp(
"Builtin_IF",
Keyword("IF")
+ "("
+ Param("arg1", Expression)
+ ","
+ Param("arg2", Expression)
+ ","
+ Param("arg3", Expression)
+ ")",
).setEvalFn(op.Builtin_IF)
| Comp(
"Builtin_STRLANG",
Keyword("STRLANG")
+ "("
+ Param("arg1", Expression)
+ ","
+ Param("arg2", Expression)
+ ")",
).setEvalFn(op.Builtin_STRLANG)
| Comp(
"Builtin_STRDT",
Keyword("STRDT")
+ "("
+ Param("arg1", Expression)
+ ","
+ Param("arg2", Expression)
+ ")",
).setEvalFn(op.Builtin_STRDT)
| Comp(
"Builtin_sameTerm",
Keyword("sameTerm")
+ "("
+ Param("arg1", Expression)
+ ","
+ Param("arg2", Expression)
+ ")",
).setEvalFn(op.Builtin_sameTerm)
| Comp(
"Builtin_isIRI", Keyword("isIRI") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_isIRI)
| Comp(
"Builtin_isURI", Keyword("isURI") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_isIRI)
| Comp(
"Builtin_isBLANK", Keyword("isBLANK") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_isBLANK)
| Comp(
"Builtin_isLITERAL", Keyword("isLITERAL") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_isLITERAL)
| Comp(
"Builtin_isNUMERIC", Keyword("isNUMERIC") + "(" + Param("arg", Expression) + ")"
).setEvalFn(op.Builtin_isNUMERIC)
| RegexExpression
| ExistsFunc
| NotExistsFunc
)
# [71] ArgList ::= NIL | '(' 'DISTINCT'? Expression ( ',' Expression )* ')'
ArgList = (
NIL
| "("
+ Param("distinct", _Distinct)
+ delimitedList(ParamList("expr", Expression))
+ ")"
)
# [128] iriOrFunction ::= iri Optional(ArgList)
iriOrFunction = (
Comp("Function", Param("iri", iri) + ArgList).setEvalFn(op.Function)
) | iri
# [70] FunctionCall ::= iri ArgList
FunctionCall = Comp("Function", Param("iri", iri) + ArgList).setEvalFn(op.Function)
# [120] BrackettedExpression ::= '(' Expression ')'
BrackettedExpression = Suppress("(") + Expression + Suppress(")")
# [119] PrimaryExpression ::= BrackettedExpression | BuiltInCall | iriOrFunction | RDFLiteral | NumericLiteral | BooleanLiteral | Var
PrimaryExpression = (
BrackettedExpression
| BuiltInCall
| iriOrFunction
| RDFLiteral
| NumericLiteral
| BooleanLiteral
| Var
)
# [118] UnaryExpression ::= '!' PrimaryExpression
# | '+' PrimaryExpression
# | '-' PrimaryExpression
# | PrimaryExpression
UnaryExpression = (
Comp("UnaryNot", "!" + Param("expr", PrimaryExpression)).setEvalFn(op.UnaryNot)
| Comp("UnaryPlus", "+" + Param("expr", PrimaryExpression)).setEvalFn(op.UnaryPlus)
| Comp("UnaryMinus", "-" + Param("expr", PrimaryExpression)).setEvalFn(
op.UnaryMinus
)
| PrimaryExpression
)
# [117] MultiplicativeExpression ::= UnaryExpression ( '*' UnaryExpression | '/' UnaryExpression )*
MultiplicativeExpression = Comp(
"MultiplicativeExpression",
Param("expr", UnaryExpression)
+ ZeroOrMore(
ParamList("op", "*") + ParamList("other", UnaryExpression)
| ParamList("op", "/") + ParamList("other", UnaryExpression)
),
).setEvalFn(op.MultiplicativeExpression)
# [116] AdditiveExpression ::= MultiplicativeExpression ( '+' MultiplicativeExpression | '-' MultiplicativeExpression | ( NumericLiteralPositive | NumericLiteralNegative ) ( ( '*' UnaryExpression ) | ( '/' UnaryExpression ) )* )*
# NOTE: The second part of this production is there because:
# "In signed numbers, no white space is allowed between the sign and the number. The AdditiveExpression grammar rule allows for this by covering the two cases of an expression followed by a signed number. These produce an addition or subtraction of the unsigned number as appropriate."
# Here (I think) this is not necessary since pyparsing doesn't separate
# tokenizing and parsing
AdditiveExpression = Comp(
"AdditiveExpression",
Param("expr", MultiplicativeExpression)
+ ZeroOrMore(
ParamList("op", "+") + ParamList("other", MultiplicativeExpression)
| ParamList("op", "-") + ParamList("other", MultiplicativeExpression)
),
).setEvalFn(op.AdditiveExpression)
# [115] NumericExpression ::= AdditiveExpression
NumericExpression = AdditiveExpression
# [114] RelationalExpression ::= NumericExpression ( '=' NumericExpression | '!=' NumericExpression | '<' NumericExpression | '>' NumericExpression | '<=' NumericExpression | '>=' NumericExpression | 'IN' ExpressionList | 'NOT' 'IN' ExpressionList )?
RelationalExpression = Comp(
"RelationalExpression",
Param("expr", NumericExpression)
+ Optional(
Param("op", "=") + Param("other", NumericExpression)
| Param("op", "!=") + Param("other", NumericExpression)
| Param("op", "<") + Param("other", NumericExpression)
| Param("op", ">") + Param("other", NumericExpression)
| Param("op", "<=") + Param("other", NumericExpression)
| Param("op", ">=") + Param("other", NumericExpression)
| Param("op", Keyword("IN")) + Param("other", ExpressionList)
| Param(
"op",
Combine(Keyword("NOT") + Keyword("IN"), adjacent=False, joinString=" "),
)
+ Param("other", ExpressionList)
),
).setEvalFn(op.RelationalExpression)
# [113] ValueLogical ::= RelationalExpression
ValueLogical = RelationalExpression
# [112] ConditionalAndExpression ::= ValueLogical ( '&&' ValueLogical )*
ConditionalAndExpression = Comp(
"ConditionalAndExpression",
Param("expr", ValueLogical) + ZeroOrMore("&&" + ParamList("other", ValueLogical)),
).setEvalFn(op.ConditionalAndExpression)
# [111] ConditionalOrExpression ::= ConditionalAndExpression ( '||' ConditionalAndExpression )*
ConditionalOrExpression = Comp(
"ConditionalOrExpression",
Param("expr", ConditionalAndExpression)
+ ZeroOrMore("||" + ParamList("other", ConditionalAndExpression)),
).setEvalFn(op.ConditionalOrExpression)
# [110] Expression ::= ConditionalOrExpression
Expression <<= ConditionalOrExpression
# [69] Constraint ::= BrackettedExpression | BuiltInCall | FunctionCall
Constraint = BrackettedExpression | BuiltInCall | FunctionCall
# [68] Filter ::= 'FILTER' Constraint
Filter = Comp("Filter", Keyword("FILTER") + Param("expr", Constraint))
# [16] SourceSelector ::= iri
SourceSelector = iri
# [14] DefaultGraphClause ::= SourceSelector
DefaultGraphClause = SourceSelector
# [15] NamedGraphClause ::= 'NAMED' SourceSelector
NamedGraphClause = Keyword("NAMED") + Param("named", SourceSelector)
# [13] DatasetClause ::= 'FROM' ( DefaultGraphClause | NamedGraphClause )
DatasetClause = Comp(
"DatasetClause",
Keyword("FROM") + (Param("default", DefaultGraphClause) | NamedGraphClause),
)
# [20] GroupCondition ::= BuiltInCall | FunctionCall | '(' Expression ( 'AS' Var )? ')' | Var
GroupCondition = (
BuiltInCall
| FunctionCall
| Comp(
"GroupAs",
"("
+ Param("expr", Expression)
+ Optional(Keyword("AS") + Param("var", Var))
+ ")",
)
| Var
)
# [19] GroupClause ::= 'GROUP' 'BY' GroupCondition+
GroupClause = Comp(
"GroupClause",
Keyword("GROUP")
+ Keyword("BY")
+ OneOrMore(ParamList("condition", GroupCondition)),
)
_Silent = Optional(Param("silent", Keyword("SILENT")))
# [31] Load ::= 'LOAD' 'SILENT'? iri ( 'INTO' GraphRef )?
Load = Comp(
"Load",
Keyword("LOAD")
+ _Silent
+ Param("iri", iri)
+ Optional(Keyword("INTO") + GraphRef),
)
# [32] Clear ::= 'CLEAR' 'SILENT'? GraphRefAll
Clear = Comp("Clear", Keyword("CLEAR") + _Silent + GraphRefAll)
# [33] Drop ::= 'DROP' _Silent GraphRefAll
Drop = Comp("Drop", Keyword("DROP") + _Silent + GraphRefAll)
# [34] Create ::= 'CREATE' _Silent GraphRef
Create = Comp("Create", Keyword("CREATE") + _Silent + GraphRef)
# [35] Add ::= 'ADD' _Silent GraphOrDefault 'TO' GraphOrDefault
Add = Comp(
"Add", Keyword("ADD") + _Silent + GraphOrDefault + Keyword("TO") + GraphOrDefault
)
# [36] Move ::= 'MOVE' _Silent GraphOrDefault 'TO' GraphOrDefault
Move = Comp(
"Move", Keyword("MOVE") + _Silent + GraphOrDefault + Keyword("TO") + GraphOrDefault
)
# [37] Copy ::= 'COPY' _Silent GraphOrDefault 'TO' GraphOrDefault
Copy = Comp(
"Copy", Keyword("COPY") + _Silent + GraphOrDefault + Keyword("TO") + GraphOrDefault
)
# [38] InsertData ::= 'INSERT DATA' QuadData
InsertData = Comp("InsertData", Keyword("INSERT") + Keyword("DATA") + QuadData)
# [39] DeleteData ::= 'DELETE DATA' QuadData
DeleteData = Comp("DeleteData", Keyword("DELETE") + Keyword("DATA") + QuadData)
# [40] DeleteWhere ::= 'DELETE WHERE' QuadPattern
DeleteWhere = Comp("DeleteWhere", Keyword("DELETE") + Keyword("WHERE") + QuadPattern)
# [42] DeleteClause ::= 'DELETE' QuadPattern
DeleteClause = Comp("DeleteClause", Keyword("DELETE") + QuadPattern)
# [43] InsertClause ::= 'INSERT' QuadPattern
InsertClause = Comp("InsertClause", Keyword("INSERT") + QuadPattern)
# [44] UsingClause ::= 'USING' ( iri | 'NAMED' iri )
UsingClause = Comp(
"UsingClause",
Keyword("USING") + (Param("default", iri) | Keyword("NAMED") + Param("named", iri)),
)
# [41] Modify ::= ( 'WITH' iri )? ( DeleteClause Optional(InsertClause) | InsertClause ) ZeroOrMore(UsingClause) 'WHERE' GroupGraphPattern
Modify = Comp(
"Modify",
Optional(Keyword("WITH") + Param("withClause", iri))
+ (
Param("delete", DeleteClause) + Optional(Param("insert", InsertClause))
| Param("insert", InsertClause)
)
+ ZeroOrMore(ParamList("using", UsingClause))
+ Keyword("WHERE")
+ Param("where", GroupGraphPattern),
)
# [30] Update1 ::= Load | Clear | Drop | Add | Move | Copy | Create | InsertData | DeleteData | DeleteWhere | Modify
Update1 = (
Load
| Clear
| Drop
| Add
| Move
| Copy
| Create
| InsertData
| DeleteData
| DeleteWhere
| Modify
)
# [63] InlineDataOneVar ::= Var '{' ZeroOrMore(DataBlockValue) '}'
InlineDataOneVar = (
ParamList("var", Var) + "{" + ZeroOrMore(ParamList("value", DataBlockValue)) + "}"
)
# [64] InlineDataFull ::= ( NIL | '(' ZeroOrMore(Var) ')' ) '{' ( '(' ZeroOrMore(DataBlockValue) ')' | NIL )* '}'
InlineDataFull = (
(NIL | "(" + ZeroOrMore(ParamList("var", Var)) + ")")
+ "{"
+ ZeroOrMore(
ParamList(
"value",
Group(Suppress("(") + ZeroOrMore(DataBlockValue) + Suppress(")") | NIL),
)
)
+ "}"
)
# [62] DataBlock ::= InlineDataOneVar | InlineDataFull
DataBlock = InlineDataOneVar | InlineDataFull
# [28] ValuesClause ::= ( 'VALUES' DataBlock )?
ValuesClause = Optional(
Param("valuesClause", Comp("ValuesClause", Keyword("VALUES") + DataBlock))
)
# [74] ConstructTriples ::= TriplesSameSubject ( '.' Optional(ConstructTriples) )?
ConstructTriples = Forward()
ConstructTriples <<= ParamList("template", TriplesSameSubject) + Optional(
Suppress(".") + Optional(ConstructTriples)
)
# [73] ConstructTemplate ::= '{' Optional(ConstructTriples) '}'
ConstructTemplate = Suppress("{") + Optional(ConstructTriples) + Suppress("}")
# [57] OptionalGraphPattern ::= 'OPTIONAL' GroupGraphPattern
OptionalGraphPattern = Comp(
"OptionalGraphPattern", Keyword("OPTIONAL") + Param("graph", GroupGraphPattern)
)
# [58] GraphGraphPattern ::= 'GRAPH' VarOrIri GroupGraphPattern
GraphGraphPattern = Comp(
"GraphGraphPattern",
Keyword("GRAPH") + Param("term", VarOrIri) + Param("graph", GroupGraphPattern),
)
# [59] ServiceGraphPattern ::= 'SERVICE' _Silent VarOrIri GroupGraphPattern
ServiceGraphPattern = Comp(
"ServiceGraphPattern",
Keyword("SERVICE")
+ _Silent
+ Param("term", VarOrIri)
+ Param("graph", GroupGraphPattern),
)
# [60] Bind ::= 'BIND' '(' Expression 'AS' Var ')'
Bind = Comp(
"Bind",
Keyword("BIND")
+ "("
+ Param("expr", Expression)
+ Keyword("AS")
+ Param("var", Var)
+ ")",
)
# [61] InlineData ::= 'VALUES' DataBlock
InlineData = Comp("InlineData", Keyword("VALUES") + DataBlock)
# [56] GraphPatternNotTriples ::= GroupOrUnionGraphPattern | OptionalGraphPattern | MinusGraphPattern | GraphGraphPattern | ServiceGraphPattern | Filter | Bind | InlineData
GraphPatternNotTriples = (
GroupOrUnionGraphPattern
| OptionalGraphPattern
| MinusGraphPattern
| GraphGraphPattern
| ServiceGraphPattern
| Filter
| Bind
| InlineData
)
# [54] GroupGraphPatternSub ::= Optional(TriplesBlock) ( GraphPatternNotTriples '.'? Optional(TriplesBlock) )*
GroupGraphPatternSub = Comp(
"GroupGraphPatternSub",
Optional(ParamList("part", Comp("TriplesBlock", TriplesBlock)))
+ ZeroOrMore(
ParamList("part", GraphPatternNotTriples)
+ Optional(".")
+ Optional(ParamList("part", Comp("TriplesBlock", TriplesBlock)))
),
)
# ----------------
# [22] HavingCondition ::= Constraint
HavingCondition = Constraint
# [21] HavingClause ::= 'HAVING' HavingCondition+
HavingClause = Comp(
"HavingClause",
Keyword("HAVING") + OneOrMore(ParamList("condition", HavingCondition)),
)
# [24] OrderCondition ::= ( ( 'ASC' | 'DESC' ) BrackettedExpression )
# | ( Constraint | Var )
OrderCondition = Comp(
"OrderCondition",
Param("order", Keyword("ASC") | Keyword("DESC"))
+ Param("expr", BrackettedExpression)
| Param("expr", Constraint | Var),
)
# [23] OrderClause ::= 'ORDER' 'BY' OneOrMore(OrderCondition)
OrderClause = Comp(
"OrderClause",
Keyword("ORDER")
+ Keyword("BY")
+ OneOrMore(ParamList("condition", OrderCondition)),
)
# [26] LimitClause ::= 'LIMIT' INTEGER
LimitClause = Keyword("LIMIT") + Param("limit", INTEGER)
# [27] OffsetClause ::= 'OFFSET' INTEGER
OffsetClause = Keyword("OFFSET") + Param("offset", INTEGER)
# [25] LimitOffsetClauses ::= LimitClause Optional(OffsetClause) | OffsetClause Optional(LimitClause)
LimitOffsetClauses = Comp(
"LimitOffsetClauses",
LimitClause + Optional(OffsetClause) | OffsetClause + Optional(LimitClause),
)
# [18] SolutionModifier ::= GroupClause? HavingClause? OrderClause? LimitOffsetClauses?
SolutionModifier = (
Optional(Param("groupby", GroupClause))
+ Optional(Param("having", HavingClause))
+ Optional(Param("orderby", OrderClause))
+ Optional(Param("limitoffset", LimitOffsetClauses))
)
# [9] SelectClause ::= 'SELECT' ( 'DISTINCT' | 'REDUCED' )? ( ( Var | ( '(' Expression 'AS' Var ')' ) )+ | '*' )
SelectClause = (
Keyword("SELECT")
+ Optional(Param("modifier", Keyword("DISTINCT") | Keyword("REDUCED")))
+ (
OneOrMore(
ParamList(
"projection",
Comp(
"vars",
Param("var", Var)
| (
Literal("(")
+ Param("expr", Expression)
+ Keyword("AS")
+ Param("evar", Var)
+ ")"
),
),
)
)
| "*"
)
)
# [17] WhereClause ::= 'WHERE'? GroupGraphPattern
WhereClause = Optional(Keyword("WHERE")) + Param("where", GroupGraphPattern)
# [8] SubSelect ::= SelectClause WhereClause SolutionModifier ValuesClause
SubSelect = Comp(
"SubSelect", SelectClause + WhereClause + SolutionModifier + ValuesClause
)
# [53] GroupGraphPattern ::= '{' ( SubSelect | GroupGraphPatternSub ) '}'
GroupGraphPattern <<= Suppress("{") + (SubSelect | GroupGraphPatternSub) + Suppress("}")
# [7] SelectQuery ::= SelectClause DatasetClause* WhereClause SolutionModifier
SelectQuery = Comp(
"SelectQuery",
SelectClause
+ ZeroOrMore(ParamList("datasetClause", DatasetClause))
+ WhereClause
+ SolutionModifier
+ ValuesClause,
)
# [10] ConstructQuery ::= 'CONSTRUCT' ( ConstructTemplate DatasetClause* WhereClause SolutionModifier | DatasetClause* 'WHERE' '{' TriplesTemplate? '}' SolutionModifier )
# NOTE: The CONSTRUCT WHERE alternative has unnecessarily many Comp/Param pairs
# to allow it to through the same algebra translation process
ConstructQuery = Comp(
"ConstructQuery",
Keyword("CONSTRUCT")
+ (
ConstructTemplate
+ ZeroOrMore(ParamList("datasetClause", DatasetClause))
+ WhereClause
+ SolutionModifier
+ ValuesClause
| ZeroOrMore(ParamList("datasetClause", DatasetClause))
+ Keyword("WHERE")
+ "{"
+ Optional(
Param(
"where",
Comp(
"FakeGroupGraphPatten",
ParamList("part", Comp("TriplesBlock", TriplesTemplate)),
),
)
)
+ "}"
+ SolutionModifier
+ ValuesClause
),
)
# [12] AskQuery ::= 'ASK' DatasetClause* WhereClause SolutionModifier
AskQuery = Comp(
"AskQuery",
Keyword("ASK")
+ Param("datasetClause", ZeroOrMore(DatasetClause))
+ WhereClause
+ SolutionModifier
+ ValuesClause,
)
# [11] DescribeQuery ::= 'DESCRIBE' ( VarOrIri+ | '*' ) DatasetClause* WhereClause? SolutionModifier
DescribeQuery = Comp(
"DescribeQuery",
Keyword("DESCRIBE")
+ (OneOrMore(ParamList("var", VarOrIri)) | "*")
+ ZeroOrMore(ParamList("datasetClause", DatasetClause))
+ Optional(WhereClause)
+ SolutionModifier
+ ValuesClause,
)
# [29] Update ::= Prologue ( Update1 ( ';' Update )? )?
Update = Forward()
Update <<= ParamList("prologue", Prologue) + Optional(
ParamList("request", Update1) + Optional(";" + Update)
)
# [2] Query ::= Prologue
# ( SelectQuery | ConstructQuery | DescribeQuery | AskQuery )
# ValuesClause
# NOTE: ValuesClause was moved to individual queries
Query = Prologue + (SelectQuery | ConstructQuery | DescribeQuery | AskQuery)
# [3] UpdateUnit ::= Update
UpdateUnit = Comp("Update", Update)
# [1] QueryUnit ::= Query
QueryUnit = Query
QueryUnit.ignore("#" + restOfLine)
UpdateUnit.ignore("#" + restOfLine)
expandUnicodeEscapes_re: re.Pattern = re.compile(
r"\\u([0-9a-f]{4}(?:[0-9a-f]{4})?)", flags=re.I
)
[docs]def expandUnicodeEscapes(q: str) -> str:
r"""
The syntax of the SPARQL Query Language is expressed over code points in Unicode [UNICODE]. The encoding is always UTF-8 [RFC3629].
Unicode code points may also be expressed using an \ uXXXX (U+0 to U+FFFF) or \ UXXXXXXXX syntax (for U+10000 onwards) where X is a hexadecimal digit [0-9A-F]
"""
def expand(m: re.Match) -> str:
try:
return chr(int(m.group(1), 16))
except (ValueError, OverflowError) as e:
raise ValueError("Invalid unicode code point: " + m.group(1)) from e
return expandUnicodeEscapes_re.sub(expand, q)
[docs]def parseQuery(q: Union[str, bytes, TextIO, BinaryIO]) -> ParseResults:
if hasattr(q, "read"):
q = q.read()
if isinstance(q, bytes):
q = q.decode("utf-8")
q = expandUnicodeEscapes(q)
return Query.parseString(q, parseAll=True)
[docs]def parseUpdate(q: Union[str, bytes, TextIO, BinaryIO]) -> CompValue:
if hasattr(q, "read"):
q = q.read()
if isinstance(q, bytes):
q = q.decode("utf-8")
q = expandUnicodeEscapes(q)
return UpdateUnit.parseString(q, parseAll=True)[0]