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)) |