linked-data-python 0.0.4__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. ldpy/__init__.py +35 -11
  2. ldpy/__main__.py +75 -250
  3. ldpy/build.py +91 -0
  4. ldpy/console.py +116 -0
  5. ldpy/debug.py +235 -0
  6. ldpy/formatter.py +329 -0
  7. ldpy/importer.py +110 -0
  8. ldpy/lsp/__init__.py +11 -0
  9. ldpy/lsp/__main__.py +4 -0
  10. ldpy/lsp/backend.py +118 -0
  11. ldpy/lsp/rpc.py +104 -0
  12. ldpy/lsp/server.py +353 -0
  13. ldpy/lsp/translate.py +140 -0
  14. ldpy/pygments_lexer.py +613 -0
  15. ldpy/runtime.py +931 -0
  16. ldpy/sparql.py +551 -0
  17. ldpy/transpiler/__init__.py +14 -0
  18. ldpy/transpiler/core.py +2558 -0
  19. ldpy/transpiler/errors.py +38 -0
  20. ldpy/transpiler/linemap.py +292 -0
  21. linked_data_python-0.2.0.dist-info/METADATA +158 -0
  22. linked_data_python-0.2.0.dist-info/RECORD +26 -0
  23. {linked_data_python-0.0.4.dist-info → linked_data_python-0.2.0.dist-info}/WHEEL +1 -1
  24. linked_data_python-0.2.0.dist-info/entry_points.txt +9 -0
  25. {linked_data_python-0.0.4.dist-info → linked_data_python-0.2.0.dist-info/licenses}/LICENSE.md +0 -0
  26. {linked_data_python-0.0.4.dist-info → linked_data_python-0.2.0.dist-info}/top_level.txt +0 -0
  27. ldpy/grun/lib.py +0 -63
  28. ldpy/grun/util.py +0 -11
  29. ldpy/ldpy.py +0 -183
  30. ldpy/rewriter/IndentedStringWriter.py +0 -54
  31. ldpy/rewriter/LDPythonRewriter.py +0 -677
  32. ldpy/rewriter/MultiChannelTokenStream.py +0 -127
  33. ldpy/rewriter/Result.py +0 -49
  34. ldpy/rewriter/__init__.py +0 -7
  35. ldpy/rewriter/antlr/LDPythonLexer.py +0 -870
  36. ldpy/rewriter/antlr/LDPythonParser.py +0 -9336
  37. ldpy/rewriter/antlr/LDPythonVisitor.py +0 -573
  38. ldpy/sparql/builtin.py +0 -326
  39. linked_data_python-0.0.4.dist-info/METADATA +0 -139
  40. linked_data_python-0.0.4.dist-info/RECORD +0 -20
  41. linked_data_python-0.0.4.dist-info/entry_points.txt +0 -3
