mapFolding 0.9.1__py3-none-any.whl → 0.9.3__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.
- mapFolding/Z0Z_flowControl.py +99 -0
- mapFolding/__init__.py +63 -60
- mapFolding/basecamp.py +40 -35
- mapFolding/beDRY.py +72 -66
- mapFolding/dataBaskets.py +49 -0
- mapFolding/datatypes.py +21 -0
- mapFolding/oeis.py +57 -75
- mapFolding/reference/__init__.py +2 -2
- mapFolding/someAssemblyRequired/__init__.py +6 -4
- mapFolding/someAssemblyRequired/_theTypes.py +9 -1
- mapFolding/someAssemblyRequired/_tool_Make.py +0 -1
- mapFolding/someAssemblyRequired/_tool_Then.py +16 -8
- mapFolding/someAssemblyRequired/_toolboxAntecedents.py +111 -35
- mapFolding/someAssemblyRequired/_toolboxContainers.py +58 -49
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +42 -42
- mapFolding/someAssemblyRequired/toolboxNumba.py +3 -11
- mapFolding/someAssemblyRequired/transformationTools.py +94 -70
- mapFolding/syntheticModules/numbaCount.py +9 -11
- mapFolding/theDao.py +19 -21
- mapFolding/theDaoOfMapFolding.py +142 -0
- mapFolding/theSSOT.py +36 -58
- mapFolding/toolboxFilesystem.py +29 -38
- {mapfolding-0.9.1.dist-info → mapfolding-0.9.3.dist-info}/METADATA +4 -3
- mapfolding-0.9.3.dist-info/RECORD +51 -0
- {mapfolding-0.9.1.dist-info → mapfolding-0.9.3.dist-info}/WHEEL +1 -1
- tests/__init__.py +2 -2
- tests/conftest.py +7 -7
- tests/test_computations.py +15 -13
- tests/test_other.py +0 -7
- tests/test_tasks.py +2 -2
- mapfolding-0.9.1.dist-info/RECORD +0 -47
- /mapFolding/reference/{lunnanNumpy.py → lunnonNumpy.py} +0 -0
- /mapFolding/reference/{lunnanWhile.py → lunnonWhile.py} +0 -0
- {mapfolding-0.9.1.dist-info → mapfolding-0.9.3.dist-info}/entry_points.txt +0 -0
- {mapfolding-0.9.1.dist-info → mapfolding-0.9.3.dist-info}/licenses/LICENSE +0 -0
- {mapfolding-0.9.1.dist-info → mapfolding-0.9.3.dist-info}/top_level.txt +0 -0
|
@@ -2,17 +2,22 @@
|
|
|
2
2
|
AST Node Predicate and Access Utilities for Pattern Matching and Traversal
|
|
3
3
|
|
|
4
4
|
This module provides utilities for accessing and matching AST nodes in a consistent way.
|
|
5
|
-
It contains
|
|
5
|
+
It contains three primary classes:
|
|
6
6
|
|
|
7
7
|
1. DOT: Provides consistent accessor methods for AST node attributes across different
|
|
8
8
|
node types, simplifying the access to node properties.
|
|
9
9
|
|
|
10
|
-
2.
|
|
10
|
+
2. be: Offers type-guard functions that verify AST node types, enabling safe type
|
|
11
|
+
narrowing for static type checking and improving code safety.
|
|
12
|
+
|
|
13
|
+
3. ifThis: Contains predicate functions for matching AST nodes based on various criteria,
|
|
11
14
|
enabling precise targeting of nodes for analysis or transformation.
|
|
12
15
|
|
|
13
16
|
These utilities form the foundation of the pattern-matching component in the AST
|
|
14
17
|
manipulation framework, working in conjunction with the NodeChanger and NodeTourist
|
|
15
|
-
classes to enable precise and targeted code transformations.
|
|
18
|
+
classes to enable precise and targeted code transformations. Together, they implement
|
|
19
|
+
a declarative approach to AST manipulation that separates node identification (ifThis),
|
|
20
|
+
type verification (be), and data access (DOT).
|
|
16
21
|
"""
|
|
17
22
|
|
|
18
23
|
from collections.abc import Callable
|
|
@@ -26,6 +31,7 @@ from mapFolding.someAssemblyRequired import (
|
|
|
26
31
|
astClassHasDOTvalue_expr,
|
|
27
32
|
astClassOptionallyHasDOTnameNotName,
|
|
28
33
|
astClassHasDOTvalue_exprNone,
|
|
34
|
+
ImaCallToName,
|
|
29
35
|
)
|
|
30
36
|
from typing import Any, overload, TypeGuard
|
|
31
37
|
import ast
|
|
@@ -65,9 +71,17 @@ class DOT:
|
|
|
65
71
|
@staticmethod
|
|
66
72
|
def attr(node: ast.Attribute) -> ast_Identifier:
|
|
67
73
|
return node.attr
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
@overload
|
|
77
|
+
def func(node: ImaCallToName) -> ast.Name:...
|
|
68
78
|
@staticmethod
|
|
69
|
-
|
|
79
|
+
@overload
|
|
80
|
+
def func(node: ast.Call) -> ast.expr:...
|
|
81
|
+
@staticmethod
|
|
82
|
+
def func(node: ast.Call | ImaCallToName) -> ast.expr | ast.Name:
|
|
70
83
|
return node.func
|
|
84
|
+
|
|
71
85
|
@staticmethod
|
|
72
86
|
def id(node: ast.Name) -> ast_Identifier:
|
|
73
87
|
return node.id
|
|
@@ -111,6 +125,75 @@ class DOT:
|
|
|
111
125
|
def value(node: astClassHasDOTvalue) -> Any | ast.expr | bool | None:
|
|
112
126
|
return node.value
|
|
113
127
|
|
|
128
|
+
class be:
|
|
129
|
+
"""
|
|
130
|
+
Provide type-guard functions for safely verifying AST node types during manipulation.
|
|
131
|
+
|
|
132
|
+
The be class contains static methods that perform runtime type verification of AST nodes,
|
|
133
|
+
returning TypeGuard results that enable static type checkers to narrow node types in
|
|
134
|
+
conditional branches. These type-guards:
|
|
135
|
+
|
|
136
|
+
1. Improve code safety by preventing operations on incompatible node types
|
|
137
|
+
2. Enable IDE tooling to provide better autocompletion and error detection
|
|
138
|
+
3. Document expected node types in a way that's enforced by the type system
|
|
139
|
+
4. Support pattern-matching workflows where node types must be verified before access
|
|
140
|
+
|
|
141
|
+
When used with conditional statements, these type-guards allow for precise,
|
|
142
|
+
type-safe manipulation of AST nodes while maintaining full static type checking
|
|
143
|
+
capabilities, even in complex transformation scenarios.
|
|
144
|
+
"""
|
|
145
|
+
@staticmethod
|
|
146
|
+
def AnnAssign(node: ast.AST) -> TypeGuard[ast.AnnAssign]:
|
|
147
|
+
return isinstance(node, ast.AnnAssign)
|
|
148
|
+
|
|
149
|
+
@staticmethod
|
|
150
|
+
def arg(node: ast.AST) -> TypeGuard[ast.arg]:
|
|
151
|
+
return isinstance(node, ast.arg)
|
|
152
|
+
|
|
153
|
+
@staticmethod
|
|
154
|
+
def Assign(node: ast.AST) -> TypeGuard[ast.Assign]:
|
|
155
|
+
return isinstance(node, ast.Assign)
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def Attribute(node: ast.AST) -> TypeGuard[ast.Attribute]:
|
|
159
|
+
return isinstance(node, ast.Attribute)
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def AugAssign(node: ast.AST) -> TypeGuard[ast.AugAssign]:
|
|
163
|
+
return isinstance(node, ast.AugAssign)
|
|
164
|
+
|
|
165
|
+
@staticmethod
|
|
166
|
+
def Call(node: ast.AST) -> TypeGuard[ast.Call]:
|
|
167
|
+
return isinstance(node, ast.Call)
|
|
168
|
+
|
|
169
|
+
@staticmethod
|
|
170
|
+
def ClassDef(node: ast.AST) -> TypeGuard[ast.ClassDef]:
|
|
171
|
+
return isinstance(node, ast.ClassDef)
|
|
172
|
+
|
|
173
|
+
@staticmethod
|
|
174
|
+
def FunctionDef(node: ast.AST) -> TypeGuard[ast.FunctionDef]:
|
|
175
|
+
return isinstance(node, ast.FunctionDef)
|
|
176
|
+
|
|
177
|
+
@staticmethod
|
|
178
|
+
def keyword(node: ast.AST) -> TypeGuard[ast.keyword]:
|
|
179
|
+
return isinstance(node, ast.keyword)
|
|
180
|
+
|
|
181
|
+
@staticmethod
|
|
182
|
+
def Name(node: ast.AST) -> TypeGuard[ast.Name]:
|
|
183
|
+
return isinstance(node, ast.Name)
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def Return(node: ast.AST) -> TypeGuard[ast.Return]:
|
|
187
|
+
return isinstance(node, ast.Return)
|
|
188
|
+
|
|
189
|
+
@staticmethod
|
|
190
|
+
def Starred(node: ast.AST) -> TypeGuard[ast.Starred]:
|
|
191
|
+
return isinstance(node, ast.Starred)
|
|
192
|
+
|
|
193
|
+
@staticmethod
|
|
194
|
+
def Subscript(node: ast.AST) -> TypeGuard[ast.Subscript]:
|
|
195
|
+
return isinstance(node, ast.Subscript)
|
|
196
|
+
|
|
114
197
|
class ifThis:
|
|
115
198
|
"""
|
|
116
199
|
Provide predicate functions for matching and filtering AST nodes based on various criteria.
|
|
@@ -135,41 +218,41 @@ class ifThis:
|
|
|
135
218
|
@staticmethod
|
|
136
219
|
def is_arg_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.arg] | bool]:
|
|
137
220
|
"""see also `isArgument_Identifier`"""
|
|
138
|
-
return lambda node:
|
|
221
|
+
return lambda node: be.arg(node) and ifThis._Identifier(identifier)(DOT.arg(node))
|
|
139
222
|
@staticmethod
|
|
140
223
|
def is_keyword_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.keyword] | bool]:
|
|
141
224
|
"""see also `isArgument_Identifier`"""
|
|
142
|
-
return lambda node:
|
|
225
|
+
return lambda node: be.keyword(node) and ifThis._Identifier(identifier)(DOT.arg(node))
|
|
143
226
|
|
|
144
227
|
@staticmethod
|
|
145
228
|
def isAnnAssign_targetIs(targetPredicate: Callable[[ast.expr], TypeGuard[ast.expr] | bool]) -> Callable[[ast.AST], TypeGuard[ast.AnnAssign] | bool]:
|
|
146
229
|
def workhorse(node: ast.AST) -> TypeGuard[ast.AnnAssign] | bool:
|
|
147
|
-
return
|
|
230
|
+
return be.AnnAssign(node) and targetPredicate(DOT.target(node))
|
|
148
231
|
return workhorse
|
|
149
232
|
|
|
150
233
|
@staticmethod
|
|
151
234
|
def isArgument_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.arg | ast.keyword] | bool]:
|
|
152
|
-
return lambda node: (
|
|
235
|
+
return lambda node: (be.arg(node) or be.keyword(node)) and ifThis._Identifier(identifier)(DOT.arg(node))
|
|
153
236
|
|
|
154
237
|
@staticmethod
|
|
155
238
|
def isAssignAndTargets0Is(targets0Predicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[ast.AnnAssign] | bool]:
|
|
156
239
|
"""node is Assign and node.targets[0] matches `targets0Predicate`."""
|
|
157
|
-
return lambda node:
|
|
240
|
+
return lambda node: be.Assign(node) and targets0Predicate(node.targets[0])
|
|
158
241
|
@staticmethod
|
|
159
242
|
def isAssignAndValueIs(valuePredicate: Callable[[ast.AST], bool]) -> Callable[[ast.AST], TypeGuard[ast.Assign] | bool]:
|
|
160
243
|
"""node is ast.Assign and node.value matches `valuePredicate`. """
|
|
161
|
-
return lambda node:
|
|
244
|
+
return lambda node: be.Assign(node) and valuePredicate(DOT.value(node))
|
|
162
245
|
|
|
163
246
|
@staticmethod
|
|
164
247
|
def isAttribute_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Attribute] | bool]:
|
|
165
248
|
"""node is `ast.Attribute` and the top-level `ast.Name` is `identifier`"""
|
|
166
249
|
def workhorse(node: ast.AST) -> TypeGuard[ast.Attribute]:
|
|
167
|
-
return
|
|
250
|
+
return be.Attribute(node) and ifThis._nested_Identifier(identifier)(DOT.value(node))
|
|
168
251
|
return workhorse
|
|
169
252
|
@staticmethod
|
|
170
253
|
def isAttributeName(node: ast.AST) -> TypeGuard[ast.Attribute]:
|
|
171
254
|
""" Displayed as Name.attribute."""
|
|
172
|
-
return
|
|
255
|
+
return be.Attribute(node) and be.Name(DOT.value(node))
|
|
173
256
|
@staticmethod
|
|
174
257
|
def isAttributeNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Attribute] | bool]:
|
|
175
258
|
return lambda node: ifThis.isAttributeName(node) and ifThis.isName_Identifier(namespace)(DOT.value(node)) and ifThis._Identifier(identifier)(DOT.attr(node))
|
|
@@ -177,42 +260,47 @@ class ifThis:
|
|
|
177
260
|
@staticmethod
|
|
178
261
|
def isAugAssign_targetIs(targetPredicate: Callable[[ast.expr], TypeGuard[ast.expr] | bool]) -> Callable[[ast.AST], TypeGuard[ast.AugAssign] | bool]:
|
|
179
262
|
def workhorse(node: ast.AST) -> TypeGuard[ast.AugAssign] | bool:
|
|
180
|
-
return
|
|
263
|
+
return be.AugAssign(node) and targetPredicate(DOT.target(node))
|
|
181
264
|
return workhorse
|
|
182
265
|
|
|
183
266
|
@staticmethod
|
|
184
|
-
def isCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[
|
|
185
|
-
|
|
267
|
+
def isCall_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ImaCallToName] | bool]:
|
|
268
|
+
def workhorse(node: ast.AST) -> TypeGuard[ImaCallToName] | bool:
|
|
269
|
+
return ifThis.isCallToName(node) and ifThis._Identifier(identifier)(DOT.id(DOT.func(node)))
|
|
270
|
+
return workhorse
|
|
271
|
+
|
|
186
272
|
@staticmethod
|
|
187
273
|
def isCallAttributeNamespace_Identifier(namespace: ast_Identifier, identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Call] | bool]:
|
|
188
|
-
|
|
274
|
+
def workhorse(node: ast.AST) -> TypeGuard[ast.Call] | bool:
|
|
275
|
+
return be.Call(node) and ifThis.isAttributeNamespace_Identifier(namespace, identifier)(DOT.func(node))
|
|
276
|
+
return workhorse
|
|
189
277
|
@staticmethod
|
|
190
|
-
def isCallToName(node: ast.AST) -> TypeGuard[
|
|
191
|
-
return
|
|
278
|
+
def isCallToName(node: ast.AST) -> TypeGuard[ImaCallToName]:
|
|
279
|
+
return be.Call(node) and be.Name(DOT.func(node))
|
|
192
280
|
|
|
193
281
|
@staticmethod
|
|
194
282
|
def isClassDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.ClassDef] | bool]:
|
|
195
|
-
return lambda node:
|
|
283
|
+
return lambda node: be.ClassDef(node) and ifThis._Identifier(identifier)(DOT.name(node))
|
|
196
284
|
|
|
197
285
|
@staticmethod
|
|
198
286
|
def isFunctionDef_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.FunctionDef] | bool]:
|
|
199
|
-
return lambda node:
|
|
287
|
+
return lambda node: be.FunctionDef(node) and ifThis._Identifier(identifier)(DOT.name(node))
|
|
200
288
|
|
|
201
289
|
@staticmethod
|
|
202
290
|
def isName_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Name] | bool]:
|
|
203
|
-
return lambda node:
|
|
291
|
+
return lambda node: be.Name(node) and ifThis._Identifier(identifier)(DOT.id(node))
|
|
204
292
|
|
|
205
293
|
@staticmethod
|
|
206
294
|
def isStarred_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Starred] | bool]:
|
|
207
295
|
"""node is `ast.Starred` and the top-level `ast.Name` is `identifier`"""
|
|
208
296
|
def workhorse(node: ast.AST) -> TypeGuard[ast.Starred]:
|
|
209
|
-
return
|
|
297
|
+
return be.Starred(node) and ifThis._nested_Identifier(identifier)(DOT.value(node))
|
|
210
298
|
return workhorse
|
|
211
299
|
@staticmethod
|
|
212
300
|
def isSubscript_Identifier(identifier: ast_Identifier) -> Callable[[ast.AST], TypeGuard[ast.Subscript] | bool]:
|
|
213
301
|
"""node is `ast.Subscript` and the top-level `ast.Name` is `identifier`"""
|
|
214
302
|
def workhorse(node: ast.AST) -> TypeGuard[ast.Subscript]:
|
|
215
|
-
return
|
|
303
|
+
return be.Subscript(node) and ifThis._nested_Identifier(identifier)(DOT.value(node))
|
|
216
304
|
return workhorse
|
|
217
305
|
|
|
218
306
|
@staticmethod
|
|
@@ -231,15 +319,3 @@ class ifThis:
|
|
|
231
319
|
def Z0Z_unparseIs(astAST: ast.AST) -> Callable[[ast.AST], bool]:
|
|
232
320
|
def workhorse(node: ast.AST) -> bool: return ast.unparse(node) == ast.unparse(astAST)
|
|
233
321
|
return workhorse
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
class be:
|
|
237
|
-
@staticmethod
|
|
238
|
-
def Call(node: ast.AST) -> TypeGuard[ast.Call]:
|
|
239
|
-
return isinstance(node, ast.Call)
|
|
240
|
-
@staticmethod
|
|
241
|
-
def Name(node: ast.AST) -> TypeGuard[ast.Name]:
|
|
242
|
-
return isinstance(node, ast.Name)
|
|
243
|
-
@staticmethod
|
|
244
|
-
def Return(node: ast.AST) -> TypeGuard[ast.Return]:
|
|
245
|
-
return isinstance(node, ast.Return)
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
"""
|
|
2
2
|
AST Container Classes for Python Code Generation and Transformation
|
|
3
3
|
|
|
4
|
-
This module provides specialized container classes that organize AST nodes, imports,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
The containers work in conjunction with transformation tools that manipulate the
|
|
20
|
-
contained AST nodes to implement specific optimizations and transformations.
|
|
4
|
+
This module provides specialized container classes that organize AST nodes, imports, and program structure for code
|
|
5
|
+
generation and transformation. These classes form the organizational backbone of the code generation system, enabling:
|
|
6
|
+
|
|
7
|
+
1. Tracking and managing imports with LedgerOfImports.
|
|
8
|
+
2. Packaging function definitions with their dependencies via IngredientsFunction.
|
|
9
|
+
3. Structuring complete modules with IngredientsModule.
|
|
10
|
+
4. Configuring code synthesis with RecipeSynthesizeFlow.
|
|
11
|
+
5. Organizing decomposed dataclass representations with ShatteredDataclass.
|
|
12
|
+
|
|
13
|
+
Together, these container classes implement a component-based architecture for programmatic generation of
|
|
14
|
+
high-performance code. They maintain a clean separation between structure and content, allowing transformations to be
|
|
15
|
+
applied systematically while preserving relationships between code elements.
|
|
16
|
+
|
|
17
|
+
The containers work in conjunction with transformation tools that manipulate the contained AST nodes to implement
|
|
18
|
+
specific optimizations and transformations.
|
|
21
19
|
"""
|
|
22
20
|
|
|
23
21
|
from collections import defaultdict
|
|
@@ -33,30 +31,31 @@ class LedgerOfImports:
|
|
|
33
31
|
"""
|
|
34
32
|
Track and manage import statements for programmatically generated code.
|
|
35
33
|
|
|
36
|
-
LedgerOfImports acts as a registry for import statements, maintaining a clean
|
|
37
|
-
|
|
34
|
+
LedgerOfImports acts as a registry for import statements, maintaining a clean separation between the logical
|
|
35
|
+
structure of imports and their textual representation.
|
|
38
36
|
It enables:
|
|
39
37
|
|
|
40
|
-
1. Tracking regular imports and import-from statements
|
|
41
|
-
2. Adding imports programmatically during code transformation
|
|
42
|
-
3. Merging imports from multiple sources
|
|
43
|
-
4. Removing unnecessary or conflicting imports
|
|
44
|
-
5. Generating optimized AST import nodes for the final code
|
|
38
|
+
1. Tracking regular imports and import-from statements.
|
|
39
|
+
2. Adding imports programmatically during code transformation.
|
|
40
|
+
3. Merging imports from multiple sources.
|
|
41
|
+
4. Removing unnecessary or conflicting imports.
|
|
42
|
+
5. Generating optimized AST import nodes for the final code.
|
|
45
43
|
|
|
46
|
-
This class forms the foundation of dependency management in generated code,
|
|
47
|
-
|
|
48
|
-
conflict.
|
|
44
|
+
This class forms the foundation of dependency management in generated code, ensuring that all required libraries are
|
|
45
|
+
available without duplication or conflict.
|
|
49
46
|
"""
|
|
50
47
|
# TODO When resolving the ledger of imports, remove self-referential imports
|
|
51
|
-
# TODO add TypeIgnore tracking to the ledger of imports
|
|
52
48
|
|
|
53
|
-
|
|
49
|
+
type_ignores: list[ast.TypeIgnore]
|
|
50
|
+
|
|
51
|
+
def __init__(self, startWith: ast.AST | None = None, type_ignores: list[ast.TypeIgnore] | None = None) -> None:
|
|
54
52
|
self.dictionaryImportFrom: dict[str_nameDOTname, list[tuple[ast_Identifier, ast_Identifier | None]]] = defaultdict(list)
|
|
55
53
|
self.listImport: list[str_nameDOTname] = []
|
|
54
|
+
self.type_ignores = [] if type_ignores is None else list(type_ignores)
|
|
56
55
|
if startWith:
|
|
57
56
|
self.walkThis(startWith)
|
|
58
57
|
|
|
59
|
-
def addAst(self, astImport____: ast.Import | ast.ImportFrom) -> None:
|
|
58
|
+
def addAst(self, astImport____: ast.Import | ast.ImportFrom, type_ignores: list[ast.TypeIgnore] | None = None) -> None:
|
|
60
59
|
match astImport____:
|
|
61
60
|
case ast.Import():
|
|
62
61
|
for alias in astImport____.names:
|
|
@@ -69,14 +68,20 @@ class LedgerOfImports:
|
|
|
69
68
|
self.dictionaryImportFrom[astImport____.module].append((alias.name, alias.asname))
|
|
70
69
|
case _:
|
|
71
70
|
raise ValueError(f"I received {type(astImport____) = }, but I can only accept {ast.Import} and {ast.ImportFrom}.")
|
|
71
|
+
if type_ignores:
|
|
72
|
+
self.type_ignores.extend(type_ignores)
|
|
72
73
|
|
|
73
|
-
def addImport_asStr(self, moduleWithLogicalPath: str_nameDOTname) -> None:
|
|
74
|
+
def addImport_asStr(self, moduleWithLogicalPath: str_nameDOTname, type_ignores: list[ast.TypeIgnore] | None = None) -> None:
|
|
74
75
|
self.listImport.append(moduleWithLogicalPath)
|
|
76
|
+
if type_ignores:
|
|
77
|
+
self.type_ignores.extend(type_ignores)
|
|
75
78
|
|
|
76
|
-
def addImportFrom_asStr(self, moduleWithLogicalPath: str_nameDOTname, name: ast_Identifier, asname: ast_Identifier | None = None) -> None:
|
|
79
|
+
def addImportFrom_asStr(self, moduleWithLogicalPath: str_nameDOTname, name: ast_Identifier, asname: ast_Identifier | None = None, type_ignores: list[ast.TypeIgnore] | None = None) -> None:
|
|
77
80
|
if moduleWithLogicalPath not in self.dictionaryImportFrom:
|
|
78
81
|
self.dictionaryImportFrom[moduleWithLogicalPath] = []
|
|
79
82
|
self.dictionaryImportFrom[moduleWithLogicalPath].append((name, asname))
|
|
83
|
+
if type_ignores:
|
|
84
|
+
self.type_ignores.extend(type_ignores)
|
|
80
85
|
|
|
81
86
|
def removeImportFromModule(self, moduleWithLogicalPath: str_nameDOTname) -> None:
|
|
82
87
|
"""Remove all imports from a specific module."""
|
|
@@ -129,11 +134,14 @@ class LedgerOfImports:
|
|
|
129
134
|
self.dictionaryImportFrom = updateExtendPolishDictionaryLists(self.dictionaryImportFrom, *(ledger.dictionaryImportFrom for ledger in fromLedger), destroyDuplicates=True, reorderLists=True)
|
|
130
135
|
for ledger in fromLedger:
|
|
131
136
|
self.listImport.extend(ledger.listImport)
|
|
137
|
+
self.type_ignores.extend(ledger.type_ignores)
|
|
132
138
|
|
|
133
|
-
def walkThis(self, walkThis: ast.AST) -> None:
|
|
139
|
+
def walkThis(self, walkThis: ast.AST, type_ignores: list[ast.TypeIgnore] | None = None) -> None:
|
|
134
140
|
for nodeBuffalo in ast.walk(walkThis):
|
|
135
141
|
if isinstance(nodeBuffalo, (ast.Import, ast.ImportFrom)):
|
|
136
142
|
self.addAst(nodeBuffalo)
|
|
143
|
+
if type_ignores:
|
|
144
|
+
self.type_ignores.extend(type_ignores)
|
|
137
145
|
|
|
138
146
|
# Consolidate settings classes through inheritance https://github.com/hunterhogan/mapFolding/issues/15
|
|
139
147
|
@dataclasses.dataclass
|
|
@@ -141,17 +149,16 @@ class IngredientsFunction:
|
|
|
141
149
|
"""
|
|
142
150
|
Package a function definition with its import dependencies for code generation.
|
|
143
151
|
|
|
144
|
-
IngredientsFunction encapsulates an AST function definition along with all the
|
|
145
|
-
|
|
146
|
-
portable unit that can be:
|
|
152
|
+
IngredientsFunction encapsulates an AST function definition along with all the imports required for that function to
|
|
153
|
+
operate correctly. This creates a modular, portable unit that can be:
|
|
147
154
|
|
|
148
|
-
1. Transformed independently (e.g., by applying Numba decorators)
|
|
149
|
-
2. Transplanted between modules while maintaining dependencies
|
|
150
|
-
3. Combined with other functions to form complete modules
|
|
151
|
-
4. Analyzed for optimization opportunities
|
|
155
|
+
1. Transformed independently (e.g., by applying Numba decorators).
|
|
156
|
+
2. Transplanted between modules while maintaining dependencies.
|
|
157
|
+
3. Combined with other functions to form complete modules.
|
|
158
|
+
4. Analyzed for optimization opportunities.
|
|
152
159
|
|
|
153
|
-
This class forms the primary unit of function manipulation in the code generation
|
|
154
|
-
|
|
160
|
+
This class forms the primary unit of function manipulation in the code generation system, enabling targeted
|
|
161
|
+
transformations while preserving function dependencies.
|
|
155
162
|
|
|
156
163
|
Parameters:
|
|
157
164
|
astFunctionDef: The AST representation of the function definition
|
|
@@ -251,7 +258,6 @@ class IngredientsModule:
|
|
|
251
258
|
def appendIngredientsFunction(self, *ingredientsFunction: IngredientsFunction) -> None:
|
|
252
259
|
"""Append one or more `IngredientsFunction`."""
|
|
253
260
|
for allegedIngredientsFunction in ingredientsFunction:
|
|
254
|
-
assert isinstance(allegedIngredientsFunction, IngredientsFunction), ValueError(f"I received `{type(allegedIngredientsFunction) = }`, but I can only accept `{IngredientsFunction}`.")
|
|
255
261
|
self.listIngredientsFunctions.append(allegedIngredientsFunction)
|
|
256
262
|
|
|
257
263
|
def removeImportFromModule(self, moduleWithLogicalPath: str_nameDOTname) -> None:
|
|
@@ -267,15 +273,18 @@ class IngredientsModule:
|
|
|
267
273
|
for ingredientsFunction in self.listIngredientsFunctions:
|
|
268
274
|
ingredientsFunction.imports.removeImportFrom(moduleWithLogicalPath, name, asname)
|
|
269
275
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
"""List of `ast.Import` and `ast.ImportFrom` statements."""
|
|
276
|
+
def _consolidatedLedger(self) -> LedgerOfImports:
|
|
277
|
+
"""Consolidate all ledgers of imports."""
|
|
273
278
|
sherpaLedger = LedgerOfImports()
|
|
274
279
|
listLedgers: list[LedgerOfImports] = [self.imports]
|
|
275
280
|
for ingredientsFunction in self.listIngredientsFunctions:
|
|
276
281
|
listLedgers.append(ingredientsFunction.imports)
|
|
277
282
|
sherpaLedger.update(*listLedgers)
|
|
278
|
-
return sherpaLedger
|
|
283
|
+
return sherpaLedger
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
def list_astImportImportFrom(self) -> list[ast.Import | ast.ImportFrom]:
|
|
287
|
+
return self._consolidatedLedger().makeList_ast()
|
|
279
288
|
|
|
280
289
|
@property
|
|
281
290
|
def body(self) -> list[ast.stmt]:
|
|
@@ -292,7 +301,7 @@ class IngredientsModule:
|
|
|
292
301
|
@property
|
|
293
302
|
def type_ignores(self) -> list[ast.TypeIgnore]:
|
|
294
303
|
listTypeIgnore: list[ast.TypeIgnore] = self.supplemental_type_ignores
|
|
295
|
-
|
|
304
|
+
listTypeIgnore.extend(self._consolidatedLedger().type_ignores)
|
|
296
305
|
listTypeIgnore.extend(self.prologue.type_ignores)
|
|
297
306
|
for ingredientsFunction in self.listIngredientsFunctions:
|
|
298
307
|
listTypeIgnore.extend(ingredientsFunction.type_ignores)
|
|
@@ -317,7 +326,7 @@ class RecipeSynthesizeFlow:
|
|
|
317
326
|
|
|
318
327
|
This configuration class serves as a single source of truth for the code generation
|
|
319
328
|
process, ensuring consistency across all generated artifacts while enabling
|
|
320
|
-
customization of the transformation
|
|
329
|
+
customization of the transformation assembly line.
|
|
321
330
|
|
|
322
331
|
The transformation process uses this configuration to extract functions from the
|
|
323
332
|
source module, transform them according to optimization rules, and output
|
|
@@ -6,7 +6,7 @@ for specific map folding calculation jobs. Unlike the general-purpose transforma
|
|
|
6
6
|
in toolboxNumba.py, this module creates standalone Python modules optimized for a
|
|
7
7
|
single map shape with statically-encoded parameters.
|
|
8
8
|
|
|
9
|
-
The code generation
|
|
9
|
+
The code generation assembly line focuses on:
|
|
10
10
|
|
|
11
11
|
1. Converting function parameters to initialized variables with concrete values.
|
|
12
12
|
2. Replacing dynamic computations with statically-known values.
|
|
@@ -18,7 +18,7 @@ This creates extremely fast, specialized implementations that can be run directl
|
|
|
18
18
|
as Python scripts or further compiled into standalone executables.
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
from mapFolding
|
|
21
|
+
from mapFolding import getPathFilenameFoldsTotal, raiseIfNoneGitHubIssueNumber3, The
|
|
22
22
|
from mapFolding.someAssemblyRequired import (
|
|
23
23
|
ast_Identifier,
|
|
24
24
|
be,
|
|
@@ -29,15 +29,15 @@ from mapFolding.someAssemblyRequired import (
|
|
|
29
29
|
Make,
|
|
30
30
|
NodeChanger,
|
|
31
31
|
NodeTourist,
|
|
32
|
+
str_nameDOTname,
|
|
32
33
|
Then,
|
|
33
34
|
)
|
|
34
|
-
from mapFolding.someAssemblyRequired.toolboxNumba import parametersNumbaLight, SpicesJobNumba, decorateCallableWithNumba
|
|
35
|
-
from mapFolding.someAssemblyRequired.transformationTools import extractFunctionDef, write_astModule, makeInitializedComputationState
|
|
36
35
|
from mapFolding.someAssemblyRequired.RecipeJob import RecipeJob
|
|
37
|
-
from mapFolding import
|
|
38
|
-
from
|
|
39
|
-
from Z0Z_tools import autoDecodingRLE
|
|
36
|
+
from mapFolding.someAssemblyRequired.toolboxNumba import parametersNumbaLight, SpicesJobNumba, decorateCallableWithNumba
|
|
37
|
+
from mapFolding.someAssemblyRequired.transformationTools import dictionaryEstimates, extractFunctionDef, write_astModule, makeInitializedComputationState
|
|
40
38
|
from pathlib import PurePosixPath
|
|
39
|
+
from typing import cast, NamedTuple
|
|
40
|
+
from Z0Z_tools import autoDecodingRLE
|
|
41
41
|
import ast
|
|
42
42
|
"""Synthesize one file to compute `foldsTotal` of `mapShape`."""
|
|
43
43
|
|
|
@@ -150,7 +150,7 @@ def move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsFunction: Ingre
|
|
|
150
150
|
case 'scalar':
|
|
151
151
|
ImaAnnAssign.value.args[0].value = int(job.state.__dict__[ast_arg.arg]) # type: ignore
|
|
152
152
|
case 'array':
|
|
153
|
-
dataAsStrRLE: str = autoDecodingRLE(job.state.__dict__[ast_arg.arg],
|
|
153
|
+
dataAsStrRLE: str = autoDecodingRLE(job.state.__dict__[ast_arg.arg], True)
|
|
154
154
|
dataAs_astExpr: ast.expr = cast(ast.Expr, ast.parse(dataAsStrRLE).body[0]).value
|
|
155
155
|
ImaAnnAssign.value.args = [dataAs_astExpr] # type: ignore
|
|
156
156
|
case _:
|
|
@@ -175,7 +175,7 @@ def makeJobNumba(job: RecipeJob, spices: SpicesJobNumba) -> None:
|
|
|
175
175
|
"""
|
|
176
176
|
Generate a highly-optimized, single-purpose Numba module for a specific map shape.
|
|
177
177
|
|
|
178
|
-
This function implements the complete transformation
|
|
178
|
+
This function implements the complete transformation assembly line for creating a
|
|
179
179
|
standalone, specialized implementation for calculating map folding solutions for
|
|
180
180
|
a specific shape. The process includes:
|
|
181
181
|
|
|
@@ -228,9 +228,9 @@ if __name__ == '__main__':
|
|
|
228
228
|
writeStream = open('{job.pathFilenameFoldsTotal.as_posix()}', 'w')
|
|
229
229
|
writeStream.write(str(foldsTotal))
|
|
230
230
|
writeStream.close()
|
|
231
|
-
from mapFolding.oeis import getFoldsTotalKnown
|
|
232
|
-
print(foldsTotal == getFoldsTotalKnown({job.state.mapShape}))
|
|
233
231
|
"""
|
|
232
|
+
# from mapFolding.oeis import getFoldsTotalKnown
|
|
233
|
+
# print(foldsTotal == getFoldsTotalKnown({job.state.mapShape}))
|
|
234
234
|
ingredientsModule.appendLauncher(ast.parse(linesLaunch))
|
|
235
235
|
changeReturnParallelCallable = NodeChanger(be.Return, Then.replaceWith(Make.Return(job.shatteredDataclass.countingVariableName)))
|
|
236
236
|
changeReturnParallelCallable.visit(ingredientsCount.astFunctionDef)
|
|
@@ -238,38 +238,37 @@ if __name__ == '__main__':
|
|
|
238
238
|
|
|
239
239
|
ingredientsCount = move_arg2FunctionDefDOTbodyAndAssignInitialValues(ingredientsCount, job)
|
|
240
240
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
241
|
+
class DatatypeConfig(NamedTuple):
|
|
242
|
+
Z0Z_module: str_nameDOTname
|
|
243
|
+
fml: ast_Identifier
|
|
244
|
+
Z0Z_type_name: ast_Identifier
|
|
245
|
+
Z0Z_asname: ast_Identifier | None = None
|
|
246
|
+
|
|
247
|
+
listDatatypeConfigs = [
|
|
248
|
+
DatatypeConfig(fml='DatatypeLeavesTotal', Z0Z_module='numba', Z0Z_type_name='uint8'),
|
|
249
|
+
DatatypeConfig(fml='DatatypeElephino', Z0Z_module='numba', Z0Z_type_name='uint16'),
|
|
250
|
+
DatatypeConfig(fml='DatatypeFoldsTotal', Z0Z_module='numba', Z0Z_type_name='uint64'),
|
|
251
|
+
]
|
|
252
|
+
|
|
253
|
+
for datatypeConfig in listDatatypeConfigs:
|
|
254
|
+
ingredientsModule.imports.addImportFrom_asStr(datatypeConfig.Z0Z_module, datatypeConfig.Z0Z_type_name)
|
|
255
|
+
statement = Make.Assign(
|
|
256
|
+
[Make.Name(datatypeConfig.fml, ast.Store())],
|
|
257
|
+
Make.Name(datatypeConfig.Z0Z_type_name)
|
|
258
|
+
)
|
|
259
|
+
ingredientsModule.appendPrologue(statement=statement)
|
|
246
260
|
|
|
247
|
-
|
|
248
|
-
Z0Z_type = 'uint8'
|
|
249
|
-
ingredientsModule.imports.addImportFrom_asStr('numba', Z0Z_type)
|
|
250
|
-
Z0Z_statement = Make.Assign([Make.Name(Z0Z_Identifier, ast.Store())], Make.Name(Z0Z_type))
|
|
251
|
-
ingredientsModule.appendPrologue(statement=Z0Z_statement)
|
|
261
|
+
ingredientsCount.imports.removeImportFromModule('mapFolding.theSSOT')
|
|
252
262
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
263
|
+
listNumPyTypeConfigs = [
|
|
264
|
+
DatatypeConfig(fml='Array1DLeavesTotal', Z0Z_module='numpy', Z0Z_type_name='uint8', Z0Z_asname='Array1DLeavesTotal'),
|
|
265
|
+
DatatypeConfig(fml='Array1DElephino', Z0Z_module='numpy', Z0Z_type_name='uint16', Z0Z_asname='Array1DElephino'),
|
|
266
|
+
DatatypeConfig(fml='Array3D', Z0Z_module='numpy', Z0Z_type_name='uint8', Z0Z_asname='Array3D'),
|
|
267
|
+
]
|
|
258
268
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
ingredientsCount.imports.removeImportFrom(Z0Z_module, None, Z0Z_asname)
|
|
263
|
-
Z0Z_type_name = 'uint8'
|
|
264
|
-
ingredientsCount.imports.addImportFrom_asStr(Z0Z_module, Z0Z_type_name, Z0Z_asname)
|
|
265
|
-
Z0Z_asname = 'Array1DElephino'
|
|
266
|
-
ingredientsCount.imports.removeImportFrom(Z0Z_module, None, Z0Z_asname)
|
|
267
|
-
Z0Z_type_name = 'uint8'
|
|
268
|
-
ingredientsCount.imports.addImportFrom_asStr(Z0Z_module, Z0Z_type_name, Z0Z_asname)
|
|
269
|
-
Z0Z_asname = 'Array3D'
|
|
270
|
-
ingredientsCount.imports.removeImportFrom(Z0Z_module, None, Z0Z_asname)
|
|
271
|
-
Z0Z_type_name = 'uint8'
|
|
272
|
-
ingredientsCount.imports.addImportFrom_asStr(Z0Z_module, Z0Z_type_name, Z0Z_asname)
|
|
269
|
+
for typeConfig in listNumPyTypeConfigs:
|
|
270
|
+
ingredientsCount.imports.removeImportFrom(typeConfig.Z0Z_module, None, typeConfig.fml)
|
|
271
|
+
ingredientsCount.imports.addImportFrom_asStr(typeConfig.Z0Z_module, typeConfig.Z0Z_type_name, typeConfig.Z0Z_asname)
|
|
273
272
|
|
|
274
273
|
ingredientsCount.astFunctionDef.decorator_list = [] # TODO low-priority, handle this more elegantly
|
|
275
274
|
# TODO when I add the function signature in numba style back to the decorator, the logic needs to handle `ProgressBarType:`
|
|
@@ -300,9 +299,10 @@ if __name__ == '__main__':
|
|
|
300
299
|
"""
|
|
301
300
|
|
|
302
301
|
if __name__ == '__main__':
|
|
303
|
-
mapShape = (
|
|
302
|
+
mapShape = (2,21)
|
|
304
303
|
state = makeInitializedComputationState(mapShape)
|
|
305
|
-
foldsTotalEstimated = getFoldsTotalKnown(state.mapShape) // state.leavesTotal
|
|
304
|
+
# foldsTotalEstimated = getFoldsTotalKnown(state.mapShape) // state.leavesTotal
|
|
305
|
+
foldsTotalEstimated = dictionaryEstimates[state.mapShape] // state.leavesTotal
|
|
306
306
|
pathModule = PurePosixPath(The.pathPackage, 'jobs')
|
|
307
307
|
pathFilenameFoldsTotal = PurePosixPath(getPathFilenameFoldsTotal(state.mapShape, pathModule))
|
|
308
308
|
aJob = RecipeJob(state, foldsTotalEstimated, pathModule=pathModule, pathFilenameFoldsTotal=pathFilenameFoldsTotal)
|
|
@@ -16,8 +16,8 @@ performance improvements while preserving code semantics and correctness.
|
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
from collections.abc import Callable, Sequence
|
|
19
|
-
from mapFolding.someAssemblyRequired import
|
|
20
|
-
from mapFolding.someAssemblyRequired.transformationTools import
|
|
19
|
+
from mapFolding.someAssemblyRequired import ast_Identifier, be, IngredientsFunction, Make, NodeTourist, RecipeSynthesizeFlow, str_nameDOTname, Then
|
|
20
|
+
from mapFolding.someAssemblyRequired.transformationTools import makeNewFlow, write_astModule
|
|
21
21
|
from numba.core.compiler import CompilerBase as numbaCompilerBase
|
|
22
22
|
from typing import Any, cast, Final, TYPE_CHECKING, TypeGuard
|
|
23
23
|
import ast
|
|
@@ -154,19 +154,11 @@ class SpicesJobNumba:
|
|
|
154
154
|
parametersNumba: ParametersNumba = dataclasses.field(default_factory=ParametersNumba) # type: ignore
|
|
155
155
|
|
|
156
156
|
# Consolidate settings classes through inheritance https://github.com/hunterhogan/mapFolding/issues/15
|
|
157
|
-
class be:
|
|
158
|
-
@staticmethod
|
|
159
|
-
def Call(node: ast.AST) -> TypeGuard[ast.Call]:
|
|
160
|
-
return isinstance(node, ast.Call)
|
|
161
|
-
@staticmethod
|
|
162
|
-
def Return(node: ast.AST) -> TypeGuard[ast.Return]:
|
|
163
|
-
return isinstance(node, ast.Return)
|
|
164
|
-
|
|
165
157
|
def makeNumbaFlow(numbaFlow: RecipeSynthesizeFlow) -> None:
|
|
166
158
|
"""
|
|
167
159
|
Transform standard Python algorithm code into optimized Numba implementations.
|
|
168
160
|
|
|
169
|
-
This function implements the complete transformation
|
|
161
|
+
This function implements the complete transformation assembly line that converts
|
|
170
162
|
a conventional Python implementation into a high-performance Numba-accelerated
|
|
171
163
|
version. The process includes:
|
|
172
164
|
|