linked-data-python 0.0.4__py3-none-any.whl → 0.2.1__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 +47 -11
  2. ldpy/__main__.py +75 -250
  3. ldpy/build.py +91 -0
  4. ldpy/console.py +116 -0
  5. ldpy/debug.py +239 -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.1.dist-info/METADATA +158 -0
  22. linked_data_python-0.2.1.dist-info/RECORD +26 -0
  23. {linked_data_python-0.0.4.dist-info → linked_data_python-0.2.1.dist-info}/WHEEL +1 -1
  24. linked_data_python-0.2.1.dist-info/entry_points.txt +9 -0
  25. {linked_data_python-0.0.4.dist-info → linked_data_python-0.2.1.dist-info/licenses}/LICENSE.md +0 -0
  26. {linked_data_python-0.0.4.dist-info → linked_data_python-0.2.1.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
@@ -0,0 +1,38 @@
1
+ """Errors and warnings of the Linked-Data Python transpiler."""
2
+
3
+
4
+ class LdpySyntaxError(SyntaxError):
5
+ """A syntax error in a .ldpy source.
6
+
7
+ line and col are 0-based internally; SyntaxError.lineno/offset are filled
8
+ in 1-based, as Python requires.
9
+ """
10
+
11
+ def __init__(self, message, filename="<ldpy>", line=0, col=0):
12
+ super().__init__(message)
13
+ self.msg = message
14
+ self.filename = filename
15
+ self.lineno = line + 1
16
+ self.offset = col + 1
17
+ self.line = line
18
+ self.col = col
19
+
20
+ def __str__(self):
21
+ return "%s (%s, ligne %d:%d)" % (self.msg, self.filename, self.lineno, self.offset)
22
+
23
+
24
+ class LdpyWarning:
25
+ """A non-blocking warning emitted during transpilation."""
26
+
27
+ def __init__(self, message, filename="<ldpy>", line=0, col=0):
28
+ self.message = message
29
+ self.filename = filename
30
+ self.line = line
31
+ self.col = col
32
+
33
+ def __str__(self):
34
+ return "LdpyWarning: %s (%s, ligne %d:%d)" % (
35
+ self.message, self.filename, self.line + 1, self.col + 1)
36
+
37
+ def __repr__(self):
38
+ return "LdpyWarning(%r)" % (self.message,)
@@ -0,0 +1,292 @@
1
+ """Segment-level language map between a .ldpy source and the generated Python.
2
+
3
+ Voir docs/reference/language-map.md.
4
+
5
+ Positions 0-based, fins exclusives. Trois sortes de segments :
6
+ - "copy" : text copied verbatim -> exact position translation;
7
+ - "island:*" : rewritten RDF island -> translation at region granularity;
8
+ - "synthetic" : generated text with no origin (the runtime import prelude).
9
+ """
10
+
11
+ import json
12
+
13
+
14
+ class Segment:
15
+ """One source/generated correspondence segment (copy, island:*, synthetic)."""
16
+
17
+ __slots__ = ("kind", "src", "gen")
18
+
19
+ def __init__(self, kind, src, gen):
20
+ self.kind = kind
21
+ self.src = src # (line0, col0, line1, col1), or None for synthetic
22
+ self.gen = gen # (line0, col0, line1, col1)
23
+
24
+ def __repr__(self):
25
+ return "Segment(%r, src=%r, gen=%r)" % (self.kind, self.src, self.gen)
26
+
27
+ def to_dict(self):
28
+ """Forme JSON du segment."""
29
+ d = {"kind": self.kind, "gen": list(self.gen)}
30
+ if self.src is not None:
31
+ d["src"] = list(self.src)
32
+ return d
33
+
34
+
35
+ def _pos_in(range4, line, col):
36
+ l0, c0, l1, c1 = range4
37
+ if line < l0 or line > l1:
38
+ return False
39
+ if line == l0 and col < c0:
40
+ return False
41
+ if line == l1 and col >= c1 and not (l0 == l1 and c0 == c1):
42
+ return False
43
+ return True
44
+
45
+
46
+ def _translate_copy(range_from, range_to, line, col):
47
+ fl0, fc0, _, _ = range_from
48
+ tl0, tc0, _, _ = range_to
49
+ if line == fl0:
50
+ return (tl0, col - fc0 + tc0)
51
+ return (line - fl0 + tl0, col)
52
+
53
+
54
+ class LanguageMap:
55
+ """Bidirectional .ldpy <-> generated Python correspondence
56
+ (an ordered list of Segments; see docs/reference/language-map.md)."""
57
+
58
+ def __init__(self, source_name="<ldpy>", generated_name=None):
59
+ self.source_name = source_name
60
+ self.generated_name = generated_name
61
+ self.segments = [] # ordered by increasing gen AND src positions
62
+
63
+ def add(self, kind, src, gen):
64
+ """Add a segment (segments empty on both sides are ignored)."""
65
+ # ignore segments that are empty on both sides
66
+ if src is not None and src[:2] == src[2:] and gen[:2] == gen[2:]:
67
+ return
68
+ self.segments.append(Segment(kind, src, gen))
69
+
70
+ # -- traduction ---------------------------------------------------------
71
+
72
+ def to_src(self, line, col):
73
+ """Generated position -> source position (None if synthetic)."""
74
+ for seg in self.segments:
75
+ if _pos_in(seg.gen, line, col):
76
+ if seg.src is None:
77
+ return None
78
+ if seg.kind == "copy":
79
+ return _translate_copy(seg.gen, seg.src, line, col)
80
+ return (seg.src[0], seg.src[1])
81
+ return None
82
+
83
+ def to_gen(self, line, col):
84
+ """Source position -> generated position."""
85
+ for seg in self.segments:
86
+ if seg.src is not None and _pos_in(seg.src, line, col):
87
+ if seg.kind == "copy":
88
+ return _translate_copy(seg.src, seg.gen, line, col)
89
+ return (seg.gen[0], seg.gen[1])
90
+ return None
91
+
92
+ def src_line_for_gen_line(self, line):
93
+ """Source line matching a generated line (for tracebacks)."""
94
+ best = None
95
+ for seg in self.segments:
96
+ if seg.src is None:
97
+ continue
98
+ if seg.gen[0] <= line <= seg.gen[2]:
99
+ if seg.kind == "copy":
100
+ return _translate_copy(seg.gen, seg.src, line,
101
+ seg.gen[1] if line == seg.gen[0] else 0)[0]
102
+ best = seg.src[0]
103
+ return best
104
+
105
+ # -- serialisation ------------------------------------------------------
106
+
107
+ def to_dict(self):
108
+ """Forme JSON (version 1, format maison)."""
109
+ return {
110
+ "version": 1,
111
+ "source": self.source_name,
112
+ "generated": self.generated_name,
113
+ "segments": [s.to_dict() for s in self.segments],
114
+ }
115
+
116
+ def to_json(self, **kw):
117
+ """Serialise to JSON (kwargs passed to json.dumps)."""
118
+ return json.dumps(self.to_dict(), **kw)
119
+
120
+ @classmethod
121
+ def from_dict(cls, d):
122
+ """Reconstruit une map depuis sa forme JSON."""
123
+ m = cls(d.get("source", "<ldpy>"), d.get("generated"))
124
+ for sd in d.get("segments", []):
125
+ src = tuple(sd["src"]) if "src" in sd else None
126
+ m.segments.append(Segment(sd["kind"], src, tuple(sd["gen"])))
127
+ return m
128
+
129
+ @classmethod
130
+ def from_json(cls, s):
131
+ """Rebuild a map from a JSON string."""
132
+ return cls.from_dict(json.loads(s))
133
+
134
+
135
+ def snap_breakpoint_line(lmap, line_1based):
136
+ """The .ldpy line where a breakpoint set on `line_1based` will REALLY bind.
137
+
138
+ A multi-line island collapses into one statement whose code object carries
139
+ the START line (record ldpy/011): no interior line is executable. Yet
140
+ pydevd answers `verified: true` to a breakpoint set there, and never stops
141
+ on it — a silent lie (measured, record vscode/103). So we snap the line to
142
+ the island's start, and tooling can MOVE the dot to say so.
143
+ l'outillage peut DÉPLACER la pastille pour le dire.
144
+
145
+ Returns the same line when there is nothing to snap."""
146
+ line0 = line_1based - 1
147
+ for seg in lmap.segments:
148
+ if seg.src is None or seg.kind == "copy":
149
+ continue
150
+ if seg.src[0] < line0 <= seg.src[2]:
151
+ return seg.src[0] + 1
152
+ return line_1based
153
+
154
+
155
+ def snap_breakpoint_lines(lmap, lines_1based):
156
+ """`snap_breakpoint_line` over a list (order preserved)."""
157
+ return [snap_breakpoint_line(lmap, l) for l in lines_1based]
158
+
159
+
160
+ # ---------------------------------------------------------------------------
161
+ # "Remapped" compilation: the generated code is compiled with the line numbers
162
+ # OF THE .ldpy SOURCE (through the map), so that tracebacks, pdb and debugpy
163
+ # all speak directly in .ldpy coordinates (record ldpy/011).
164
+ # ---------------------------------------------------------------------------
165
+
166
+
167
+ def remap_ast_lines(tree, lmap):
168
+ """Rewrite lineno/end_lineno of every node of the GENERATED code's AST to
169
+ the .ldpy source lines. A generated line with no origin (the synthetic
170
+ prelude) snaps to line 1; the inside of a collapsed island snaps to the
171
+ island's start line. Columns are kept as they are (co_positions are
172
+ approximate on rewritten lines)."""
173
+ import ast
174
+ cache = {}
175
+
176
+ def src_line(gen_1based):
177
+ if gen_1based not in cache:
178
+ s = lmap.src_line_for_gen_line(gen_1based - 1)
179
+ cache[gen_1based] = (s + 1) if s is not None else None
180
+ return cache[gen_1based]
181
+
182
+ for node in ast.walk(tree):
183
+ lineno = getattr(node, "lineno", None)
184
+ if lineno is None:
185
+ continue
186
+ new_lineno = src_line(lineno) or 1
187
+ node.lineno = new_lineno
188
+ end = getattr(node, "end_lineno", None)
189
+ if end is not None:
190
+ new_end = src_line(end) or new_lineno
191
+ node.end_lineno = max(new_end, new_lineno)
192
+ return tree
193
+
194
+
195
+ def compile_mapped(gen_code, lmap, filename, mode="exec",
196
+ dont_inherit=True, optimize=-1):
197
+ """Compile the generated Python code with `filename` (the .ldpy) and the
198
+ source line numbers, through `remap_ast_lines`. On an unexpected AST
199
+ parse failure, fall back to an ordinary compilation (generated lines)."""
200
+ import ast
201
+ try:
202
+ tree = ast.parse(gen_code, filename, mode)
203
+ except SyntaxError:
204
+ return compile(gen_code, filename, mode,
205
+ dont_inherit=dont_inherit, optimize=optimize)
206
+ remap_ast_lines(tree, lmap)
207
+ return compile(tree, filename, mode,
208
+ dont_inherit=dont_inherit, optimize=optimize)
209
+
210
+
211
+ # ---------------------------------------------------------------------------
212
+ # Export Source Map v3 : le format standard
213
+ # JavaScript tooling, to interoperate with the tools that read it.
214
+ # https://tc39.es/ecma426/ — champs [genCol, srcIdx, srcLine, srcCol] en
215
+ # base64-VLQ, as deltas; one ";" entry per generated line.
216
+ # ---------------------------------------------------------------------------
217
+
218
+ _B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
219
+
220
+
221
+ def _vlq(value):
222
+ """Encode a signed integer in base64-VLQ (zigzag + groups of 5 bits)."""
223
+ v = (value << 1) if value >= 0 else ((-value << 1) | 1)
224
+ out = []
225
+ while True:
226
+ digit = v & 0x1F
227
+ v >>= 5
228
+ if v:
229
+ digit |= 0x20
230
+ out.append(_B64[digit])
231
+ if not v:
232
+ return "".join(out)
233
+
234
+
235
+ def _mapping_points(lmap):
236
+ """Points (gen_line, gen_col, src_line, src_col), sorted, deduplicated.
237
+
238
+ One point per island start; for a copy segment, one point per generated
239
+ line covered (the standard debugger granularity)."""
240
+ points = {}
241
+ for seg in lmap.segments:
242
+ if seg.src is None:
243
+ continue
244
+ gl0, gc0, gl1, gc1 = seg.gen
245
+ sl0, sc0, _, _ = seg.src
246
+ if seg.kind == "copy":
247
+ # exclusive end: if the segment ends at column 0, its last
248
+ # "line" is empty and carries no point
249
+ last = gl1 if gc1 > 0 else gl1 - 1
250
+ for l in range(gl0, last + 1):
251
+ gcol = gc0 if l == gl0 else 0
252
+ scol = sc0 if l == gl0 else 0
253
+ points.setdefault((l, gcol), (sl0 + (l - gl0), scol))
254
+ else:
255
+ points.setdefault((gl0, gc0), (sl0, sc0))
256
+ return sorted((g[0], g[1], s[0], s[1]) for g, s in points.items())
257
+
258
+
259
+ def _to_sourcemap_v3(self):
260
+ """Return the Source Map v3 dict equivalent to this map."""
261
+ points = _mapping_points(self)
262
+ lines = []
263
+ prev_gcol = prev_sline = prev_scol = 0
264
+ cur_line = 0
265
+ buf = []
266
+ for gline, gcol, sline, scol in points:
267
+ while cur_line < gline:
268
+ lines.append(",".join(buf))
269
+ buf = []
270
+ prev_gcol = 0
271
+ cur_line += 1
272
+ seg = (_vlq(gcol - prev_gcol) + _vlq(0) +
273
+ _vlq(sline - prev_sline) + _vlq(scol - prev_scol))
274
+ buf.append(seg)
275
+ prev_gcol, prev_sline, prev_scol = gcol, sline, scol
276
+ lines.append(",".join(buf))
277
+ return {
278
+ "version": 3,
279
+ "file": self.generated_name or "",
280
+ "sources": [self.source_name],
281
+ "names": [],
282
+ "mappings": ";".join(lines),
283
+ }
284
+
285
+
286
+ def _to_sourcemap_v3_json(self, **kw):
287
+ import json as _json
288
+ return _json.dumps(self.to_sourcemap_v3(), **kw)
289
+
290
+
291
+ LanguageMap.to_sourcemap_v3 = _to_sourcemap_v3
292
+ LanguageMap.to_sourcemap_v3_json = _to_sourcemap_v3_json
@@ -0,0 +1,158 @@
1
+ Metadata-Version: 2.4
2
+ Name: linked-data-python
3
+ Version: 0.2.1
4
+ Summary: Python extended with Semantic Web primitives: IRIs, RDF literals and Turtle-notation graphs as expressions, transpiled by island parsing.
5
+ Author-email: Maxime Lefrançois <maxime.lefrancois@emse.fr>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/linked-data-python/ldpy
8
+ Project-URL: Documentation, https://linked-data-python.readthedocs.io/
9
+ Project-URL: Source, https://github.com/linked-data-python/ldpy
10
+ Keywords: rdf,semantic-web,turtle,sparql,transpiler,linked-data,knowledge-graph
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Compilers
20
+ Classifier: Topic :: Software Development :: Pre-processors
21
+ Classifier: Topic :: Text Processing :: Markup
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE.md
25
+ Requires-Dist: rdflib>=6.0
26
+ Provides-Extra: lsp
27
+ Requires-Dist: python-lsp-server; extra == "lsp"
28
+ Requires-Dist: pyflakes; extra == "lsp"
29
+ Provides-Extra: debug
30
+ Requires-Dist: debugpy; extra == "debug"
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest; extra == "dev"
33
+ Provides-Extra: highlight
34
+ Requires-Dist: pygments>=2.14; extra == "highlight"
35
+ Provides-Extra: format
36
+ Requires-Dist: black>=24; extra == "format"
37
+ Provides-Extra: docs
38
+ Requires-Dist: mkdocs; extra == "docs"
39
+ Requires-Dist: mkdocs-material; extra == "docs"
40
+ Requires-Dist: pygments>=2.14; extra == "docs"
41
+ Dynamic: license-file
42
+
43
+ # Linked-Data Python
44
+
45
+ **Python, with the Semantic Web in its syntax.** IRIs, prefixed names, RDF
46
+ literals, SPARQL variables and whole graphs written in Turtle's notation are
47
+ expressions of the language — interpolated with arbitrary Python, transpiled to
48
+ plain Python, running on rdflib.
49
+
50
+ ![](ldpyIcon.png)
51
+
52
+ ```text
53
+ @prefix sosa: <http://www.w3.org/ns/sosa/> .
54
+ @base <http://example.org/building/> .
55
+
56
+ def observation(sensor, value):
57
+ return g{ f<sensor/{sensor}> a sosa:Sensor ;
58
+ sosa:madeObservation [ sosa:hasSimpleResult {value} ] }
59
+ ```
60
+
61
+ The language also reads and queries, writes into a current graph, and turns
62
+ rows into triples:
63
+
64
+ ```text
65
+ @prefix ex: <http://example.org/> .
66
+ @graph as kg
67
+ for @bindings in csv.DictReader(f): # any iterable of mappings
68
+ +{ e<http://example.org/{?id}> ex:value ?v }
69
+
70
+ for s, v in m{ ?s ex:value ?v }: # a graph pattern, no engine
71
+ ...
72
+ rows = s{ SELECT ?s WHERE { ?s ex:value ?v } } # all of SPARQL, checked early
73
+ adult = e{ ?age >= 18 && BOUND(?name) } # deferred, over bindings
74
+ ```
75
+
76
+ `.ldpy` files are **transpiled to plain Python** by an *island parser*: the
77
+ Python is copied verbatim — every valid Python file is a valid ldpy file,
78
+ returned byte-identical — and only the RDF islands are parsed and rewritten.
79
+ The transpiler is ~1 500 lines with no parsing dependency and sustains
80
+ 56 000–110 000 source lines/s depending on island density.
81
+
82
+ ## Quick start
83
+
84
+ ```text
85
+ git clone https://github.com/linked-data-python/ldpy.git
86
+ cd linked-data-python && pip install -e . # or: pip install -e .[lsp,debug]
87
+
88
+ ldpy program.ldpy # run a file
89
+ ldpy # interactive console
90
+ ldpy-lsp # language server (LSP, stdio)
91
+ ldpy-debug program.ldpy # debug via the shadow .py + debugpy
92
+ ```
93
+
94
+ From Python: `import ldpy; ldpy.install()` then `import yourmodule` finds
95
+ `yourmodule.ldpy` on `sys.path`.
96
+
97
+ ## Documentation
98
+
99
+ Read it at **<https://linked-data-python.readthedocs.io/>** — start with the
100
+ home page for an overview, then:
101
+
102
+ - **Tutorials** — [first steps](https://linked-data-python.readthedocs.io/en/latest/tutorials/getting-started/), then
103
+ [build a knowledge graph](https://linked-data-python.readthedocs.io/en/latest/tutorials/build-a-knowledge-graph/) from
104
+ tabular data.
105
+ - **How-to guides** — [run & import](https://linked-data-python.readthedocs.io/en/latest/how-to/run-and-import/),
106
+ [build graphs from tables](https://linked-data-python.readthedocs.io/en/latest/how-to/build-graphs-from-tables/),
107
+ [read and query](https://linked-data-python.readthedocs.io/en/latest/how-to/query-a-graph/),
108
+ [migrate from rdflib](https://linked-data-python.readthedocs.io/en/latest/how-to/migrate-from-rdflib/),
109
+ [VS Code](https://linked-data-python.readthedocs.io/en/latest/how-to/use-vscode/), [debugging](https://linked-data-python.readthedocs.io/en/latest/how-to/debug/),
110
+ [language server](https://linked-data-python.readthedocs.io/en/latest/how-to/language-server/),
111
+ [highlighting](https://linked-data-python.readthedocs.io/en/latest/how-to/highlight-ldpy/).
112
+ - **Reference** — [the language](https://linked-data-python.readthedocs.io/en/latest/reference/language/), one page
113
+ per island family; [SPARQL expressions](https://linked-data-python.readthedocs.io/en/latest/reference/sparql-expressions/);
114
+ [CLI](https://linked-data-python.readthedocs.io/en/latest/reference/cli/); [Python API](https://linked-data-python.readthedocs.io/en/latest/reference/api/);
115
+ [language map formats](https://linked-data-python.readthedocs.io/en/latest/reference/language-map/).
116
+ - **Explanation** — [why](https://linked-data-python.readthedocs.io/en/latest/explanation/why/),
117
+ [what real RDF code does](https://linked-data-python.readthedocs.io/en/latest/explanation/what-real-code-does/) (the
118
+ corpus study that drove the language's second wave),
119
+ [designing the syntax](https://linked-data-python.readthedocs.io/en/latest/explanation/designing-the-syntax/),
120
+ [island parsing](https://linked-data-python.readthedocs.io/en/latest/explanation/island-parsing/),
121
+ [emission & semantics](https://linked-data-python.readthedocs.io/en/latest/explanation/emission-and-semantics/),
122
+ [tooling](https://linked-data-python.readthedocs.io/en/latest/explanation/tooling/),
123
+ [how this is tested](https://linked-data-python.readthedocs.io/en/latest/explanation/how-it-is-tested/).
124
+
125
+ Every `ldpy` and `python` block in the documentation is executed by the test
126
+ suite, and its assertions are the test.
127
+
128
+ ## Tooling
129
+
130
+ - **VS Code extension** (`vscode-ldpy`): highlighting (TextMate + LSP semantic
131
+ tokens), diagnostics as you type, completion/hover/definition, run and debug.
132
+ - **Language server**: dependency-free, LSP over stdio; delegates Python
133
+ intelligence to an unmodified `pylsp` through the language map.
134
+ - **Debugging**: `.ldpy` code compiles in `.ldpy` coordinates, so `pdb` and
135
+ `debugpy` work directly; `ldpy.build` also materialises real `.py` shadow
136
+ files with JSON and Source Map v3 maps.
137
+ - **Highlighting anywhere else**: the package registers a Pygments lexer built
138
+ on the language map — MkDocs, Sphinx and `pygmentize` colour `.ldpy` with no
139
+ further setup.
140
+ - **Benchmark harness** (`bench/`): seeded random program generator and
141
+ reproducible throughput campaigns.
142
+
143
+ ## Project
144
+
145
+ - Tests: `python -m pytest tests/ -q` — byte-identity over the CPython standard
146
+ library, golden transpilation, RDF isomorphism against rdflib as an oracle,
147
+ LSP end to end, executable documentation.
148
+ - Licence: MIT. Author: Maxime Lefrançois (Mines Saint-Étienne).
149
+ - The 2023 ANTLR-based release (v1, PyPI 0.0.4) is preliminary work, superseded
150
+ by this island-parsing rewrite (the `main` branch of this repository; the 2023
151
+ code remains on the legacy gitlab.com/coswot/ldpy).
152
+
153
+ ## Design records
154
+
155
+ Every non-trivial choice in this repository is written down, one file per
156
+ decision, in the [`pilotage`](https://github.com/linked-data-python/pilotage) repository. Comments and docs
157
+ cite them by identifier — `ldpy/024`, `vscode/103` — which resolves to
158
+ [`design/`](https://github.com/linked-data-python/pilotage/tree/main/design).
@@ -0,0 +1,26 @@
1
+ ldpy/__init__.py,sha256=IyuzHrp5OtuGO_NrrdmMUQW6bTYcyS5uob7CeoXg8mY,1943
2
+ ldpy/__main__.py,sha256=YKTEjmNQuh_0p-KiEus9HngFs_ZDmS56RXzHTspiWVI,3077
3
+ ldpy/build.py,sha256=495F9KyFMar-hTCuaQkF3FvzWQX4mE9H0U0V-JuRDmA,3394
4
+ ldpy/console.py,sha256=1FrroxoTTWG7fIBzaDQvOxPGj6NkyXJDJhhl5RXyE_0,4204
5
+ ldpy/debug.py,sha256=5o8oVYIbFk6rB9-ShTIfym_bhVG3imTw_l0taJZ9qH0,9462
6
+ ldpy/formatter.py,sha256=VaDPECxhivsMe8jnX_fOL7BUyLEu5r4xREdr4Hav-0c,12695
7
+ ldpy/importer.py,sha256=QXAhSujPN6rFMJSQuj0UsWyI4CajirKt1SOrykJ_fUw,3670
8
+ ldpy/pygments_lexer.py,sha256=mcQ0u9nZjLnSWpqzcxd6bZjk69LfKSNTsf9A3rqfhhk,25320
9
+ ldpy/runtime.py,sha256=k1k_DfblW4Dxacu4BkZpfuEPV1mMN83PkLtML9SAoms,33390
10
+ ldpy/sparql.py,sha256=CHSB4v5lEuVQKRPuwcM8xI3jN2FVHkmPVb675QkAD9Y,14770
11
+ ldpy/lsp/__init__.py,sha256=rK5GYOITi9PN09-WVlJ2aPKodW8dWTpgI_X6dFbP1zA,542
12
+ ldpy/lsp/__main__.py,sha256=MIp4ktqaoWZCNIMfv14bSC9pqK86JBS5XVjd1Z3c24o,72
13
+ ldpy/lsp/backend.py,sha256=c5Jx1fZ1ZhoYet9__RB1mUqFUtcCl63tO8vgnv8iMII,4644
14
+ ldpy/lsp/rpc.py,sha256=-TF_9U8dum23Pi4O1FZLogwoZgrGrt14J5BFB1OYVZw,3554
15
+ ldpy/lsp/server.py,sha256=Xpl1ApcNlauLjEiabITOTJ6i8aVGDdPB7SgPxK2ePJs,14329
16
+ ldpy/lsp/translate.py,sha256=_xDnKrJdx02puRkcuzhGwCLdEByuvhTfSIc9dAFR6a8,4931
17
+ ldpy/transpiler/__init__.py,sha256=9mQx0e47ixz0iMqSbCBbtGiwJ7PLxTcsVSIp6KNTMG4,504
18
+ ldpy/transpiler/core.py,sha256=s0jzTHSsazF7Xm-32q_Km6dKCxyjWEucmyVqd0eTP1s,106350
19
+ ldpy/transpiler/errors.py,sha256=zEEwsNvC2Tawq8jIYls7hrbcWSvQtd7eo89k_SKp0k0,1142
20
+ ldpy/transpiler/linemap.py,sha256=6b7y0nNmgnAMr_bqomgty7V0RgWPXFAp_8BZE4VYqzc,10580
21
+ linked_data_python-0.2.1.dist-info/licenses/LICENSE.md,sha256=R6KjQ098eAFd2k5mKaSRJqMH1LhY4o7xkHrKT5vjRfs,1074
22
+ linked_data_python-0.2.1.dist-info/METADATA,sha256=5fwrzPfP6GRlap3pL9JSnjS3AKil4J3ACiBVdisCdrg,7974
23
+ linked_data_python-0.2.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
24
+ linked_data_python-0.2.1.dist-info/entry_points.txt,sha256=n8Vs52oH81BrKOe1DNUvdFLSOlEufRMpK2waPFa6aYk,224
25
+ linked_data_python-0.2.1.dist-info/top_level.txt,sha256=dkG_F4jYkh6CS6gdpQeV_gKW3yaJBvl1ukUu0zo2KA8,5
26
+ linked_data_python-0.2.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.34.2)
2
+ Generator: setuptools (84.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,9 @@
1
+ [console_scripts]
2
+ ldpy = ldpy.__main__:main
3
+ ldpy-build = ldpy.build:main
4
+ ldpy-debug = ldpy.debug:main
5
+ ldpy-format = ldpy.formatter:main
6
+ ldpy-lsp = ldpy.lsp.server:main
7
+
8
+ [pygments.lexers]
9
+ ldpy = ldpy.pygments_lexer:LdpyLexer
ldpy/grun/lib.py DELETED
@@ -1,63 +0,0 @@
1
- # from https://github.com/charmoniumQ/antlr4-python-grun
2
-
3
- import enum
4
- import json
5
- import importlib
6
- import subprocess
7
- import shutil
8
- from pathlib import Path
9
- import os
10
- from typing import Tuple, Iterable
11
- import antlr4 # type: ignore
12
- from . import util
13
-
14
- def readTokenTypes(path):
15
- tokenTypes = dict()
16
- tokenTypes[-1] = "EOF"
17
- with open(path, 'r') as tokensFile:
18
- for line in tokensFile:
19
- i = line.rindex("=")
20
- tokenTypes[int(line[i+1:])] = line[:i]
21
- return tokenTypes
22
-
23
- def format_tree(
24
- root: antlr4.ParserRuleContext,
25
- pretty: bool,
26
- tokenTypes: dict()
27
- ) -> str:
28
- stack = [(root, True)]
29
- depth = 0
30
- buf = []
31
- while stack:
32
- node, last = stack.pop()
33
- if node == "end":
34
- depth -= 1
35
- # buf.append(depth * " " if pretty else "")
36
- # buf.append(")")
37
- # if not last:
38
- # buf.append(" ")
39
- elif isinstance(node, antlr4.tree.Tree.TerminalNodeImpl):
40
- buf.append(depth * " " if pretty else "")
41
- token = node.symbol
42
- lexer = token.getTokenSource()
43
- token_type_map = {
44
- symbolic_id + len(lexer.literalNames) - 1: symbolic_name
45
- for symbolic_id, symbolic_name in enumerate(lexer.symbolicNames)
46
- }
47
- type_ = token_type_map.get(token.type, "literal")
48
- buf.append(f"({tokenTypes[token.type] if token.type in tokenTypes else token.type} {token.text})")
49
- if not last:
50
- buf.append(" ")
51
- if pretty:
52
- buf.append("\n")
53
- else:
54
- buf.append(depth * " " if pretty else "")
55
- rule_name = node.parser.ruleNames[node.getRuleIndex()]
56
- buf.append(f"({rule_name}")
57
- depth += 1
58
- children = [] if node.children is None else node.children
59
- stack.append(("end", last))
60
- stack.extend(util.first_sentinel(list(children)[::-1]))
61
- if pretty:
62
- buf.append("\n")
63
- return "".join(buf)
ldpy/grun/util.py DELETED
@@ -1,11 +0,0 @@
1
- # from https://github.com/charmoniumQ/antlr4-python-grun
2
- from typing import TypeVar, Iterator, Tuple, Iterable
3
-
4
- Value = TypeVar("Value")
5
-
6
- def first_sentinel(iterable: Iterable[Value]) -> Iterator[Tuple[Value, bool]]:
7
- iterator = iter(iterable)
8
- yield (next(iterator), True)
9
- for elem in iterator:
10
- yield (elem, False)
11
-