ldpy/sparql.py ADDED
@@ -0,0 +1,551 @@
1
+ """Evaluation semantics of SPARQL expression nodes.
2
+
3
+ `e{ <SPARQL expression> }` transpiles to a DEFERRED ``Expression`` object:
4
+ where ``f{...}``/``?{...}`` evaluate immediately, an Expression evaluates
5
+ later, against a *solution mapping*:
6
+
7
+ majeur = e{ ?age >= 18 && BOUND(?nom) }
8
+ majeur({"age": 20, "nom": "Ana"}) # -> Literal(True)
9
+ adult.ebv({"age": 20, "name": "Ana"}) # -> True (a Python bool)
10
+
11
+ The semantics follow SPARQL 1.1: numeric promotion (integer < decimal <
12
+ float < double, integer division -> decimal), errors PROPAGATED (unbound
13
+ variable, incomparable types) and absorbed by ``||``/``&&``/``IF``/
14
+ ``COALESCE`` per SPARQL's three-valued truth table. No rdflib parser is used;
15
+ rdflib only serves as the data model.
16
+ """
17
+
18
+ import re as _re
19
+
20
+ from rdflib import BNode, Literal, URIRef, Variable, XSD
21
+ from rdflib.term import Node
22
+
23
+ from ldpy.runtime import node as _node
24
+
25
+ __all__ = ["SparqlError", "Expression", "expr"]
26
+
27
+
28
+ class SparqlError(Exception):
29
+ """A SPARQL evaluation error (unbound variable, incomparable types…).
30
+
31
+ It propagates through the operators and is absorbed only where SPARQL
32
+ SPARQL l'absorbe : ||, &&, IF, COALESCE."""
33
+
34
+
35
+ class Expression:
36
+ """A compiled SPARQL expression, to evaluate against a solution mapping.
37
+
38
+ The mapping accepts str or Variable keys, and Python or RDF values
39
+ termes RDF (coercition par ldpy.runtime.node)."""
40
+
41
+ __slots__ = ("_fn", "src")
42
+
43
+ def __init__(self, fn, src=""):
44
+ self._fn = fn
45
+ self.src = src
46
+
47
+ def _mapping(self, sm, kw):
48
+ out = {}
49
+ for k, v in list((sm or {}).items()) + list(kw.items()):
50
+ name = str(k) if isinstance(k, Variable) else k
51
+ out[name] = v if isinstance(v, Node) else _node(v)
52
+ return out
53
+
54
+ def __call__(self, sm=None, **kw):
55
+ """Evaluate; returns an RDF term, or raises SparqlError."""
56
+ return self._fn(self._mapping(sm, kw))
57
+
58
+ def evaluate(self, sm=None, **kw):
59
+ """Alias explicite de __call__."""
60
+ return self(sm, **kw)
61
+
62
+ def ebv(self, sm=None, **kw):
63
+ """Effective boolean value (a Python bool); raises SparqlError otherwise."""
64
+ return ebv(self(sm, **kw))
65
+
66
+ def __repr__(self):
67
+ return "e{ %s }" % self.src if self.src else "Expression(...)"
68
+
69
+
70
+ def expr(fn, src=""):
71
+ """Build an Expression (called by the emitted code)."""
72
+ return Expression(fn, src)
73
+
74
+
75
+ # ---------------------------------------------------------------- valeurs
76
+
77
+ _NUM_TYPES = {XSD.integer: 0, XSD.decimal: 1, XSD.float: 2, XSD.double: 3}
78
+ _INT_TYPES = {XSD.integer, XSD.int, XSD.long, XSD.short, XSD.byte,
79
+ XSD.nonNegativeInteger, XSD.positiveInteger,
80
+ XSD.negativeInteger, XSD.nonPositiveInteger,
81
+ XSD.unsignedInt, XSD.unsignedLong}
82
+
83
+
84
+ def var(sm, name):
85
+ """A variable's value; unbound -> SparqlError."""
86
+ try:
87
+ v = sm[name]
88
+ except KeyError:
89
+ raise SparqlError("unbound variable: ?%s" % name)
90
+ if v is None:
91
+ raise SparqlError("unbound variable: ?%s" % name)
92
+ return v
93
+
94
+
95
+ def bound(sm, name):
96
+ """BOUND(?v)."""
97
+ return Literal(name in sm and sm[name] is not None)
98
+
99
+
100
+ def py(value):
101
+ """Python interpolation {expr}: coerced to a term, at EVERY evaluation."""
102
+ return _node(value)
103
+
104
+
105
+ def number(lexical):
106
+ """A SPARQL numeric literal (integer / decimal / double)."""
107
+ if _re.search(r"[eE]", lexical):
108
+ return Literal(lexical, datatype=XSD.double)
109
+ if "." in lexical:
110
+ return Literal(lexical, datatype=XSD.decimal)
111
+ return Literal(lexical, datatype=XSD.integer)
112
+
113
+
114
+ def _numeric(term):
115
+ if isinstance(term, Literal):
116
+ dt = term.datatype
117
+ if dt in _NUM_TYPES or dt in _INT_TYPES:
118
+ try:
119
+ return term.toPython()
120
+ except Exception:
121
+ pass
122
+ raise SparqlError("not a numeric value: %r" % (term,))
123
+
124
+
125
+ def _num_rank(term):
126
+ dt = term.datatype
127
+ if dt in _INT_TYPES:
128
+ return 0
129
+ return _NUM_TYPES.get(dt, 1)
130
+
131
+
132
+ def _num_result(value, rank):
133
+ dt = (XSD.integer, XSD.decimal, XSD.float, XSD.double)[rank]
134
+ if dt == XSD.integer:
135
+ value = int(value)
136
+ return Literal(value, datatype=dt)
137
+
138
+
139
+ def _promote(value, rank):
140
+ """Bring a value to the Python type of the target rank — SPARQL 1.1
141
+ numeric promotion applies to the OPERANDS, not only to the result:
142
+ without it, xsd:double * xsd:decimal would be a float times a Decimal,
143
+ refuse."""
144
+ if rank == 0:
145
+ return int(value)
146
+ if rank == 1:
147
+ from decimal import Decimal
148
+ return value if isinstance(value, Decimal) else Decimal(str(value))
149
+ return float(value)
150
+
151
+
152
+ def _arith(a, b, op, div=False):
153
+ va, vb = _numeric(a), _numeric(b)
154
+ rank = max(_num_rank(a), _num_rank(b))
155
+ if div and rank == 0:
156
+ rank = 1 # SPARQL : integer / integer -> decimal
157
+ va, vb = _promote(va, rank), _promote(vb, rank)
158
+ try:
159
+ return _num_result(op(va, vb), rank)
160
+ except ZeroDivisionError:
161
+ raise SparqlError("division by zero")
162
+
163
+
164
+ def add(a, b):
165
+ """The + operator."""
166
+ return _arith(a, b, lambda x, y: x + y)
167
+
168
+
169
+ def sub(a, b):
170
+ """The - operator."""
171
+ return _arith(a, b, lambda x, y: x - y)
172
+
173
+
174
+ def mul(a, b):
175
+ """The * operator."""
176
+ return _arith(a, b, lambda x, y: x * y)
177
+
178
+
179
+ def div(a, b):
180
+ """The / operator (integer/integer -> decimal). Operands are promoted to
181
+ the rank of the result, so never two ints here."""
182
+ return _arith(a, b, lambda x, y: x / y, div=True)
183
+
184
+
185
+ def neg(a):
186
+ """Moins unaire."""
187
+ return _arith(a, number("0"), lambda x, y: -x)
188
+
189
+
190
+ # ------------------------------------------------------------- comparaisons
191
+
192
+ def _cmp(a, b):
193
+ """-1/0/1, or SparqlError if the terms are not comparable."""
194
+ if isinstance(a, Literal) and isinstance(b, Literal):
195
+ try:
196
+ va, vb = _numeric(a), _numeric(b)
197
+ return (va > vb) - (va < vb)
198
+ except SparqlError:
199
+ pass
200
+ if (a.datatype in (None, XSD.string)
201
+ and b.datatype in (None, XSD.string)
202
+ and a.language is None and b.language is None):
203
+ return (str(a) > str(b)) - (str(a) < str(b))
204
+ if a.datatype == XSD.boolean and b.datatype == XSD.boolean:
205
+ va, vb = a.toPython(), b.toPython()
206
+ return (va > vb) - (va < vb)
207
+ if a.datatype == b.datatype and a.datatype in (
208
+ XSD.dateTime, XSD.date):
209
+ va, vb = a.toPython(), b.toPython()
210
+ return (va > vb) - (va < vb)
211
+ if a.language and b.language and a.language == b.language:
212
+ return (str(a) > str(b)) - (str(a) < str(b))
213
+ raise SparqlError("termes non comparables : %r / %r" % (a, b))
214
+
215
+
216
+ def eq(a, b):
217
+ """The = operator (value equality, then term identity)."""
218
+ try:
219
+ return Literal(_cmp(a, b) == 0)
220
+ except SparqlError:
221
+ if a == b:
222
+ return Literal(True)
223
+ if isinstance(a, Literal) and isinstance(b, Literal):
224
+ raise
225
+ return Literal(False)
226
+
227
+
228
+ def ne(a, b):
229
+ """The != operator."""
230
+ return Literal(not ebv(eq(a, b)))
231
+
232
+
233
+ def lt(a, b):
234
+ """The < operator."""
235
+ return Literal(_cmp(a, b) < 0)
236
+
237
+
238
+ def gt(a, b):
239
+ """The > operator."""
240
+ return Literal(_cmp(a, b) > 0)
241
+
242
+
243
+ def le(a, b):
244
+ """The <= operator."""
245
+ return Literal(_cmp(a, b) <= 0)
246
+
247
+
248
+ def ge(a, b):
249
+ """The >= operator."""
250
+ return Literal(_cmp(a, b) >= 0)
251
+
252
+
253
+ def in_(a, items):
254
+ """The IN operator (semantics of "= chained by ||")."""
255
+ err = None
256
+ for it in items:
257
+ try:
258
+ if ebv(eq(a, it)):
259
+ return Literal(True)
260
+ except SparqlError as e:
261
+ err = e
262
+ if err is not None:
263
+ raise err
264
+ return Literal(False)
265
+
266
+
267
+ def not_in(a, items):
268
+ """The NOT IN operator."""
269
+ return Literal(not ebv(in_(a, items)))
270
+
271
+
272
+ # ------------------------------------------------------------------ logique
273
+
274
+ def ebv(term):
275
+ """Effective boolean value (SPARQL 17.2.2)."""
276
+ if isinstance(term, Literal):
277
+ if term.datatype == XSD.boolean:
278
+ v = term.toPython()
279
+ if isinstance(v, bool):
280
+ return v
281
+ return str(term) in ("true", "1")
282
+ if term.datatype in _NUM_TYPES or term.datatype in _INT_TYPES:
283
+ try:
284
+ return bool(term.toPython())
285
+ except Exception:
286
+ return False
287
+ if term.datatype in (None, XSD.string):
288
+ return len(str(term)) > 0
289
+ raise SparqlError("no EBV for %r" % (term,))
290
+
291
+
292
+ def and_(la, lb):
293
+ """&& — SPARQL's three-valued table (F && err = F)."""
294
+ try:
295
+ a = ebv(la())
296
+ except SparqlError:
297
+ if ebv(lb()) is False:
298
+ return Literal(False)
299
+ raise
300
+ if not a:
301
+ return Literal(False)
302
+ return Literal(ebv(lb()))
303
+
304
+
305
+ def or_(la, lb):
306
+ """|| — SPARQL's three-valued table (T || err = T)."""
307
+ try:
308
+ a = ebv(la())
309
+ except SparqlError:
310
+ if ebv(lb()) is True:
311
+ return Literal(True)
312
+ raise
313
+ if a:
314
+ return Literal(True)
315
+ return Literal(ebv(lb()))
316
+
317
+
318
+ def not_(a):
319
+ """The ! operator."""
320
+ return Literal(not ebv(a))
321
+
322
+
323
+ def if_(cond, lt_, lf_):
324
+ """IF(cond, alors, sinon) — branches paresseuses."""
325
+ return lt_() if ebv(cond) else lf_()
326
+
327
+
328
+ def coalesce(*lams):
329
+ """COALESCE — premier argument sans erreur."""
330
+ for lam in lams:
331
+ try:
332
+ return lam()
333
+ except SparqlError:
334
+ continue
335
+ raise SparqlError("COALESCE: no argument could be evaluated")
336
+
337
+
338
+ # ------------------------------------------------------------- built-ins
339
+
340
+ def STR(t):
341
+ """STR(term)."""
342
+ if isinstance(t, BNode):
343
+ raise SparqlError("STR of a blank node")
344
+ return Literal(str(t))
345
+
346
+
347
+ def LANG(t):
348
+ """LANG(literal)."""
349
+ if not isinstance(t, Literal):
350
+ raise SparqlError("LANG wants a literal")
351
+ return Literal(t.language or "")
352
+
353
+
354
+ def DATATYPE(t):
355
+ """DATATYPE(literal)."""
356
+ if not isinstance(t, Literal):
357
+ raise SparqlError("DATATYPE wants a literal")
358
+ if t.language:
359
+ return URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#langString")
360
+ return t.datatype or XSD.string
361
+
362
+
363
+ def IRI(t, base=None):
364
+ """IRI(str) — resolved against the lexical base of the call site."""
365
+ s = str(t)
366
+ if base and not _re.match(r"^[A-Za-z][A-Za-z0-9+.\-]*:", s):
367
+ from ldpy.runtime import firi
368
+ return firi(s, base=base)
369
+ return URIRef(s)
370
+
371
+
372
+ def BNODE(t=None):
373
+ """BNODE() / BNODE(str)."""
374
+ return BNode() if t is None else BNode(str(t))
375
+
376
+
377
+ def CONCAT(*ts):
378
+ """CONCAT(...)."""
379
+ return Literal("".join(str(t) for t in ts))
380
+
381
+
382
+ def UCASE(t):
383
+ """UCASE(str)."""
384
+ return Literal(str(t).upper(), lang=getattr(t, "language", None))
385
+
386
+
387
+ def LCASE(t):
388
+ """LCASE(str)."""
389
+ return Literal(str(t).lower(), lang=getattr(t, "language", None))
390
+
391
+
392
+ def STRLEN(t):
393
+ """STRLEN(str)."""
394
+ return Literal(len(str(t)), datatype=XSD.integer)
395
+
396
+
397
+ def SUBSTR(t, start, length=None):
398
+ """SUBSTR(str, start-1-based[, length])."""
399
+ s = str(t)
400
+ i = int(_numeric(start)) - 1
401
+ if length is None:
402
+ return Literal(s[i:], lang=getattr(t, "language", None))
403
+ return Literal(s[i:i + int(_numeric(length))],
404
+ lang=getattr(t, "language", None))
405
+
406
+
407
+ def STRSTARTS(a, b):
408
+ """STRSTARTS(str, prefix)."""
409
+ return Literal(str(a).startswith(str(b)))
410
+
411
+
412
+ def STRENDS(a, b):
413
+ """STRENDS(str, suffixe)."""
414
+ return Literal(str(a).endswith(str(b)))
415
+
416
+
417
+ def CONTAINS(a, b):
418
+ """CONTAINS(str, aiguille)."""
419
+ return Literal(str(b) in str(a))
420
+
421
+
422
+ def STRBEFORE(a, b):
423
+ """STRBEFORE(str, separator)."""
424
+ s, sep = str(a), str(b)
425
+ i = s.find(sep)
426
+ return Literal("" if i < 0 else s[:i])
427
+
428
+
429
+ def STRAFTER(a, b):
430
+ """STRAFTER(str, separator)."""
431
+ s, sep = str(a), str(b)
432
+ i = s.find(sep)
433
+ return Literal("" if i < 0 else s[i + len(sep):])
434
+
435
+
436
+ def REPLACE(t, pat, repl, flags=None):
437
+ """REPLACE(str, motif, remplacement[, drapeaux]) — regex XPath ~ Python."""
438
+ f = _re_flags(flags)
439
+ return Literal(_re.sub(str(pat), str(repl), str(t), flags=f))
440
+
441
+
442
+ def REGEX(t, pat, flags=None):
443
+ """REGEX(str, motif[, drapeaux])."""
444
+ return Literal(bool(_re.search(str(pat), str(t), _re_flags(flags))))
445
+
446
+
447
+ def _re_flags(flags):
448
+ f = 0
449
+ for c in str(flags or ""):
450
+ f |= {"i": _re.I, "s": _re.S, "m": _re.M, "x": _re.X}.get(c, 0)
451
+ return f
452
+
453
+
454
+ def ABS(t):
455
+ """ABS(num)."""
456
+ return _arith(t, number("0"), lambda x, y: abs(x))
457
+
458
+
459
+ def ROUND(t):
460
+ """ROUND(num)."""
461
+ import math
462
+ return _arith(t, number("0"), lambda x, y: math.floor(x + 0.5))
463
+
464
+
465
+ def CEIL(t):
466
+ """CEIL(num)."""
467
+ import math
468
+ return _arith(t, number("0"), lambda x, y: math.ceil(x))
469
+
470
+
471
+ def FLOOR(t):
472
+ """FLOOR(num)."""
473
+ import math
474
+ return _arith(t, number("0"), lambda x, y: math.floor(x))
475
+
476
+
477
+ def SAMETERM(a, b):
478
+ """SAMETERM(a, b)."""
479
+ return Literal(a == b)
480
+
481
+
482
+ def ISIRI(t):
483
+ """isIRI / isURI."""
484
+ return Literal(isinstance(t, URIRef))
485
+
486
+
487
+ ISURI = ISIRI
488
+
489
+
490
+ def ISBLANK(t):
491
+ """isBLANK."""
492
+ return Literal(isinstance(t, BNode))
493
+
494
+
495
+ def ISLITERAL(t):
496
+ """isLITERAL."""
497
+ return Literal(isinstance(t, Literal))
498
+
499
+
500
+ def ISNUMERIC(t):
501
+ """isNUMERIC."""
502
+ try:
503
+ _numeric(t)
504
+ return Literal(True)
505
+ except SparqlError:
506
+ return Literal(False)
507
+
508
+
509
+ def LANGMATCHES(tag, rng):
510
+ """LANGMATCHES(tag, range) — '*' and range prefixes."""
511
+ t, r = str(tag).lower(), str(rng).lower()
512
+ if not t:
513
+ return Literal(False)
514
+ if r == "*":
515
+ return Literal(True)
516
+ return Literal(t == r or t.startswith(r + "-"))
517
+
518
+
519
+ def build_iri(parts, base=None):
520
+ """e<...>: concatenate the parts — statics as they are, values through
521
+ STR() then IRI-safe encoding (the IRI(CONCAT(ENCODE_FOR_IRI…)) semantics
522
+ of the dev-sparql specification) — then resolve against the lexical base."""
523
+ from ldpy.runtime import firi
524
+ out = []
525
+ for p in parts:
526
+ if type(p) is str: # partie statique du gabarit
527
+ out.append(p)
528
+ else: # a value (Literal IS a str: exact type)
529
+ out.append(_iri_safe(str(STR(p))))
530
+ iri = "".join(out)
531
+ if base:
532
+ return firi(iri, base=base)
533
+ return URIRef(iri)
534
+
535
+
536
+ _IUNRESERVED = "-._~"
537
+
538
+
539
+ def _iri_safe(value):
540
+ """ENCODE_FOR_IRI (aligned on the R2RML harness: iunreserved preserved)."""
541
+ out = []
542
+ for ch in value:
543
+ if ch.isalnum() and ch.isascii() or ch in _IUNRESERVED \
544
+ or not ch.isascii():
545
+ out.append(ch)
546
+ else:
547
+ out.append("".join("%%%02X" % b for b in ch.encode("utf-8")))
548
+ return "".join(out)
549
+
550
+
551
+ ENCODE_FOR_IRI = lambda t: Literal(_iri_safe(str(t))) # noqa: E731
@@ -0,0 +1,14 @@
1
+ """Transpileur Linked-Data Python v2 (island parsing).
2
+
3
+ API publique : transpile(source, filename) -> TranspileResult.
4
+ """
5
+
6
+ from ldpy.transpiler.core import transpile, Transpiler, TranspileResult, \
7
+ RUNTIME_ALIAS, PRELUDE
8
+ from ldpy.transpiler.errors import LdpySyntaxError, LdpyWarning
9
+ from ldpy.transpiler.linemap import LanguageMap, Segment
10
+
11
+ __all__ = [
12
+ "transpile", "Transpiler", "TranspileResult", "RUNTIME_ALIAS", "PRELUDE",
13
+ "LdpySyntaxError", "LdpyWarning", "LanguageMap", "Segment",
14
+ ]