clindocs 1.5.2__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. clindocs-1.5.2.dist-info/METADATA +81 -0
  2. clindocs-1.5.2.dist-info/RECORD +41 -0
  3. clindocs-1.5.2.dist-info/WHEEL +5 -0
  4. clindocs-1.5.2.dist-info/entry_points.txt +2 -0
  5. clindocs-1.5.2.dist-info/licenses/LICENSE +21 -0
  6. clindocs-1.5.2.dist-info/top_level.txt +1 -0
  7. mkdocstrings_handlers/asp/__init__.py +5 -0
  8. mkdocstrings_handlers/asp/_internal/__init__.py +0 -0
  9. mkdocstrings_handlers/asp/_internal/collect/__init__.py +0 -0
  10. mkdocstrings_handlers/asp/_internal/collect/debug.py +19 -0
  11. mkdocstrings_handlers/asp/_internal/collect/extractors.py +317 -0
  12. mkdocstrings_handlers/asp/_internal/collect/load.py +92 -0
  13. mkdocstrings_handlers/asp/_internal/collect/queries/body.scm +11 -0
  14. mkdocstrings_handlers/asp/_internal/collect/queries/documentation_argument.scm +4 -0
  15. mkdocstrings_handlers/asp/_internal/collect/queries/documentation_predicate.scm +12 -0
  16. mkdocstrings_handlers/asp/_internal/collect/queries/head.scm +54 -0
  17. mkdocstrings_handlers/asp/_internal/collect/queries/predicate.scm +29 -0
  18. mkdocstrings_handlers/asp/_internal/collect/queries/show.scm +15 -0
  19. mkdocstrings_handlers/asp/_internal/collect/syntax.py +95 -0
  20. mkdocstrings_handlers/asp/_internal/config.py +135 -0
  21. mkdocstrings_handlers/asp/_internal/domain.py +140 -0
  22. mkdocstrings_handlers/asp/_internal/error.py +5 -0
  23. mkdocstrings_handlers/asp/_internal/handler.py +175 -0
  24. mkdocstrings_handlers/asp/_internal/render/__init__.py +0 -0
  25. mkdocstrings_handlers/asp/_internal/render/dependency_graph_context.py +66 -0
  26. mkdocstrings_handlers/asp/_internal/render/encodings_context.py +139 -0
  27. mkdocstrings_handlers/asp/_internal/render/glossary_context.py +155 -0
  28. mkdocstrings_handlers/asp/_internal/render/predicate_info.py +197 -0
  29. mkdocstrings_handlers/asp/_internal/render/predicate_table_context.py +56 -0
  30. mkdocstrings_handlers/asp/_internal/render/render_context.py +74 -0
  31. mkdocstrings_handlers/asp/py.typed +0 -0
  32. mkdocstrings_handlers/asp/templates/material/dependency_graph.html.jinja +55 -0
  33. mkdocstrings_handlers/asp/templates/material/documentation.html.jinja +13 -0
  34. mkdocstrings_handlers/asp/templates/material/encodings.html.jinja +59 -0
  35. mkdocstrings_handlers/asp/templates/material/glossary.html.jinja +67 -0
  36. mkdocstrings_handlers/asp/templates/material/glossary_references.html.jinja +51 -0
  37. mkdocstrings_handlers/asp/templates/material/icons.html.jinja +62 -0
  38. mkdocstrings_handlers/asp/templates/material/predicate_table.html.jinja +39 -0
  39. mkdocstrings_handlers/asp/templates/material/separator.html.jinja +3 -0
  40. mkdocstrings_handlers/asp/templates/material/source.html.jinja +8 -0
  41. mkdocstrings_handlers/asp/templates/material/style.css +89 -0
