Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/rdflib/plugins/sparql/operators.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
| author | guerler |
|---|---|
| date | Fri, 31 Jul 2020 00:32:28 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:d30785e31577 | 1:56ad4e20f292 |
|---|---|
| 1 """ | |
| 2 This contains evaluation functions for expressions | |
| 3 | |
| 4 They get bound as instances-methods to the CompValue objects from parserutils | |
| 5 using setEvalFn | |
| 6 | |
| 7 """ | |
| 8 | |
| 9 import sys | |
| 10 import re | |
| 11 import math | |
| 12 import random | |
| 13 import uuid | |
| 14 import hashlib | |
| 15 import urllib.request, urllib.error, urllib.parse | |
| 16 | |
| 17 from decimal import Decimal, ROUND_HALF_UP, InvalidOperation | |
| 18 | |
| 19 import operator as pyop # python operators | |
| 20 | |
| 21 import isodate | |
| 22 | |
| 23 from rdflib.plugins.sparql.parserutils import CompValue, Expr | |
| 24 from rdflib.plugins.sparql.datatypes import XSD_DTs, type_promotion | |
| 25 from rdflib import URIRef, BNode, Variable, Literal, XSD, RDF | |
| 26 from rdflib.term import Node | |
| 27 | |
| 28 from pyparsing import ParseResults | |
| 29 | |
| 30 from rdflib.plugins.sparql.sparql import SPARQLError, SPARQLTypeError | |
| 31 from functools import reduce | |
| 32 | |
| 33 | |
| 34 # closed namespace, langString isn't in it | |
| 35 RDF_langString = URIRef(RDF.uri + "langString") | |
| 36 | |
| 37 | |
| 38 def Builtin_IRI(expr, ctx): | |
| 39 """ | |
| 40 http://www.w3.org/TR/sparql11-query/#func-iri | |
| 41 """ | |
| 42 | |
| 43 a = expr.arg | |
| 44 | |
| 45 if isinstance(a, URIRef): | |
| 46 return a | |
| 47 if isinstance(a, Literal): | |
| 48 return ctx.prologue.absolutize(URIRef(a)) | |
| 49 | |
| 50 raise SPARQLError('IRI function only accepts URIRefs or Literals/Strings!') | |
| 51 | |
| 52 | |
| 53 def Builtin_isBLANK(expr, ctx): | |
| 54 return Literal(isinstance(expr.arg, BNode)) | |
| 55 | |
| 56 | |
| 57 def Builtin_isLITERAL(expr, ctx): | |
| 58 return Literal(isinstance(expr.arg, Literal)) | |
| 59 | |
| 60 | |
| 61 def Builtin_isIRI(expr, ctx): | |
| 62 return Literal(isinstance(expr.arg, URIRef)) | |
| 63 | |
| 64 | |
| 65 def Builtin_isNUMERIC(expr, ctx): | |
| 66 try: | |
| 67 numeric(expr.arg) | |
| 68 return Literal(True) | |
| 69 except: | |
| 70 return Literal(False) | |
| 71 | |
| 72 | |
| 73 def Builtin_BNODE(expr, ctx): | |
| 74 """ | |
| 75 http://www.w3.org/TR/sparql11-query/#func-bnode | |
| 76 """ | |
| 77 | |
| 78 a = expr.arg | |
| 79 | |
| 80 if a is None: | |
| 81 return BNode() | |
| 82 | |
| 83 if isinstance(a, Literal): | |
| 84 return ctx.bnodes[a] # defaultdict does the right thing | |
| 85 | |
| 86 raise SPARQLError( | |
| 87 'BNode function only accepts no argument or literal/string') | |
| 88 | |
| 89 | |
| 90 def Builtin_ABS(expr, ctx): | |
| 91 """ | |
| 92 http://www.w3.org/TR/sparql11-query/#func-abs | |
| 93 """ | |
| 94 | |
| 95 return Literal(abs(numeric(expr.arg))) | |
| 96 | |
| 97 | |
| 98 def Builtin_IF(expr, ctx): | |
| 99 """ | |
| 100 http://www.w3.org/TR/sparql11-query/#func-if | |
| 101 """ | |
| 102 | |
| 103 return expr.arg2 if EBV(expr.arg1) else expr.arg3 | |
| 104 | |
| 105 | |
| 106 def Builtin_RAND(expr, ctx): | |
| 107 """ | |
| 108 http://www.w3.org/TR/sparql11-query/#idp2133952 | |
| 109 """ | |
| 110 | |
| 111 return Literal(random.random()) | |
| 112 | |
| 113 | |
| 114 def Builtin_UUID(expr, ctx): | |
| 115 """ | |
| 116 http://www.w3.org/TR/sparql11-query/#func-strdt | |
| 117 """ | |
| 118 | |
| 119 return URIRef(uuid.uuid4().urn) | |
| 120 | |
| 121 | |
| 122 def Builtin_STRUUID(expr, ctx): | |
| 123 """ | |
| 124 http://www.w3.org/TR/sparql11-query/#func-strdt | |
| 125 """ | |
| 126 | |
| 127 return Literal(str(uuid.uuid4())) | |
| 128 | |
| 129 | |
| 130 def Builtin_MD5(expr, ctx): | |
| 131 s = string(expr.arg).encode("utf-8") | |
| 132 return Literal(hashlib.md5(s).hexdigest()) | |
| 133 | |
| 134 | |
| 135 def Builtin_SHA1(expr, ctx): | |
| 136 s = string(expr.arg).encode("utf-8") | |
| 137 return Literal(hashlib.sha1(s).hexdigest()) | |
| 138 | |
| 139 | |
| 140 def Builtin_SHA256(expr, ctx): | |
| 141 s = string(expr.arg).encode("utf-8") | |
| 142 return Literal(hashlib.sha256(s).hexdigest()) | |
| 143 | |
| 144 | |
| 145 def Builtin_SHA384(expr, ctx): | |
| 146 s = string(expr.arg).encode("utf-8") | |
| 147 return Literal(hashlib.sha384(s).hexdigest()) | |
| 148 | |
| 149 | |
| 150 def Builtin_SHA512(expr, ctx): | |
| 151 s = string(expr.arg).encode("utf-8") | |
| 152 return Literal(hashlib.sha512(s).hexdigest()) | |
| 153 | |
| 154 | |
| 155 def Builtin_COALESCE(expr, ctx): | |
| 156 """ | |
| 157 http://www.w3.org/TR/sparql11-query/#func-coalesce | |
| 158 """ | |
| 159 for x in expr.get('arg', variables=True): | |
| 160 if x is not None and not isinstance(x, (SPARQLError, Variable)): | |
| 161 return x | |
| 162 raise SPARQLError( | |
| 163 "COALESCE got no arguments that did not evaluate to an error") | |
| 164 | |
| 165 | |
| 166 def Builtin_CEIL(expr, ctx): | |
| 167 """ | |
| 168 http://www.w3.org/TR/sparql11-query/#func-ceil | |
| 169 """ | |
| 170 | |
| 171 l = expr.arg | |
| 172 return Literal(int(math.ceil(numeric(l))), datatype=l.datatype) | |
| 173 | |
| 174 | |
| 175 def Builtin_FLOOR(expr, ctx): | |
| 176 """ | |
| 177 http://www.w3.org/TR/sparql11-query/#func-floor | |
| 178 """ | |
| 179 l = expr.arg | |
| 180 return Literal(int(math.floor(numeric(l))), datatype=l.datatype) | |
| 181 | |
| 182 | |
| 183 def Builtin_ROUND(expr, ctx): | |
| 184 """ | |
| 185 http://www.w3.org/TR/sparql11-query/#func-round | |
| 186 """ | |
| 187 | |
| 188 # This used to be just math.bound | |
| 189 # but in py3k bound was changed to | |
| 190 # "round-to-even" behaviour | |
| 191 # this is an ugly work-around | |
| 192 l = expr.arg | |
| 193 v = numeric(l) | |
| 194 v = int(Decimal(v).quantize(1, ROUND_HALF_UP)) | |
| 195 return Literal(v, datatype=l.datatype) | |
| 196 | |
| 197 | |
| 198 def Builtin_REGEX(expr, ctx): | |
| 199 """ | |
| 200 http://www.w3.org/TR/sparql11-query/#func-regex | |
| 201 Invokes the XPath fn:matches function to match text against a regular | |
| 202 expression pattern. | |
| 203 The regular expression language is defined in XQuery 1.0 and XPath 2.0 | |
| 204 Functions and Operators section 7.6.1 Regular Expression Syntax | |
| 205 """ | |
| 206 | |
| 207 text = string(expr.text) | |
| 208 pattern = string(expr.pattern) | |
| 209 flags = expr.flags | |
| 210 | |
| 211 cFlag = 0 | |
| 212 if flags: | |
| 213 # Maps XPath REGEX flags (http://www.w3.org/TR/xpath-functions/#flags) | |
| 214 # to Python's re flags | |
| 215 flagMap = dict( | |
| 216 [('i', re.IGNORECASE), ('s', re.DOTALL), ('m', re.MULTILINE)]) | |
| 217 cFlag = reduce(pyop.or_, [flagMap.get(f, 0) for f in flags]) | |
| 218 | |
| 219 return Literal(bool(re.search(str(pattern), text, cFlag))) | |
| 220 | |
| 221 | |
| 222 def Builtin_REPLACE(expr, ctx): | |
| 223 """ | |
| 224 http://www.w3.org/TR/sparql11-query/#func-substr | |
| 225 """ | |
| 226 text = string(expr.arg) | |
| 227 pattern = string(expr.pattern) | |
| 228 replacement = string(expr.replacement) | |
| 229 flags = expr.flags | |
| 230 | |
| 231 # python uses \1, xpath/sparql uses $1 | |
| 232 replacement = re.sub('\\$([0-9]*)', r'\\\1', replacement) | |
| 233 | |
| 234 def _r(m): | |
| 235 | |
| 236 # Now this is ugly. | |
| 237 # Python has a "feature" where unmatched groups return None | |
| 238 # then re.sub chokes on this. | |
| 239 # see http://bugs.python.org/issue1519638 , fixed and errs in py3.5 | |
| 240 | |
| 241 # this works around and hooks into the internal of the re module... | |
| 242 | |
| 243 # the match object is replaced with a wrapper that | |
| 244 # returns "" instead of None for unmatched groups | |
| 245 | |
| 246 class _m(): | |
| 247 def __init__(self, m): | |
| 248 self.m = m | |
| 249 self.string = m.string | |
| 250 | |
| 251 def group(self, n): | |
| 252 return m.group(n) or "" | |
| 253 | |
| 254 return re._expand(pattern, _m(m), replacement) | |
| 255 | |
| 256 cFlag = 0 | |
| 257 if flags: | |
| 258 # Maps XPath REGEX flags (http://www.w3.org/TR/xpath-functions/#flags) | |
| 259 # to Python's re flags | |
| 260 flagMap = dict( | |
| 261 [('i', re.IGNORECASE), ('s', re.DOTALL), ('m', re.MULTILINE)]) | |
| 262 cFlag = reduce(pyop.or_, [flagMap.get(f, 0) for f in flags]) | |
| 263 | |
| 264 # @@FIXME@@ either datatype OR lang, NOT both | |
| 265 | |
| 266 # this is necessary due to different treatment of unmatched groups in | |
| 267 # python versions. see comments above in _r(m). | |
| 268 compat_r = str(replacement) if sys.version_info[:2] >= (3, 5) else _r | |
| 269 | |
| 270 return Literal(re.sub(str(pattern), compat_r, text, cFlag), | |
| 271 datatype=text.datatype, lang=text.language) | |
| 272 | |
| 273 | |
| 274 def Builtin_STRDT(expr, ctx): | |
| 275 """ | |
| 276 http://www.w3.org/TR/sparql11-query/#func-strdt | |
| 277 """ | |
| 278 | |
| 279 return Literal(str(expr.arg1), datatype=expr.arg2) | |
| 280 | |
| 281 | |
| 282 def Builtin_STRLANG(expr, ctx): | |
| 283 """ | |
| 284 http://www.w3.org/TR/sparql11-query/#func-strlang | |
| 285 """ | |
| 286 | |
| 287 s = string(expr.arg1) | |
| 288 if s.language or s.datatype: | |
| 289 raise SPARQLError('STRLANG expects a simple literal') | |
| 290 | |
| 291 # TODO: normalisation of lang tag to lower-case | |
| 292 # should probably happen in literal __init__ | |
| 293 return Literal(str(s), lang=str(expr.arg2).lower()) | |
| 294 | |
| 295 | |
| 296 def Builtin_CONCAT(expr, ctx): | |
| 297 """ | |
| 298 http://www.w3.org/TR/sparql11-query/#func-concat | |
| 299 """ | |
| 300 | |
| 301 # dt/lang passed on only if they all match | |
| 302 | |
| 303 dt = set(x.datatype for x in expr.arg) | |
| 304 dt = dt.pop() if len(dt) == 1 else None | |
| 305 | |
| 306 lang = set(x.language for x in expr.arg) | |
| 307 lang = lang.pop() if len(lang) == 1 else None | |
| 308 | |
| 309 return Literal("".join(string(x) | |
| 310 for x in expr.arg), datatype=dt, lang=lang) | |
| 311 | |
| 312 | |
| 313 def _compatibleStrings(a, b): | |
| 314 string(a) | |
| 315 string(b) | |
| 316 | |
| 317 if b.language and a.language != b.language: | |
| 318 raise SPARQLError('incompatible arguments to str functions') | |
| 319 | |
| 320 | |
| 321 def Builtin_STRSTARTS(expr, ctx): | |
| 322 """ | |
| 323 http://www.w3.org/TR/sparql11-query/#func-strstarts | |
| 324 """ | |
| 325 | |
| 326 a = expr.arg1 | |
| 327 b = expr.arg2 | |
| 328 _compatibleStrings(a, b) | |
| 329 | |
| 330 return Literal(a.startswith(b)) | |
| 331 | |
| 332 | |
| 333 def Builtin_STRENDS(expr, ctx): | |
| 334 """ | |
| 335 http://www.w3.org/TR/sparql11-query/#func-strends | |
| 336 """ | |
| 337 a = expr.arg1 | |
| 338 b = expr.arg2 | |
| 339 | |
| 340 _compatibleStrings(a, b) | |
| 341 | |
| 342 return Literal(a.endswith(b)) | |
| 343 | |
| 344 | |
| 345 def Builtin_STRBEFORE(expr, ctx): | |
| 346 """ | |
| 347 http://www.w3.org/TR/sparql11-query/#func-strbefore | |
| 348 """ | |
| 349 | |
| 350 a = expr.arg1 | |
| 351 b = expr.arg2 | |
| 352 _compatibleStrings(a, b) | |
| 353 | |
| 354 i = a.find(b) | |
| 355 if i == -1: | |
| 356 return Literal("") | |
| 357 else: | |
| 358 return Literal(a[:i], lang=a.language, datatype=a.datatype) | |
| 359 | |
| 360 | |
| 361 def Builtin_STRAFTER(expr, ctx): | |
| 362 """ | |
| 363 http://www.w3.org/TR/sparql11-query/#func-strafter | |
| 364 """ | |
| 365 | |
| 366 a = expr.arg1 | |
| 367 b = expr.arg2 | |
| 368 _compatibleStrings(a, b) | |
| 369 | |
| 370 i = a.find(b) | |
| 371 if i == -1: | |
| 372 return Literal("") | |
| 373 else: | |
| 374 return Literal(a[i + len(b):], lang=a.language, datatype=a.datatype) | |
| 375 | |
| 376 | |
| 377 def Builtin_CONTAINS(expr, ctx): | |
| 378 """ | |
| 379 http://www.w3.org/TR/sparql11-query/#func-strcontains | |
| 380 """ | |
| 381 | |
| 382 a = expr.arg1 | |
| 383 b = expr.arg2 | |
| 384 _compatibleStrings(a, b) | |
| 385 | |
| 386 return Literal(b in a) | |
| 387 | |
| 388 | |
| 389 def Builtin_ENCODE_FOR_URI(expr, ctx): | |
| 390 return Literal(urllib.parse.quote(string(expr.arg).encode("utf-8"))) | |
| 391 | |
| 392 | |
| 393 def Builtin_SUBSTR(expr, ctx): | |
| 394 """ | |
| 395 http://www.w3.org/TR/sparql11-query/#func-substr | |
| 396 """ | |
| 397 | |
| 398 a = string(expr.arg) | |
| 399 | |
| 400 start = numeric(expr.start) - 1 | |
| 401 | |
| 402 length = expr.length | |
| 403 if length is not None: | |
| 404 length = numeric(length) + start | |
| 405 | |
| 406 return Literal(a[start:length], lang=a.language, datatype=a.datatype) | |
| 407 | |
| 408 | |
| 409 def Builtin_STRLEN(e, ctx): | |
| 410 l = string(e.arg) | |
| 411 | |
| 412 return Literal(len(l)) | |
| 413 | |
| 414 | |
| 415 def Builtin_STR(e, ctx): | |
| 416 arg = e.arg | |
| 417 if isinstance(arg, SPARQLError): | |
| 418 raise arg | |
| 419 return Literal(str(arg)) # plain literal | |
| 420 | |
| 421 | |
| 422 def Builtin_LCASE(e, ctx): | |
| 423 l = string(e.arg) | |
| 424 | |
| 425 return Literal(l.lower(), datatype=l.datatype, lang=l.language) | |
| 426 | |
| 427 | |
| 428 def Builtin_LANGMATCHES(e, ctx): | |
| 429 """ | |
| 430 http://www.w3.org/TR/sparql11-query/#func-langMatches | |
| 431 | |
| 432 | |
| 433 """ | |
| 434 langTag = string(e.arg1) | |
| 435 langRange = string(e.arg2) | |
| 436 | |
| 437 if str(langTag) == "": | |
| 438 return Literal(False) # nothing matches empty! | |
| 439 | |
| 440 return Literal(_lang_range_check(langRange, langTag)) | |
| 441 | |
| 442 | |
| 443 def Builtin_NOW(e, ctx): | |
| 444 """ | |
| 445 http://www.w3.org/TR/sparql11-query/#func-now | |
| 446 """ | |
| 447 return Literal(ctx.now) | |
| 448 | |
| 449 | |
| 450 def Builtin_YEAR(e, ctx): | |
| 451 d = datetime(e.arg) | |
| 452 return Literal(d.year) | |
| 453 | |
| 454 | |
| 455 def Builtin_MONTH(e, ctx): | |
| 456 d = datetime(e.arg) | |
| 457 return Literal(d.month) | |
| 458 | |
| 459 | |
| 460 def Builtin_DAY(e, ctx): | |
| 461 d = datetime(e.arg) | |
| 462 return Literal(d.day) | |
| 463 | |
| 464 | |
| 465 def Builtin_HOURS(e, ctx): | |
| 466 d = datetime(e.arg) | |
| 467 return Literal(d.hour) | |
| 468 | |
| 469 | |
| 470 def Builtin_MINUTES(e, ctx): | |
| 471 d = datetime(e.arg) | |
| 472 return Literal(d.minute) | |
| 473 | |
| 474 | |
| 475 def Builtin_SECONDS(e, ctx): | |
| 476 """ | |
| 477 http://www.w3.org/TR/sparql11-query/#func-seconds | |
| 478 """ | |
| 479 d = datetime(e.arg) | |
| 480 return Literal(d.second, datatype=XSD.decimal) | |
| 481 | |
| 482 | |
| 483 def Builtin_TIMEZONE(e, ctx): | |
| 484 """ | |
| 485 http://www.w3.org/TR/sparql11-query/#func-timezone | |
| 486 | |
| 487 :returns: the timezone part of arg as an xsd:dayTimeDuration. | |
| 488 :raises: an error if there is no timezone. | |
| 489 """ | |
| 490 dt = datetime(e.arg) | |
| 491 if not dt.tzinfo: | |
| 492 raise SPARQLError('datatime has no timezone: %r' % dt) | |
| 493 | |
| 494 delta = dt.tzinfo.utcoffset(ctx.now) | |
| 495 | |
| 496 d = delta.days | |
| 497 s = delta.seconds | |
| 498 neg = "" | |
| 499 | |
| 500 if d < 0: | |
| 501 s = -24 * 60 * 60 * d - s | |
| 502 d = 0 | |
| 503 neg = "-" | |
| 504 | |
| 505 h = s / (60 * 60) | |
| 506 m = (s - h * 60 * 60) / 60 | |
| 507 s = s - h * 60 * 60 - m * 60 | |
| 508 | |
| 509 tzdelta = "%sP%sT%s%s%s" % (neg, | |
| 510 "%dD" % d if d else "", | |
| 511 "%dH" % h if h else "", | |
| 512 "%dM" % m if m else "", | |
| 513 "%dS" % s if not d and not h and not m else "") | |
| 514 | |
| 515 return Literal(tzdelta, datatype=XSD.dayTimeDuration) | |
| 516 | |
| 517 | |
| 518 def Builtin_TZ(e, ctx): | |
| 519 d = datetime(e.arg) | |
| 520 if not d.tzinfo: | |
| 521 return Literal("") | |
| 522 n = d.tzinfo.tzname(d) | |
| 523 if n == "UTC": | |
| 524 n = "Z" | |
| 525 return Literal(n) | |
| 526 | |
| 527 | |
| 528 def Builtin_UCASE(e, ctx): | |
| 529 l = string(e.arg) | |
| 530 | |
| 531 return Literal(l.upper(), datatype=l.datatype, lang=l.language) | |
| 532 | |
| 533 | |
| 534 def Builtin_LANG(e, ctx): | |
| 535 | |
| 536 """ | |
| 537 http://www.w3.org/TR/sparql11-query/#func-lang | |
| 538 | |
| 539 Returns the language tag of ltrl, if it has one. It returns "" if ltrl has | |
| 540 no language tag. Note that the RDF data model does not include literals | |
| 541 with an empty language tag. | |
| 542 """ | |
| 543 | |
| 544 l = literal(e.arg) | |
| 545 return Literal(l.language or "") | |
| 546 | |
| 547 | |
| 548 def Builtin_DATATYPE(e, ctx): | |
| 549 l = e.arg | |
| 550 if not isinstance(l, Literal): | |
| 551 raise SPARQLError('Can only get datatype of literal: %r' % l) | |
| 552 if l.language: | |
| 553 return RDF_langString | |
| 554 if not l.datatype and not l.language: | |
| 555 return XSD.string | |
| 556 return l.datatype | |
| 557 | |
| 558 | |
| 559 def Builtin_sameTerm(e, ctx): | |
| 560 a = e.arg1 | |
| 561 b = e.arg2 | |
| 562 return Literal(a == b) | |
| 563 | |
| 564 | |
| 565 def Builtin_BOUND(e, ctx): | |
| 566 """ | |
| 567 http://www.w3.org/TR/sparql11-query/#func-bound | |
| 568 """ | |
| 569 n = e.get('arg', variables=True) | |
| 570 | |
| 571 return Literal(not isinstance(n, Variable)) | |
| 572 | |
| 573 | |
| 574 def Builtin_EXISTS(e, ctx): | |
| 575 # damn... | |
| 576 from rdflib.plugins.sparql.evaluate import evalPart | |
| 577 | |
| 578 exists = e.name == 'Builtin_EXISTS' | |
| 579 | |
| 580 ctx = ctx.ctx.thaw(ctx) # hmm | |
| 581 for x in evalPart(ctx, e.graph): | |
| 582 return Literal(exists) | |
| 583 return Literal(not exists) | |
| 584 | |
| 585 | |
| 586 def Function(e, ctx): | |
| 587 """ | |
| 588 Custom functions (and casts!) | |
| 589 """ | |
| 590 | |
| 591 if e.iri in XSD_DTs: | |
| 592 # a cast | |
| 593 | |
| 594 if not e.expr: | |
| 595 raise SPARQLError("Nothing given to cast.") | |
| 596 if len(e.expr) > 1: | |
| 597 raise SPARQLError("Cannot cast more than one thing!") | |
| 598 | |
| 599 x = e.expr[0] | |
| 600 | |
| 601 if e.iri == XSD.string: | |
| 602 | |
| 603 if isinstance(x, (URIRef, Literal)): | |
| 604 return Literal(x, datatype=XSD.string) | |
| 605 else: | |
| 606 raise SPARQLError( | |
| 607 "Cannot cast term %r of type %r" % (x, type(x))) | |
| 608 | |
| 609 if not isinstance(x, Literal): | |
| 610 raise SPARQLError( | |
| 611 "Can only cast Literals to non-string data-types") | |
| 612 | |
| 613 if x.datatype and not x.datatype in XSD_DTs: | |
| 614 raise SPARQLError( | |
| 615 "Cannot cast literal with unknown datatype: %r" % x.datatype) | |
| 616 | |
| 617 if e.iri == XSD.dateTime: | |
| 618 if x.datatype and x.datatype not in (XSD.dateTime, XSD.string): | |
| 619 raise SPARQLError( | |
| 620 "Cannot cast %r to XSD:dateTime" % x.datatype) | |
| 621 try: | |
| 622 return Literal(isodate.parse_datetime(x), datatype=e.iri) | |
| 623 except: | |
| 624 raise SPARQLError("Cannot interpret '%r' as datetime" % x) | |
| 625 | |
| 626 if x.datatype == XSD.dateTime: | |
| 627 raise SPARQLError("Cannot cast XSD.dateTime to %r" % e.iri) | |
| 628 | |
| 629 if e.iri in (XSD.float, XSD.double): | |
| 630 try: | |
| 631 return Literal(float(x), datatype=e.iri) | |
| 632 except: | |
| 633 raise SPARQLError("Cannot interpret '%r' as float" % x) | |
| 634 | |
| 635 elif e.iri == XSD.decimal: | |
| 636 if "e" in x or "E" in x: # SPARQL/XSD does not allow exponents in decimals | |
| 637 raise SPARQLError("Cannot interpret '%r' as decimal" % x) | |
| 638 try: | |
| 639 return Literal(Decimal(x), datatype=e.iri) | |
| 640 except: | |
| 641 raise SPARQLError("Cannot interpret '%r' as decimal" % x) | |
| 642 | |
| 643 elif e.iri == XSD.integer: | |
| 644 try: | |
| 645 return Literal(int(x), datatype=XSD.integer) | |
| 646 except: | |
| 647 raise SPARQLError("Cannot interpret '%r' as int" % x) | |
| 648 | |
| 649 elif e.iri == XSD.boolean: | |
| 650 # # I would argue that any number is True... | |
| 651 # try: | |
| 652 # return Literal(bool(int(x)), datatype=XSD.boolean) | |
| 653 # except: | |
| 654 if x.lower() in ("1", "true"): | |
| 655 return Literal(True) | |
| 656 if x.lower() in ("0", "false"): | |
| 657 return Literal(False) | |
| 658 | |
| 659 raise SPARQLError("Cannot interpret '%r' as bool" % x) | |
| 660 else: | |
| 661 raise Exception("I do not know how to cast to %r" % e.iri) | |
| 662 | |
| 663 else: | |
| 664 raise SPARQLError('Unknown function %r"%e.iri') | |
| 665 | |
| 666 # TODO: Custom functions! | |
| 667 | |
| 668 | |
| 669 def UnaryNot(expr, ctx): | |
| 670 return Literal(not EBV(expr.expr)) | |
| 671 | |
| 672 | |
| 673 def UnaryMinus(expr, ctx): | |
| 674 return Literal(-numeric(expr.expr)) | |
| 675 | |
| 676 | |
| 677 def UnaryPlus(expr, ctx): | |
| 678 return Literal(+numeric(expr.expr)) | |
| 679 | |
| 680 | |
| 681 def MultiplicativeExpression(e, ctx): | |
| 682 | |
| 683 expr = e.expr | |
| 684 other = e.other | |
| 685 | |
| 686 # because of the way the mul-expr production handled operator precedence | |
| 687 # we sometimes have nothing to do | |
| 688 if other is None: | |
| 689 return expr | |
| 690 try: | |
| 691 res = Decimal(numeric(expr)) | |
| 692 for op, f in zip(e.op, other): | |
| 693 f = numeric(f) | |
| 694 | |
| 695 if type(f) == float: | |
| 696 res = float(res) | |
| 697 | |
| 698 if op == '*': | |
| 699 res *= f | |
| 700 else: | |
| 701 res /= f | |
| 702 except (InvalidOperation, ZeroDivisionError): | |
| 703 raise SPARQLError('divide by 0') | |
| 704 | |
| 705 return Literal(res) | |
| 706 | |
| 707 | |
| 708 def AdditiveExpression(e, ctx): | |
| 709 | |
| 710 expr = e.expr | |
| 711 other = e.other | |
| 712 | |
| 713 # because of the way the add-expr production handled operator precedence | |
| 714 # we sometimes have nothing to do | |
| 715 if other is None: | |
| 716 return expr | |
| 717 | |
| 718 res = numeric(expr) | |
| 719 | |
| 720 dt = expr.datatype | |
| 721 | |
| 722 for op, term in zip(e.op, other): | |
| 723 n = numeric(term) | |
| 724 if isinstance(n, Decimal) and isinstance(res, float): | |
| 725 n = float(n) | |
| 726 if isinstance(n, float) and isinstance(res, Decimal): | |
| 727 res = float(res) | |
| 728 | |
| 729 dt = type_promotion(dt, term.datatype) | |
| 730 | |
| 731 if op == '+': | |
| 732 res += n | |
| 733 else: | |
| 734 res -= n | |
| 735 | |
| 736 return Literal(res, datatype=dt) | |
| 737 | |
| 738 | |
| 739 def RelationalExpression(e, ctx): | |
| 740 | |
| 741 expr = e.expr | |
| 742 other = e.other | |
| 743 op = e.op | |
| 744 | |
| 745 # because of the way the add-expr production handled operator precedence | |
| 746 # we sometimes have nothing to do | |
| 747 if other is None: | |
| 748 return expr | |
| 749 | |
| 750 ops = dict([('>', lambda x, y: x.__gt__(y)), | |
| 751 ('<', lambda x, y: x.__lt__(y)), | |
| 752 ('=', lambda x, y: x.eq(y)), | |
| 753 ('!=', lambda x, y: x.neq(y)), | |
| 754 ('>=', lambda x, y: x.__ge__(y)), | |
| 755 ('<=', lambda x, y: x.__le__(y)), | |
| 756 ('IN', pyop.contains), | |
| 757 ('NOT IN', lambda x, y: not pyop.contains(x, y))]) | |
| 758 | |
| 759 if op in ('IN', 'NOT IN'): | |
| 760 | |
| 761 res = (op == 'NOT IN') | |
| 762 | |
| 763 error = False | |
| 764 | |
| 765 if other == RDF.nil: | |
| 766 other = [] | |
| 767 | |
| 768 for x in other: | |
| 769 try: | |
| 770 if x == expr: | |
| 771 return Literal(True ^ res) | |
| 772 except SPARQLError as e: | |
| 773 error = e | |
| 774 if not error: | |
| 775 return Literal(False ^ res) | |
| 776 else: | |
| 777 raise error | |
| 778 | |
| 779 if not op in ('=', '!=', 'IN', 'NOT IN'): | |
| 780 if not isinstance(expr, Literal): | |
| 781 raise SPARQLError( | |
| 782 "Compare other than =, != of non-literals is an error: %r" % | |
| 783 expr) | |
| 784 if not isinstance(other, Literal): | |
| 785 raise SPARQLError( | |
| 786 "Compare other than =, != of non-literals is an error: %r" % | |
| 787 other) | |
| 788 else: | |
| 789 if not isinstance(expr, Node): | |
| 790 raise SPARQLError('I cannot compare this non-node: %r' % expr) | |
| 791 if not isinstance(other, Node): | |
| 792 raise SPARQLError('I cannot compare this non-node: %r' % other) | |
| 793 | |
| 794 if isinstance(expr, Literal) and isinstance(other, Literal): | |
| 795 | |
| 796 if expr.datatype != None and expr.datatype not in XSD_DTs and other.datatype != None and other.datatype not in XSD_DTs: | |
| 797 # in SPARQL for non-XSD DT Literals we can only do =,!= | |
| 798 if op not in ('=', '!='): | |
| 799 raise SPARQLError( | |
| 800 'Can only do =,!= comparisons of non-XSD Literals') | |
| 801 | |
| 802 try: | |
| 803 r = ops[op](expr, other) | |
| 804 if r == NotImplemented: | |
| 805 raise SPARQLError('Error when comparing') | |
| 806 except TypeError as te: | |
| 807 raise SPARQLError(*te.args) | |
| 808 return Literal(r) | |
| 809 | |
| 810 | |
| 811 def ConditionalAndExpression(e, ctx): | |
| 812 | |
| 813 # TODO: handle returned errors | |
| 814 | |
| 815 expr = e.expr | |
| 816 other = e.other | |
| 817 | |
| 818 # because of the way the add-expr production handled operator precedence | |
| 819 # we sometimes have nothing to do | |
| 820 if other is None: | |
| 821 return expr | |
| 822 | |
| 823 return Literal(all(EBV(x) for x in [expr] + other)) | |
| 824 | |
| 825 | |
| 826 def ConditionalOrExpression(e, ctx): | |
| 827 | |
| 828 # TODO: handle errors | |
| 829 | |
| 830 expr = e.expr | |
| 831 other = e.other | |
| 832 | |
| 833 # because of the way the add-expr production handled operator precedence | |
| 834 # we sometimes have nothing to do | |
| 835 if other is None: | |
| 836 return expr | |
| 837 # A logical-or that encounters an error on only one branch | |
| 838 # will return TRUE if the other branch is TRUE and an error | |
| 839 # if the other branch is FALSE. | |
| 840 error = None | |
| 841 for x in [expr] + other: | |
| 842 try: | |
| 843 if EBV(x): | |
| 844 return Literal(True) | |
| 845 except SPARQLError as e: | |
| 846 error = e | |
| 847 if error: | |
| 848 raise error | |
| 849 return Literal(False) | |
| 850 | |
| 851 | |
| 852 def not_(arg): | |
| 853 return Expr('UnaryNot', UnaryNot, expr=arg) | |
| 854 | |
| 855 | |
| 856 def and_(*args): | |
| 857 if len(args) == 1: | |
| 858 return args[0] | |
| 859 | |
| 860 return Expr('ConditionalAndExpression', ConditionalAndExpression, | |
| 861 expr=args[0], other=list(args[1:])) | |
| 862 | |
| 863 TrueFilter = Expr('TrueFilter', lambda _1, _2: Literal(True)) | |
| 864 | |
| 865 | |
| 866 def simplify(expr): | |
| 867 if isinstance(expr, ParseResults) and len(expr) == 1: | |
| 868 return simplify(expr[0]) | |
| 869 | |
| 870 if isinstance(expr, (list, ParseResults)): | |
| 871 return list(map(simplify, expr)) | |
| 872 if not isinstance(expr, CompValue): | |
| 873 return expr | |
| 874 if expr.name.endswith('Expression'): | |
| 875 if expr.other is None: | |
| 876 return simplify(expr.expr) | |
| 877 | |
| 878 for k in list(expr.keys()): | |
| 879 expr[k] = simplify(expr[k]) | |
| 880 # expr['expr']=simplify(expr.expr) | |
| 881 # expr['other']=simplify(expr.other) | |
| 882 | |
| 883 return expr | |
| 884 | |
| 885 | |
| 886 def literal(s): | |
| 887 if not isinstance(s, Literal): | |
| 888 raise SPARQLError("Non-literal passed as string: %r" % s) | |
| 889 return s | |
| 890 | |
| 891 | |
| 892 def datetime(e): | |
| 893 if not isinstance(e, Literal): | |
| 894 raise SPARQLError("Non-literal passed as datetime: %r" % e) | |
| 895 if not e.datatype == XSD.dateTime: | |
| 896 raise SPARQLError( | |
| 897 "Literal with wrong datatype passed as datetime: %r" % e) | |
| 898 return e.toPython() | |
| 899 | |
| 900 | |
| 901 def string(s): | |
| 902 """ | |
| 903 Make sure the passed thing is a string literal | |
| 904 i.e. plain literal, xsd:string literal or lang-tagged literal | |
| 905 """ | |
| 906 if not isinstance(s, Literal): | |
| 907 raise SPARQLError("Non-literal passes as string: %r" % s) | |
| 908 if s.datatype and s.datatype != XSD.string: | |
| 909 raise SPARQLError( | |
| 910 "Non-string datatype-literal passes as string: %r" % s) | |
| 911 return s | |
| 912 | |
| 913 | |
| 914 def numeric(expr): | |
| 915 """ | |
| 916 return a number from a literal | |
| 917 http://www.w3.org/TR/xpath20/#promotion | |
| 918 | |
| 919 or TypeError | |
| 920 """ | |
| 921 | |
| 922 if not isinstance(expr, Literal): | |
| 923 raise SPARQLTypeError("%r is not a literal!" % expr) | |
| 924 | |
| 925 if expr.datatype not in (XSD.float, XSD.double, | |
| 926 XSD.decimal, XSD.integer, | |
| 927 XSD.nonPositiveInteger, XSD.negativeInteger, | |
| 928 XSD.nonNegativeInteger, XSD.positiveInteger, | |
| 929 XSD.unsignedLong, XSD.unsignedInt, | |
| 930 XSD.unsignedShort, XSD.unsignedByte, | |
| 931 XSD.long, XSD.int, XSD.short, XSD.byte): | |
| 932 raise SPARQLTypeError("%r does not have a numeric datatype!" % expr) | |
| 933 | |
| 934 return expr.toPython() | |
| 935 | |
| 936 | |
| 937 def EBV(rt): | |
| 938 """ | |
| 939 * If the argument is a typed literal with a datatype of xsd:boolean, | |
| 940 the EBV is the value of that argument. | |
| 941 * If the argument is a plain literal or a typed literal with a | |
| 942 datatype of xsd:string, the EBV is false if the operand value | |
| 943 has zero length; otherwise the EBV is true. | |
| 944 * If the argument is a numeric type or a typed literal with a datatype | |
| 945 derived from a numeric type, the EBV is false if the operand value is | |
| 946 NaN or is numerically equal to zero; otherwise the EBV is true. | |
| 947 * All other arguments, including unbound arguments, produce a type error. | |
| 948 | |
| 949 """ | |
| 950 | |
| 951 if isinstance(rt, Literal): | |
| 952 | |
| 953 if rt.datatype == XSD.boolean: | |
| 954 return rt.toPython() | |
| 955 | |
| 956 elif rt.datatype == XSD.string or rt.datatype is None: | |
| 957 return len(rt) > 0 | |
| 958 | |
| 959 else: | |
| 960 pyRT = rt.toPython() | |
| 961 | |
| 962 if isinstance(pyRT, Literal): | |
| 963 # Type error, see: http://www.w3.org/TR/rdf-sparql-query/#ebv | |
| 964 raise SPARQLTypeError( | |
| 965 "http://www.w3.org/TR/rdf-sparql-query/#ebv - ' + \ | |
| 966 'Could not determine the EBV for : %r" % rt) | |
| 967 else: | |
| 968 return bool(pyRT) | |
| 969 | |
| 970 else: | |
| 971 raise SPARQLTypeError( | |
| 972 "http://www.w3.org/TR/rdf-sparql-query/#ebv - ' + \ | |
| 973 'Only literals have Boolean values! %r" % rt) | |
| 974 | |
| 975 | |
| 976 def _lang_range_check(range, lang): | |
| 977 """ | |
| 978 Implementation of the extended filtering algorithm, as defined in point | |
| 979 3.3.2, of U{RFC 4647<http://www.rfc-editor.org/rfc/rfc4647.txt>}, on | |
| 980 matching language ranges and language tags. | |
| 981 Needed to handle the C{rdf:PlainLiteral} datatype. | |
| 982 @param range: language range | |
| 983 @param lang: language tag | |
| 984 @rtype: boolean | |
| 985 | |
| 986 @author: U{Ivan Herman<a href="http://www.w3.org/People/Ivan/">} | |
| 987 | |
| 988 Taken from `RDFClosure/RestrictedDatatype.py`__ | |
| 989 | |
| 990 .. __:http://dev.w3.org/2004/PythonLib-IH/RDFClosure/RestrictedDatatype.py | |
| 991 | |
| 992 """ | |
| 993 def _match(r, l): | |
| 994 """ | |
| 995 Matching of a range and language item: either range is a wildcard | |
| 996 or the two are equal | |
| 997 @param r: language range item | |
| 998 @param l: language tag item | |
| 999 @rtype: boolean | |
| 1000 """ | |
| 1001 return r == '*' or r == l | |
| 1002 | |
| 1003 rangeList = range.strip().lower().split('-') | |
| 1004 langList = lang.strip().lower().split('-') | |
| 1005 if not _match(rangeList[0], langList[0]): | |
| 1006 return False | |
| 1007 if len(rangeList) > len(langList): | |
| 1008 return False | |
| 1009 | |
| 1010 return all(_match(*x) for x in zip(rangeList, langList)) |