@@ -0,0 +1,81 @@
1
+ Metadata-Version: 2.4
2
+ Name: clindocs
3
+ Version: 1.5.2
4
+ Summary: Mkdocs plugin to generate documentation from clingo files
5
+ Author-email: Potassco <hahnmartinlu@uni-potsdam.de>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024 Potassco
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/potassco/clindocs.git/
29
+ Requires-Python: >=3.11
30
+ Description-Content-Type: text/markdown
31
+ License-File: LICENSE
32
+ Requires-Dist: clingo
33
+ Requires-Dist: mkdocstrings>=1.0.0
34
+ Requires-Dist: tree-sitter
35
+ Requires-Dist: tree-sitter-clingo>=1.0.5
36
+ Requires-Dist: pydantic
37
+ Requires-Dist: pygments_clingo
38
+ Requires-Dist: mkdocs>=1.5
39
+ Requires-Dist: mkdocs-material>=9.5
40
+ Requires-Dist: mkdocs-autorefs>=1.4
41
+ Provides-Extra: format
42
+ Requires-Dist: black; extra == "format"
43
+ Requires-Dist: isort; extra == "format"
44
+ Requires-Dist: autoflake; extra == "format"
45
+ Provides-Extra: lint-pylint
46
+ Requires-Dist: pylint; extra == "lint-pylint"
47
+ Requires-Dist: djlint; extra == "lint-pylint"
48
+ Provides-Extra: typecheck
49
+ Requires-Dist: types-setuptools; extra == "typecheck"
50
+ Requires-Dist: mypy; extra == "typecheck"
51
+ Requires-Dist: types-Markdown; extra == "typecheck"
52
+ Requires-Dist: types-Pygments; extra == "typecheck"
53
+ Provides-Extra: test
54
+ Requires-Dist: coverage[toml]; extra == "test"
55
+ Requires-Dist: pytest; extra == "test"
56
+ Provides-Extra: doc
57
+ Requires-Dist: mkdocstrings[python]; extra == "doc"
58
+ Requires-Dist: mkdocs-literate-nav; extra == "doc"
59
+ Provides-Extra: dev
60
+ Requires-Dist: clindocs[doc,lint_pylint,test,typecheck]; extra == "dev"
61
+ Dynamic: license-file
62
+
63
+ # clindocs
64
+
65
+ **clindocs** is an automated documentation tool tailored for **Answer Set
66
+ Programming (ASP)** code. Built on [MkDocs](https://www.mkdocs.org/) and
67
+ [mkdocs-material](https://squidfunk.github.io/mkdocs-material/), it streamlines
68
+ the creation of high-quality documentation with the following features:
69
+
70
+ - **Render encodings**: Automatically format ASP encodings with comments
71
+ written in Markdown.
72
+ - **Predicate analysis**: Collect and document predicates used across included
73
+ files.
74
+ - **Navigation-friendly documentation**: Generate organized predicate
75
+ documentation with intuitive navigation.
76
+ - **Input/output identification**: Detect and highlight input and output
77
+ predicates.
78
+ - **Dependency graphs**: Visualize dependencies between predicates and files.
79
+
80
+ For installation instructions and detailed usage, visit our
81
+ [official documentation](https://potassco.org/clindocs/docs).
@@ -0,0 +1,41 @@
1
+ clindocs-1.5.2.dist-info/licenses/LICENSE,sha256=ENv21OEPTlPYM34OARsrJK5nyORaoT0-Bp-H3Xa3gyg,1065
2
+ mkdocstrings_handlers/asp/__init__.py,sha256=3R9Ma3P3-t8LJtHm1RCoEmOhpnORGVOSx4FXPx2ZPs8,150
3
+ mkdocstrings_handlers/asp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ mkdocstrings_handlers/asp/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ mkdocstrings_handlers/asp/_internal/config.py,sha256=WdU-m_3PR5Et8ESJr2hWRa4de6_WoptGOGemRs7mreI,4211
6
+ mkdocstrings_handlers/asp/_internal/domain.py,sha256=pjtl7djVbs7ID4GVzVFcHLAPLzkpPbbJLUKYrWOfw3Y,3923
7
+ mkdocstrings_handlers/asp/_internal/error.py,sha256=1KeOtM31AKvCELVi4kcqnXQjIhy1hP_7XfeSaUbH4Wc,169
8
+ mkdocstrings_handlers/asp/_internal/handler.py,sha256=TYMPVJtQ5gGVobFDxsIhfJLul8UWEnta1PcEy-bep7A,5299
9
+ mkdocstrings_handlers/asp/_internal/collect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ mkdocstrings_handlers/asp/_internal/collect/debug.py,sha256=zUPYCq-h-iW7_tCuDv-zdBzvAYqdW6FjnqIbMg3WJyw,595
11
+ mkdocstrings_handlers/asp/_internal/collect/extractors.py,sha256=2klxVlYnV-fhKWt2Zg2Wg7EljWdNGWbKDE_Pit6Ogp8,9052
12
+ mkdocstrings_handlers/asp/_internal/collect/load.py,sha256=LQakM5oVIcELcy3-w6tQSJTAxeh36p12ZnxD9mo5bZY,3270
13
+ mkdocstrings_handlers/asp/_internal/collect/syntax.py,sha256=8oH7BOc_Yd342QD_SindIWyJu0HGUI79OqNmk38VHGc,2747
14
+ mkdocstrings_handlers/asp/_internal/collect/queries/body.scm,sha256=yfkZakz32nkbmSZJD6udhe2FrtaVrZ27H778dbhcod4,305
15
+ mkdocstrings_handlers/asp/_internal/collect/queries/documentation_argument.scm,sha256=WdgpiFuSRWdTqVJChC_sgZEM8o_zBox3p71ojkCqsQs,67
16
+ mkdocstrings_handlers/asp/_internal/collect/queries/documentation_predicate.scm,sha256=VbkRK8PxkeXArtu8LngZU9M-mhmibzNgJ5TkL2tfsGM,223
17
+ mkdocstrings_handlers/asp/_internal/collect/queries/head.scm,sha256=_fX566sB8zS1QZqQijD1SZlRQpjZwhxXiOLFT4NiNtg,1105
18
+ mkdocstrings_handlers/asp/_internal/collect/queries/predicate.scm,sha256=Ga9Tm7zM4ti-W3mbD9raYP6jF7eFYjmGYibZKo5Cww0,692
19
+ mkdocstrings_handlers/asp/_internal/collect/queries/show.scm,sha256=lhQ8pez9EKIl8cvXDvLGO7FGQSdESQkfyBZ2YstmQ60,199
20
+ mkdocstrings_handlers/asp/_internal/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ mkdocstrings_handlers/asp/_internal/render/dependency_graph_context.py,sha256=QnJ50hpkfRP--T-zVHN2jI7RGJmJ7_DXmuY5G6UUMJ0,2232
22
+ mkdocstrings_handlers/asp/_internal/render/encodings_context.py,sha256=ystrk5ravvGrXNI2hSFHagilEuiuVgLAsANLtvtDQOo,4358
23
+ mkdocstrings_handlers/asp/_internal/render/glossary_context.py,sha256=UxqDaKG-SUK2jSWKeHlZq5RxGUizNKY4cWODz0yJ-vE,4853
24
+ mkdocstrings_handlers/asp/_internal/render/predicate_info.py,sha256=dvXGS4lnexUXkVVGIvWhhCkyYlPtHS6h27hPVFUcbSk,7110
25
+ mkdocstrings_handlers/asp/_internal/render/predicate_table_context.py,sha256=I6wl1YejWEce1tEV7cgtLq_TYP8b2v7nTYpjL0LSFsk,1793
26
+ mkdocstrings_handlers/asp/_internal/render/render_context.py,sha256=uhoe1tHCpyRd3oB6_SDkV8qHyw71eeaLpIinB_kNUR8,2654
27
+ mkdocstrings_handlers/asp/templates/material/dependency_graph.html.jinja,sha256=PXLhW2zlYMFsQSzZF476d0wEV15kjYTJgUCo4bIr_6g,2913
28
+ mkdocstrings_handlers/asp/templates/material/documentation.html.jinja,sha256=B4tepM5f0HjzUQxPLK-HwCKSV1Qye5Lwy0qXkZvQofQ,439
29
+ mkdocstrings_handlers/asp/templates/material/encodings.html.jinja,sha256=rfZQXgMyqcy_rLMDr2zE2Tjb25QRVuF1KDnQRXMo4j0,3838
30
+ mkdocstrings_handlers/asp/templates/material/glossary.html.jinja,sha256=OHXeoXMpgnXrLhRI0JUw5pWTxBE04b9xYBDv8b_rOOM,2649
31
+ mkdocstrings_handlers/asp/templates/material/glossary_references.html.jinja,sha256=3YTnNMAQGm66YK89F8oefEMHDppmxnXql0yp8m5I4ZQ,1918
32
+ mkdocstrings_handlers/asp/templates/material/icons.html.jinja,sha256=nCi2uDpWEnCXbTa6csULENr73bkLVQokXCT_m0XF6Fg,1993
33
+ mkdocstrings_handlers/asp/templates/material/predicate_table.html.jinja,sha256=BxxhTEUs7QE8J2HlHIbs-eXv9L10_srAyQER0O1kmHg,1562
34
+ mkdocstrings_handlers/asp/templates/material/separator.html.jinja,sha256=oX1ZUWDBCFcR2uGLrcR_fZYwSFD6qiwIeigq0KZ7PEA,61
35
+ mkdocstrings_handlers/asp/templates/material/source.html.jinja,sha256=cs7vXtFhJ2_WrxljVKLE408MtV5bFUkFC2h2zVMErS0,159
36
+ mkdocstrings_handlers/asp/templates/material/style.css,sha256=TD6mkJY7jxxyEdjA3-FZaTwXYFdgqhiavui7vltIa_M,2778
37
+ clindocs-1.5.2.dist-info/METADATA,sha256=x94Fs6M3QFbBoK9G29rMK2_4NfYtVL9hpvECXHnr5-Q,3580
38
+ clindocs-1.5.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
39
+ clindocs-1.5.2.dist-info/entry_points.txt,sha256=r4jCxLx7C7iZyzmXNS5d_XnMEeRjUPXwqgRfgVnRtqA,88
40
+ clindocs-1.5.2.dist-info/top_level.txt,sha256=IP9FE6TMun3TXULzTNhW_Y0Tlj9Dh3J4k1QHrzs9NnY,22
41
+ clindocs-1.5.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [mkdocstrings.handlers]
2
+ clingo = mkdocstrings_handlers.asp._internal.handler:ASPHandler
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Potassco
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ mkdocstrings_handlers
@@ -0,0 +1,5 @@
1
+ """Entry point of the mkdocstrings handler module."""
2
+
3
+ from mkdocstrings_handlers.asp._internal.handler import get_handler
4
+
5
+ __all__ = ["get_handler"]
File without changes
@@ -0,0 +1,19 @@
1
+ """Utility functions for debugging tree-sitter trees."""
2
+
3
+ from tree_sitter import Node
4
+
5
+
6
+ def print_tree(node: Node, source: bytes, depth: int) -> None:
7
+ """Recursively print the tree structure of a node.
8
+ Args:
9
+ node: The node to print.
10
+ source: The source code from which the node was created.
11
+ depth: The current depth in the tree.
12
+ """
13
+
14
+ node_text = node.text.decode("utf-8") if node.text is not None else ""
15
+
16
+ print(f"{' ' * depth}{node.grammar_name} {node.id}: {node_text}")
17
+
18
+ for child in node.children:
19
+ print_tree(child, source, depth + 1)
@@ -0,0 +1,317 @@
1
+ """Extractors for various ASP constructs from Tree-sitter nodes."""
2
+
3
+ from collections import defaultdict
4
+ from pathlib import Path
5
+
6
+ from tree_sitter import Node
7
+
8
+ from mkdocstrings_handlers.asp._internal.collect.syntax import Queries
9
+ from mkdocstrings_handlers.asp._internal.domain import (
10
+ ArgumentDocumentation,
11
+ BlockComment,
12
+ Include,
13
+ LineComment,
14
+ Predicate,
15
+ PredicateDocumentation,
16
+ Show,
17
+ ShowStatus,
18
+ Statement,
19
+ )
20
+ from mkdocstrings_handlers.asp._internal.error import ExtractionError
21
+
22
+
23
+ def get_node_text(node: Node | None) -> str:
24
+ """
25
+ Safely extracts and decodes text from a Tree-sitter node.
26
+
27
+ Args:
28
+ node: The Tree-sitter node.
29
+
30
+ Returns:
31
+ The decoded text content of the node.
32
+
33
+ Raises:
34
+ ExtractionError: If the node is None or has no text.
35
+ """
36
+ if node is None:
37
+ raise ExtractionError("Expected a node, but got None.")
38
+
39
+ if node.text is None:
40
+ # This usually happens with 'missing' nodes in Tree-sitter (syntax errors)
41
+ raise ExtractionError(f"Node {node.type} exists but has no text content.")
42
+
43
+ return node.text.decode("utf-8")
44
+
45
+
46
+ def get_capture_text(captures: dict[str, list[Node]], key: str, index: int = 0) -> str:
47
+ """
48
+ Safely retrieves the text from a capture group.
49
+
50
+ Args:
51
+ captures: The dictionary of captured nodes.
52
+ key: The capture group name (e.g., "identifier").
53
+ index: Which item in the capture list to retrieve (default 0).
54
+
55
+ Returns:
56
+ The decoded text content of the specified capture.
57
+
58
+ Raises:
59
+ ExtractionError: If the capture group is missing, empty, or the index is out of
60
+ bounds.
61
+ """
62
+ nodes = captures.get(key)
63
+
64
+ if not nodes:
65
+ raise ExtractionError(f"Required capture group '{key}' is missing or empty.")
66
+
67
+ if index >= len(nodes):
68
+ raise ExtractionError(f"Capture group '{key}' does not have an element at index {index}.")
69
+
70
+ return get_node_text(nodes[index])
71
+
72
+
73
+ def extract_include(node: Node, parent_file_path: Path) -> Include:
74
+ """
75
+ Extract an Include from a node.
76
+
77
+ Args:
78
+ node: The node representing the include.
79
+ base_path: The base path of the current file.
80
+
81
+ Returns:
82
+ The created Include.
83
+ """
84
+ # If the node is an include,
85
+ # then the first child is the include directive
86
+ # and the second child is the file path.
87
+
88
+ # The second child of the file path is the file path
89
+ # as a string fragment without the quotes.
90
+ file_path_node = node.children[1]
91
+ file_path = Path(get_node_text(file_path_node.children[1]))
92
+ resolved_path = parent_file_path.parent / file_path
93
+
94
+ return Include(row=node.start_point.row, content=get_node_text(node), path=resolved_path)
95
+
96
+
97
+ def extract_predicates(node: Node) -> list[Predicate]:
98
+ """
99
+ Extract a Predicate from a node.
100
+
101
+ Args:
102
+ node: A `literal` node representing the predicate.
103
+
104
+ Returns:
105
+ The created Predicate.
106
+ """
107
+ captures = Queries.PREDICATE.captures(node)
108
+
109
+ identifier = get_node_text(captures["identifier"][0])
110
+ is_negated = len(captures.get("negation", [])) > 0
111
+
112
+ if "term_group" not in captures:
113
+ return [Predicate(identifier=identifier, arity=0, negation=is_negated)]
114
+
115
+ predicates = []
116
+ for group_node in captures["term_group"]:
117
+ arity = group_node.named_child_count
118
+
119
+ predicates.append(Predicate(identifier=identifier, arity=arity, negation=is_negated))
120
+
121
+ return predicates
122
+
123
+
124
+ def extract_show(node: Node) -> Show:
125
+ """
126
+ Extract a Show directive from a node.
127
+
128
+ Args:
129
+ node: A `show_signature` or `show_term` node representing the show directive.
130
+
131
+ Returns:
132
+ The created Show directive.
133
+ """
134
+ captures = Queries.SHOW.captures(node)
135
+
136
+ raw_identifier = captures.get("identifier", [])
137
+ raw_arity = captures.get("arity", [])
138
+ raw_terms = captures.get("term", [])
139
+
140
+ identifier: str | None = get_node_text(raw_identifier[0]) if raw_identifier else None
141
+ arity: int | None = int(get_node_text(raw_arity[0])) if raw_arity else None
142
+ predicate: Predicate | None = None
143
+ status = ShowStatus.EXPLICIT
144
+
145
+ if raw_terms:
146
+ status = ShowStatus.PARTIAL
147
+ arity = len(raw_terms)
148
+
149
+ if identifier is not None and arity is not None:
150
+ predicate = Predicate(
151
+ identifier=identifier,
152
+ arity=arity,
153
+ )
154
+
155
+ return Show(
156
+ row=node.start_point.row,
157
+ content=get_node_text(node),
158
+ predicate=predicate,
159
+ status=status,
160
+ )
161
+
162
+
163
+ def extract_line_comment(node: Node) -> LineComment:
164
+ """
165
+ Extract a LineComment from a node.
166
+
167
+ Args:
168
+ node: A `line_comment` node representing the line comment.
169
+
170
+ Returns:
171
+ The created LineComment.
172
+ """
173
+ return LineComment(
174
+ row=node.start_point.row,
175
+ content=get_node_text(node).removeprefix("%"),
176
+ )
177
+
178
+
179
+ def extract_block_comment(node: Node) -> BlockComment:
180
+ """
181
+ Extract a BlockComment from a node.
182
+
183
+ Args:
184
+ node: A `block_comment` node representing the block comment.
185
+
186
+ Returns:
187
+ The created BlockComment.
188
+ """
189
+ return BlockComment(
190
+ row=node.start_point.row,
191
+ content=get_node_text(node).removeprefix("%*").removesuffix("*%"),
192
+ )
193
+
194
+
195
+ def extract_bare_statement(node: Node) -> Statement:
196
+ """
197
+ Extract a Statement without predicate tracking.
198
+
199
+ Args:
200
+ node: A node representing the statement.
201
+
202
+ Returns:
203
+ The created Statement.
204
+ """
205
+ return Statement(
206
+ row=node.start_point.row,
207
+ content=get_node_text(node),
208
+ provided_predicates=[],
209
+ needed_predicates=[],
210
+ )
211
+
212
+
213
+ def extract_statement(node: Node) -> Statement:
214
+ """
215
+ Extract a Statement from a node.
216
+
217
+ Args:
218
+ node: A node representing the statement.
219
+
220
+ Returns:
221
+ The created Statement.
222
+ """
223
+ head_node = node.child_by_field_name("head")
224
+ body_node = node.child_by_field_name("body")
225
+
226
+ captures = defaultdict(list)
227
+
228
+ if head_node:
229
+ # We don't use the head_node here
230
+ # because `head` is a supertype in the current grammar
231
+ # which leads to query difficulties with literals
232
+ head_captures = Queries.HEAD.captures(node)
233
+ for key, nodes in head_captures.items():
234
+ captures[key].extend(nodes)
235
+
236
+ if body_node:
237
+ body_captures = Queries.BODY.captures(body_node)
238
+ for key, nodes in body_captures.items():
239
+ captures[key].extend(nodes)
240
+
241
+ provided_predicates = [pred for node in captures.get("provided", []) for pred in extract_predicates(node)]
242
+ needed_predicates = [pred for node in captures.get("needed", []) for pred in extract_predicates(node)]
243
+
244
+ return Statement(
245
+ row=node.start_point.row,
246
+ content=get_node_text(node),
247
+ provided_predicates=provided_predicates,
248
+ needed_predicates=needed_predicates,
249
+ )
250
+
251
+
252
+ def extract_argument_documentation(node: Node) -> ArgumentDocumentation:
253
+ """
254
+ Extract an ArgumentDocumentation from a node.
255
+
256
+ Args:
257
+ node: The node representing the argument documentation.
258
+
259
+ Returns:
260
+ The created ArgumentDocumentation.
261
+ """
262
+ captures = Queries.DOC_ARGUMENT.captures(node)
263
+
264
+ identifier = get_capture_text(captures, "identifier", 0)
265
+ description = get_node_text(captures["description"][0]).strip() if captures.get("description") else ""
266
+
267
+ return ArgumentDocumentation(
268
+ identifier=identifier,
269
+ description=description,
270
+ )
271
+
272
+
273
+ def extract_predicate_documentation(node: Node) -> PredicateDocumentation:
274
+ """
275
+ Extract a PredicateDocumentation from a node.
276
+
277
+ Args:
278
+ node: The node representing the predicate documentation.
279
+
280
+ Returns:
281
+ The created PredicateDocumentation.
282
+ """
283
+ captures = Queries.DOC_PREDICATE.captures(node)
284
+
285
+ identifier = get_capture_text(captures, "identifier", 0)
286
+
287
+ # For some reason the query does not return the arguments
288
+ # in the order they appear. So we order them by their start byte.
289
+ argument_nodes = captures.get("argument", [])
290
+ argument_nodes.sort(key=lambda n: n.start_byte)
291
+ arguments = [get_node_text(arg) for arg in argument_nodes]
292
+
293
+ explicit_docs_map = {}
294
+ for arg_node in captures.get("arg.documentation", []):
295
+ doc = extract_argument_documentation(arg_node)
296
+ explicit_docs_map[doc.identifier] = doc
297
+
298
+ final_arguments = []
299
+ for name in arguments:
300
+ if name in explicit_docs_map:
301
+ final_arguments.append(explicit_docs_map[name])
302
+ else:
303
+ final_arguments.append(ArgumentDocumentation(identifier=name, description=""))
304
+
305
+ description = (
306
+ get_node_text(captures["description"][0]).removeprefix("%*!").removesuffix("*%").strip()
307
+ if captures.get("description")
308
+ else ""
309
+ )
310
+
311
+ return PredicateDocumentation(
312
+ row=node.start_point.row,
313
+ content=get_node_text(node),
314
+ signature=f"{identifier}/{len(arguments)}",
315
+ description=description,
316
+ arguments=final_arguments,
317
+ )
@@ -0,0 +1,92 @@
1
+ """This module handles loading and parsing ASP documents."""
2
+
3
+ import logging
4
+ from collections import deque
5
+ from pathlib import Path
6
+
7
+ from mkdocstrings_handlers.asp._internal.collect.extractors import (
8
+ extract_bare_statement,
9
+ extract_block_comment,
10
+ extract_include,
11
+ extract_line_comment,
12
+ extract_predicate_documentation,
13
+ extract_show,
14
+ extract_statement,
15
+ )
16
+ from mkdocstrings_handlers.asp._internal.collect.syntax import NodeKind, get_parser
17
+ from mkdocstrings_handlers.asp._internal.domain import Document, ShowStatus
18
+
19
+ log = logging.getLogger(__name__)
20
+
21
+
22
+ def load_documents(paths: list[Path]) -> list[Document]:
23
+ """
24
+ Load and parse multiple ASP documents from the given file paths.
25
+
26
+ Args:
27
+ paths: List of paths to ASP files.
28
+
29
+ Returns:
30
+ List of parsed Document objects.
31
+ """
32
+ parse_queue = deque(paths)
33
+ documents: dict[Path, Document] = {}
34
+ while parse_queue:
35
+ path = parse_queue.popleft()
36
+ if path.suffix != ".lp" or not path.is_file():
37
+ log.warning("skip file %s, not a valid ASP file.", path)
38
+ continue
39
+ document = load_document(path)
40
+ documents[path] = document
41
+ parse_queue.extend(include.path for include in document.includes if include.path not in documents)
42
+
43
+ return list(documents.values())
44
+
45
+
46
+ def load_document(file_path: Path) -> Document:
47
+ """
48
+ Load and parse an ASP document from the given file path.
49
+
50
+ Args:
51
+ file_path: Path to the ASP file.
52
+
53
+ Returns:
54
+ The parsed Document object.
55
+ """
56
+ with open(file_path, "rb") as f:
57
+ source_bytes = f.read()
58
+
59
+ document = Document(path=file_path, content=source_bytes.decode("utf-8"))
60
+ tree = get_parser().parse(source_bytes)
61
+
62
+ for node in tree.root_node.children:
63
+ match NodeKind.from_grammar_name(node.grammar_name):
64
+ case NodeKind.RULE | NodeKind.INTEGRITY_CONSTRAINT:
65
+ statement = extract_statement(node)
66
+ document.statements.append(statement)
67
+ case NodeKind.LINE_COMMENT:
68
+ line_comment = extract_line_comment(node)
69
+ document.line_comments.append(line_comment)
70
+ case NodeKind.BLOCK_COMMENT:
71
+ block_comment = extract_block_comment(node)
72
+ document.block_comments.append(block_comment)
73
+ case NodeKind.INCLUDE:
74
+ include = extract_include(node, file_path)
75
+ document.includes.append(include)
76
+ case NodeKind.SHOW | NodeKind.SHOW_SIGNATURE | NodeKind.SHOW_TERM:
77
+ show = extract_show(node)
78
+ statement = extract_statement(node)
79
+
80
+ if show.predicate is not None and show.status == ShowStatus.PARTIAL:
81
+ statement.provided_predicates.append(show.predicate)
82
+
83
+ document.shows.append(show)
84
+ document.statements.append(statement)
85
+ case NodeKind.DOC_COMMENT:
86
+ predicate_documentation = extract_predicate_documentation(node)
87
+ document.predicate_documentations.append(predicate_documentation)
88
+ case _:
89
+ statement = extract_bare_statement(node)
90
+ document.statements.append(statement)
91
+
92
+ return document
@@ -0,0 +1,11 @@
1
+ ; -----------------------------------------------------------------------------
2
+ ; This gathers needed predicates in the body of a statement
3
+ ; -----------------------------------------------------------------------------
4
+
5
+ (body_literal
6
+ (symbolic_atom)
7
+ ) @needed
8
+
9
+ (literal
10
+ (symbolic_atom)
11
+ ) @needed
@@ -0,0 +1,4 @@
1
+ (doc_arg
2
+ (variable) @identifier
3
+ (doc_desc)? @description
4
+ )
@@ -0,0 +1,12 @@
1
+ (doc_comment
2
+ (doc_predicate
3
+ (identifier) @identifier
4
+ (variables
5
+ (variable) @argument
6
+ )?
7
+ )
8
+ (doc_desc)? @description
9
+ (doc_args
10
+ (doc_arg) @arg.documentation
11
+ )?
12
+ )