zexus 1.6.2

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 (227) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +2513 -0
  3. package/bin/zexus +2 -0
  4. package/bin/zpics +2 -0
  5. package/bin/zpm +2 -0
  6. package/bin/zx +2 -0
  7. package/bin/zx-deploy +2 -0
  8. package/bin/zx-dev +2 -0
  9. package/bin/zx-run +2 -0
  10. package/package.json +66 -0
  11. package/scripts/README.md +24 -0
  12. package/scripts/postinstall.js +44 -0
  13. package/shared_config.json +24 -0
  14. package/src/README.md +1525 -0
  15. package/src/tests/run_zexus_tests.py +117 -0
  16. package/src/tests/test_all_phases.zx +346 -0
  17. package/src/tests/test_blockchain_features.zx +306 -0
  18. package/src/tests/test_complexity_features.zx +321 -0
  19. package/src/tests/test_core_integration.py +185 -0
  20. package/src/tests/test_phase10_ecosystem.zx +177 -0
  21. package/src/tests/test_phase1_modifiers.zx +87 -0
  22. package/src/tests/test_phase2_plugins.zx +80 -0
  23. package/src/tests/test_phase3_security.zx +97 -0
  24. package/src/tests/test_phase4_vfs.zx +116 -0
  25. package/src/tests/test_phase5_types.zx +117 -0
  26. package/src/tests/test_phase6_metaprogramming.zx +125 -0
  27. package/src/tests/test_phase7_optimization.zx +132 -0
  28. package/src/tests/test_phase9_advanced_types.zx +157 -0
  29. package/src/tests/test_security_features.py +419 -0
  30. package/src/tests/test_security_features.zx +276 -0
  31. package/src/tests/test_simple_zx.zx +1 -0
  32. package/src/tests/test_verification_simple.zx +69 -0
  33. package/src/zexus/__init__.py +28 -0
  34. package/src/zexus/__main__.py +5 -0
  35. package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
  36. package/src/zexus/__pycache__/advanced_types.cpython-312.pyc +0 -0
  37. package/src/zexus/__pycache__/builtin_modules.cpython-312.pyc +0 -0
  38. package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
  39. package/src/zexus/__pycache__/complexity_system.cpython-312.pyc +0 -0
  40. package/src/zexus/__pycache__/concurrency_system.cpython-312.pyc +0 -0
  41. package/src/zexus/__pycache__/config.cpython-312.pyc +0 -0
  42. package/src/zexus/__pycache__/dependency_injection.cpython-312.pyc +0 -0
  43. package/src/zexus/__pycache__/ecosystem.cpython-312.pyc +0 -0
  44. package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
  45. package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
  46. package/src/zexus/__pycache__/hybrid_orchestrator.cpython-312.pyc +0 -0
  47. package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
  48. package/src/zexus/__pycache__/metaprogramming.cpython-312.pyc +0 -0
  49. package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
  50. package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
  51. package/src/zexus/__pycache__/optimization.cpython-312.pyc +0 -0
  52. package/src/zexus/__pycache__/plugin_system.cpython-312.pyc +0 -0
  53. package/src/zexus/__pycache__/policy_engine.cpython-312.pyc +0 -0
  54. package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
  55. package/src/zexus/__pycache__/stdlib_integration.cpython-312.pyc +0 -0
  56. package/src/zexus/__pycache__/strategy_recovery.cpython-312.pyc +0 -0
  57. package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
  58. package/src/zexus/__pycache__/type_system.cpython-312.pyc +0 -0
  59. package/src/zexus/__pycache__/virtual_filesystem.cpython-312.pyc +0 -0
  60. package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  61. package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
  62. package/src/zexus/advanced_types.py +401 -0
  63. package/src/zexus/blockchain/__init__.py +40 -0
  64. package/src/zexus/blockchain/__pycache__/__init__.cpython-312.pyc +0 -0
  65. package/src/zexus/blockchain/__pycache__/crypto.cpython-312.pyc +0 -0
  66. package/src/zexus/blockchain/__pycache__/ledger.cpython-312.pyc +0 -0
  67. package/src/zexus/blockchain/__pycache__/transaction.cpython-312.pyc +0 -0
  68. package/src/zexus/blockchain/crypto.py +463 -0
  69. package/src/zexus/blockchain/ledger.py +255 -0
  70. package/src/zexus/blockchain/transaction.py +267 -0
  71. package/src/zexus/builtin_modules.py +284 -0
  72. package/src/zexus/builtin_plugins.py +317 -0
  73. package/src/zexus/capability_system.py +372 -0
  74. package/src/zexus/cli/__init__.py +2 -0
  75. package/src/zexus/cli/__pycache__/__init__.cpython-312.pyc +0 -0
  76. package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
  77. package/src/zexus/cli/main.py +707 -0
  78. package/src/zexus/cli/zpm.py +203 -0
  79. package/src/zexus/compare_interpreter_compiler.py +146 -0
  80. package/src/zexus/compiler/__init__.py +169 -0
  81. package/src/zexus/compiler/__pycache__/__init__.cpython-312.pyc +0 -0
  82. package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
  83. package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
  84. package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  85. package/src/zexus/compiler/bytecode.py +266 -0
  86. package/src/zexus/compiler/compat_runtime.py +277 -0
  87. package/src/zexus/compiler/lexer.py +257 -0
  88. package/src/zexus/compiler/parser.py +779 -0
  89. package/src/zexus/compiler/semantic.py +118 -0
  90. package/src/zexus/compiler/zexus_ast.py +454 -0
  91. package/src/zexus/complexity_system.py +575 -0
  92. package/src/zexus/concurrency_system.py +493 -0
  93. package/src/zexus/config.py +201 -0
  94. package/src/zexus/crypto_bridge.py +19 -0
  95. package/src/zexus/dependency_injection.py +423 -0
  96. package/src/zexus/ecosystem.py +434 -0
  97. package/src/zexus/environment.py +101 -0
  98. package/src/zexus/environment_manager.py +119 -0
  99. package/src/zexus/error_reporter.py +314 -0
  100. package/src/zexus/evaluator/__init__.py +12 -0
  101. package/src/zexus/evaluator/__pycache__/__init__.cpython-312.pyc +0 -0
  102. package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
  103. package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
  104. package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
  105. package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
  106. package/src/zexus/evaluator/__pycache__/integration.cpython-312.pyc +0 -0
  107. package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
  108. package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
  109. package/src/zexus/evaluator/bytecode_compiler.py +700 -0
  110. package/src/zexus/evaluator/core.py +891 -0
  111. package/src/zexus/evaluator/expressions.py +827 -0
  112. package/src/zexus/evaluator/functions.py +3989 -0
  113. package/src/zexus/evaluator/integration.py +396 -0
  114. package/src/zexus/evaluator/statements.py +4303 -0
  115. package/src/zexus/evaluator/utils.py +126 -0
  116. package/src/zexus/evaluator_original.py +2041 -0
  117. package/src/zexus/external_bridge.py +16 -0
  118. package/src/zexus/find_affected_imports.sh +155 -0
  119. package/src/zexus/hybrid_orchestrator.py +152 -0
  120. package/src/zexus/input_validation.py +259 -0
  121. package/src/zexus/lexer.py +571 -0
  122. package/src/zexus/logging.py +89 -0
  123. package/src/zexus/lsp/__init__.py +9 -0
  124. package/src/zexus/lsp/completion_provider.py +207 -0
  125. package/src/zexus/lsp/definition_provider.py +22 -0
  126. package/src/zexus/lsp/hover_provider.py +71 -0
  127. package/src/zexus/lsp/server.py +269 -0
  128. package/src/zexus/lsp/symbol_provider.py +31 -0
  129. package/src/zexus/metaprogramming.py +321 -0
  130. package/src/zexus/module_cache.py +89 -0
  131. package/src/zexus/module_manager.py +107 -0
  132. package/src/zexus/object.py +973 -0
  133. package/src/zexus/optimization.py +424 -0
  134. package/src/zexus/parser/__init__.py +31 -0
  135. package/src/zexus/parser/__pycache__/__init__.cpython-312.pyc +0 -0
  136. package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
  137. package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
  138. package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
  139. package/src/zexus/parser/integration.py +86 -0
  140. package/src/zexus/parser/parser.py +3977 -0
  141. package/src/zexus/parser/strategy_context.py +7254 -0
  142. package/src/zexus/parser/strategy_structural.py +1033 -0
  143. package/src/zexus/persistence.py +391 -0
  144. package/src/zexus/plugin_system.py +290 -0
  145. package/src/zexus/policy_engine.py +365 -0
  146. package/src/zexus/profiler/__init__.py +5 -0
  147. package/src/zexus/profiler/profiler.py +233 -0
  148. package/src/zexus/purity_system.py +398 -0
  149. package/src/zexus/runtime/__init__.py +20 -0
  150. package/src/zexus/runtime/async_runtime.py +324 -0
  151. package/src/zexus/search_old_imports.sh +65 -0
  152. package/src/zexus/security.py +1407 -0
  153. package/src/zexus/stack_trace.py +233 -0
  154. package/src/zexus/stdlib/__init__.py +27 -0
  155. package/src/zexus/stdlib/blockchain.py +341 -0
  156. package/src/zexus/stdlib/compression.py +167 -0
  157. package/src/zexus/stdlib/crypto.py +124 -0
  158. package/src/zexus/stdlib/datetime.py +163 -0
  159. package/src/zexus/stdlib/db_mongo.py +199 -0
  160. package/src/zexus/stdlib/db_mysql.py +162 -0
  161. package/src/zexus/stdlib/db_postgres.py +163 -0
  162. package/src/zexus/stdlib/db_sqlite.py +133 -0
  163. package/src/zexus/stdlib/encoding.py +230 -0
  164. package/src/zexus/stdlib/fs.py +195 -0
  165. package/src/zexus/stdlib/http.py +219 -0
  166. package/src/zexus/stdlib/http_server.py +248 -0
  167. package/src/zexus/stdlib/json_module.py +61 -0
  168. package/src/zexus/stdlib/math.py +360 -0
  169. package/src/zexus/stdlib/os_module.py +265 -0
  170. package/src/zexus/stdlib/regex.py +148 -0
  171. package/src/zexus/stdlib/sockets.py +253 -0
  172. package/src/zexus/stdlib/test_framework.zx +208 -0
  173. package/src/zexus/stdlib/test_runner.zx +119 -0
  174. package/src/zexus/stdlib_integration.py +341 -0
  175. package/src/zexus/strategy_recovery.py +256 -0
  176. package/src/zexus/syntax_validator.py +356 -0
  177. package/src/zexus/testing/zpics.py +407 -0
  178. package/src/zexus/testing/zpics_runtime.py +369 -0
  179. package/src/zexus/type_system.py +374 -0
  180. package/src/zexus/validation_system.py +569 -0
  181. package/src/zexus/virtual_filesystem.py +355 -0
  182. package/src/zexus/vm/__init__.py +8 -0
  183. package/src/zexus/vm/__pycache__/__init__.cpython-312.pyc +0 -0
  184. package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
  185. package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
  186. package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
  187. package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
  188. package/src/zexus/vm/__pycache__/memory_manager.cpython-312.pyc +0 -0
  189. package/src/zexus/vm/__pycache__/memory_pool.cpython-312.pyc +0 -0
  190. package/src/zexus/vm/__pycache__/optimizer.cpython-312.pyc +0 -0
  191. package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
  192. package/src/zexus/vm/__pycache__/peephole_optimizer.cpython-312.pyc +0 -0
  193. package/src/zexus/vm/__pycache__/profiler.cpython-312.pyc +0 -0
  194. package/src/zexus/vm/__pycache__/register_allocator.cpython-312.pyc +0 -0
  195. package/src/zexus/vm/__pycache__/register_vm.cpython-312.pyc +0 -0
  196. package/src/zexus/vm/__pycache__/ssa_converter.cpython-312.pyc +0 -0
  197. package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
  198. package/src/zexus/vm/async_optimizer.py +420 -0
  199. package/src/zexus/vm/bytecode.py +428 -0
  200. package/src/zexus/vm/bytecode_converter.py +297 -0
  201. package/src/zexus/vm/cache.py +532 -0
  202. package/src/zexus/vm/jit.py +720 -0
  203. package/src/zexus/vm/memory_manager.py +520 -0
  204. package/src/zexus/vm/memory_pool.py +511 -0
  205. package/src/zexus/vm/optimizer.py +478 -0
  206. package/src/zexus/vm/parallel_vm.py +899 -0
  207. package/src/zexus/vm/peephole_optimizer.py +452 -0
  208. package/src/zexus/vm/profiler.py +527 -0
  209. package/src/zexus/vm/register_allocator.py +462 -0
  210. package/src/zexus/vm/register_vm.py +520 -0
  211. package/src/zexus/vm/ssa_converter.py +757 -0
  212. package/src/zexus/vm/vm.py +1392 -0
  213. package/src/zexus/zexus_ast.py +1782 -0
  214. package/src/zexus/zexus_token.py +253 -0
  215. package/src/zexus/zpm/__init__.py +15 -0
  216. package/src/zexus/zpm/installer.py +116 -0
  217. package/src/zexus/zpm/package_manager.py +208 -0
  218. package/src/zexus/zpm/publisher.py +98 -0
  219. package/src/zexus/zpm/registry.py +110 -0
  220. package/src/zexus.egg-info/PKG-INFO +2235 -0
  221. package/src/zexus.egg-info/SOURCES.txt +876 -0
  222. package/src/zexus.egg-info/dependency_links.txt +1 -0
  223. package/src/zexus.egg-info/entry_points.txt +3 -0
  224. package/src/zexus.egg-info/not-zip-safe +1 -0
  225. package/src/zexus.egg-info/requires.txt +14 -0
  226. package/src/zexus.egg-info/top_level.txt +2 -0
  227. package/zexus.json +14 -0
@@ -0,0 +1,532 @@
1
+ """
2
+ Bytecode Caching System for Zexus VM
3
+
4
+ This module provides a comprehensive bytecode caching system to avoid recompiling
5
+ the same code multiple times. Features include:
6
+ - LRU (Least Recently Used) eviction policy
7
+ - Cache statistics tracking
8
+ - AST-based cache keys
9
+ - Optional persistent disk cache
10
+ - Memory-efficient storage
11
+
12
+ Part of Phase 4: Bytecode Caching Enhancement
13
+ """
14
+
15
+ import hashlib
16
+ import json
17
+ import pickle
18
+ import time
19
+ from collections import OrderedDict
20
+ from dataclasses import dataclass, field
21
+ from pathlib import Path
22
+ from typing import Any, Dict, Optional, Tuple
23
+
24
+ from .bytecode import Bytecode
25
+
26
+
27
+ @dataclass
28
+ class CacheStats:
29
+ """Statistics for bytecode cache"""
30
+ hits: int = 0
31
+ misses: int = 0
32
+ evictions: int = 0
33
+ memory_bytes: int = 0
34
+ total_entries: int = 0
35
+ hit_rate: float = 0.0
36
+
37
+ def update_hit_rate(self):
38
+ """Update hit rate percentage"""
39
+ total = self.hits + self.misses
40
+ self.hit_rate = (self.hits / total * 100) if total > 0 else 0.0
41
+
42
+ def to_dict(self) -> Dict[str, Any]:
43
+ """Convert stats to dictionary"""
44
+ return {
45
+ 'hits': self.hits,
46
+ 'misses': self.misses,
47
+ 'evictions': self.evictions,
48
+ 'memory_bytes': self.memory_bytes,
49
+ 'total_entries': self.total_entries,
50
+ 'hit_rate': round(self.hit_rate, 2)
51
+ }
52
+
53
+
54
+ @dataclass
55
+ class CacheEntry:
56
+ """Entry in the bytecode cache"""
57
+ bytecode: Bytecode
58
+ timestamp: float
59
+ access_count: int = 0
60
+ size_bytes: int = 0
61
+
62
+ def update_access(self):
63
+ """Update access timestamp and count"""
64
+ self.timestamp = time.time()
65
+ self.access_count += 1
66
+
67
+
68
+ class BytecodeCache:
69
+ """
70
+ LRU cache for compiled bytecode
71
+
72
+ Features:
73
+ - AST-based cache keys (hash of AST structure)
74
+ - LRU eviction when cache size limit is reached
75
+ - Statistics tracking (hits, misses, evictions)
76
+ - Optional persistent disk cache
77
+ - Memory-efficient storage
78
+
79
+ Usage:
80
+ cache = BytecodeCache(max_size=1000, persistent=False)
81
+
82
+ # Check cache
83
+ bytecode = cache.get(ast_node)
84
+ if bytecode is None:
85
+ # Compile and store
86
+ bytecode = compiler.compile(ast_node)
87
+ cache.put(ast_node, bytecode)
88
+
89
+ # Get statistics
90
+ stats = cache.get_stats()
91
+ print(f"Hit rate: {stats.hit_rate}%")
92
+ """
93
+
94
+ def __init__(
95
+ self,
96
+ max_size: int = 1000,
97
+ max_memory_mb: int = 100,
98
+ persistent: bool = False,
99
+ cache_dir: Optional[str] = None,
100
+ debug: bool = False
101
+ ):
102
+ """
103
+ Initialize bytecode cache
104
+
105
+ Args:
106
+ max_size: Maximum number of entries (default 1000)
107
+ max_memory_mb: Maximum memory usage in MB (default 100)
108
+ persistent: Enable disk-based persistent cache
109
+ cache_dir: Directory for persistent cache
110
+ debug: Enable debug output
111
+ """
112
+ self.max_size = max_size
113
+ self.max_memory_bytes = max_memory_mb * 1024 * 1024
114
+ self.persistent = persistent
115
+ self.debug = debug
116
+
117
+ # LRU cache using OrderedDict (insertion order preserved)
118
+ self._cache: OrderedDict[str, CacheEntry] = OrderedDict()
119
+
120
+ # Statistics
121
+ self.stats = CacheStats()
122
+
123
+ # Persistent cache
124
+ self.cache_dir = None
125
+ if persistent:
126
+ self.cache_dir = Path(cache_dir) if cache_dir else Path.home() / '.zexus' / 'cache'
127
+ self.cache_dir.mkdir(parents=True, exist_ok=True)
128
+ if self.debug:
129
+ print(f"📦 Cache: Persistent cache enabled at {self.cache_dir}")
130
+
131
+ def _hash_ast(self, ast_node: Any) -> str:
132
+ """
133
+ Generate unique hash for AST node
134
+
135
+ Uses JSON serialization of AST structure to create deterministic hash.
136
+ Handles circular references and complex nested structures.
137
+
138
+ Args:
139
+ ast_node: AST node to hash
140
+
141
+ Returns:
142
+ MD5 hash string (32 characters)
143
+ """
144
+ try:
145
+ # Convert AST to hashable representation
146
+ ast_repr = self._ast_to_dict(ast_node)
147
+ ast_json = json.dumps(ast_repr, sort_keys=True)
148
+ return hashlib.md5(ast_json.encode()).hexdigest()
149
+ except Exception as e:
150
+ # Fallback to string representation
151
+ if self.debug:
152
+ print(f"⚠️ Cache: AST hashing fallback ({e})")
153
+ return hashlib.md5(str(ast_node).encode()).hexdigest()
154
+
155
+ def _ast_to_dict(self, node: Any, depth: int = 0, max_depth: int = 50) -> Any:
156
+ """
157
+ Convert AST node to dictionary for hashing
158
+
159
+ Recursively converts AST nodes to dictionaries, handling:
160
+ - Node types and attributes
161
+ - Lists and tuples
162
+ - Nested nodes
163
+ - Circular references (via depth limit)
164
+
165
+ Args:
166
+ node: AST node or value
167
+ depth: Current recursion depth
168
+ max_depth: Maximum recursion depth
169
+
170
+ Returns:
171
+ Hashable representation (dict, list, or primitive)
172
+ """
173
+ if depth > max_depth:
174
+ return f"<max_depth_{type(node).__name__}>"
175
+
176
+ # Handle None
177
+ if node is None:
178
+ return None
179
+
180
+ # Handle primitives
181
+ if isinstance(node, (int, float, str, bool)):
182
+ return node
183
+
184
+ # Handle lists/tuples
185
+ if isinstance(node, (list, tuple)):
186
+ return [self._ast_to_dict(item, depth + 1, max_depth) for item in node]
187
+
188
+ # Handle dictionaries
189
+ if isinstance(node, dict):
190
+ return {k: self._ast_to_dict(v, depth + 1, max_depth) for k, v in node.items()}
191
+
192
+ # Handle AST nodes (objects with __dict__)
193
+ if hasattr(node, '__dict__'):
194
+ result = {'__type__': type(node).__name__}
195
+ for key, value in node.__dict__.items():
196
+ if not key.startswith('_'): # Skip private attributes
197
+ result[key] = self._ast_to_dict(value, depth + 1, max_depth)
198
+ return result
199
+
200
+ # Fallback to string representation
201
+ return f"<{type(node).__name__}>"
202
+
203
+ def _estimate_size(self, bytecode: Bytecode) -> int:
204
+ """
205
+ Estimate bytecode size in bytes
206
+
207
+ Approximates memory usage by counting:
208
+ - Instructions (each ~100 bytes)
209
+ - Constants (pickle size)
210
+ - Metadata (small overhead)
211
+
212
+ Args:
213
+ bytecode: Bytecode object
214
+
215
+ Returns:
216
+ Estimated size in bytes
217
+ """
218
+ try:
219
+ # Count instructions
220
+ instruction_size = len(bytecode.instructions) * 100 # ~100 bytes per instruction
221
+
222
+ # Estimate constants size
223
+ constants_size = 0
224
+ for const in bytecode.constants:
225
+ try:
226
+ constants_size += len(pickle.dumps(const))
227
+ except (TypeError, pickle.PicklingError):
228
+ constants_size += 100 # Fallback estimate
229
+
230
+ # Add metadata overhead
231
+ metadata_size = 200 # Small overhead for name, line_map, etc.
232
+
233
+ return instruction_size + constants_size + metadata_size
234
+ except (AttributeError, TypeError):
235
+ # Fallback to conservative estimate
236
+ return len(bytecode.instructions) * 150
237
+
238
+ def _evict_lru(self):
239
+ """
240
+ Evict least recently used entry
241
+
242
+ Removes the oldest entry (first in OrderedDict) and updates statistics.
243
+ """
244
+ if not self._cache:
245
+ return
246
+
247
+ # Remove oldest entry (LRU)
248
+ key, entry = self._cache.popitem(last=False)
249
+
250
+ # Update statistics
251
+ self.stats.evictions += 1
252
+ self.stats.memory_bytes -= entry.size_bytes
253
+ self.stats.total_entries -= 1
254
+
255
+ if self.debug:
256
+ print(f"🗑️ Cache: Evicted LRU entry {key[:8]}... (freed {entry.size_bytes} bytes)")
257
+
258
+ def _evict_to_fit(self, new_size: int):
259
+ """
260
+ Evict entries until new entry fits
261
+
262
+ Keeps evicting LRU entries until:
263
+ 1. Cache size < max_size
264
+ 2. Memory usage + new_size < max_memory_bytes
265
+
266
+ Args:
267
+ new_size: Size of new entry in bytes
268
+ """
269
+ # Evict by count
270
+ while len(self._cache) >= self.max_size:
271
+ self._evict_lru()
272
+
273
+ # Evict by memory
274
+ while self._cache and (self.stats.memory_bytes + new_size) > self.max_memory_bytes:
275
+ self._evict_lru()
276
+
277
+ def get(self, ast_node: Any) -> Optional[Bytecode]:
278
+ """
279
+ Get bytecode from cache
280
+
281
+ If found:
282
+ - Returns cached bytecode
283
+ - Updates access timestamp and count
284
+ - Moves entry to end (most recent in LRU)
285
+ - Increments hit counter
286
+
287
+ If not found:
288
+ - Increments miss counter
289
+ - Returns None
290
+
291
+ Args:
292
+ ast_node: AST node to look up
293
+
294
+ Returns:
295
+ Cached bytecode or None
296
+ """
297
+ key = self._hash_ast(ast_node)
298
+
299
+ if key in self._cache:
300
+ # Cache hit
301
+ entry = self._cache[key]
302
+ entry.update_access()
303
+
304
+ # Move to end (most recently used)
305
+ self._cache.move_to_end(key)
306
+
307
+ # Update statistics
308
+ self.stats.hits += 1
309
+ self.stats.update_hit_rate()
310
+
311
+ if self.debug:
312
+ print(f"✅ Cache: HIT {key[:8]}... (access #{entry.access_count})")
313
+
314
+ return entry.bytecode
315
+ else:
316
+ # Cache miss
317
+ self.stats.misses += 1
318
+ self.stats.update_hit_rate()
319
+
320
+ if self.debug:
321
+ print(f"❌ Cache: MISS {key[:8]}...")
322
+
323
+ # Try persistent cache if enabled
324
+ if self.persistent:
325
+ bytecode = self._load_from_disk(key)
326
+ if bytecode:
327
+ # Found in disk cache, add to memory cache
328
+ self.put(ast_node, bytecode, skip_disk=True)
329
+ return bytecode
330
+
331
+ return None
332
+
333
+ def put(self, ast_node: Any, bytecode: Bytecode, skip_disk: bool = False):
334
+ """
335
+ Store bytecode in cache
336
+
337
+ Process:
338
+ 1. Hash AST node to create cache key
339
+ 2. Estimate bytecode size
340
+ 3. Evict entries if needed to fit new entry
341
+ 4. Store in memory cache
342
+ 5. Optionally save to disk cache
343
+
344
+ Args:
345
+ ast_node: AST node (cache key)
346
+ bytecode: Compiled bytecode
347
+ skip_disk: Skip disk cache (used when loading from disk)
348
+ """
349
+ key = self._hash_ast(ast_node)
350
+ size = self._estimate_size(bytecode)
351
+
352
+ # Evict if needed
353
+ self._evict_to_fit(size)
354
+
355
+ # Create entry
356
+ entry = CacheEntry(
357
+ bytecode=bytecode,
358
+ timestamp=time.time(),
359
+ access_count=1,
360
+ size_bytes=size
361
+ )
362
+
363
+ # Store in cache
364
+ self._cache[key] = entry
365
+
366
+ # Update statistics
367
+ self.stats.memory_bytes += size
368
+ self.stats.total_entries += 1
369
+
370
+ if self.debug:
371
+ print(f"💾 Cache: PUT {key[:8]}... ({size} bytes, {len(self._cache)} entries)")
372
+
373
+ # Save to disk if persistent
374
+ if self.persistent and not skip_disk:
375
+ self._save_to_disk(key, bytecode)
376
+
377
+ def invalidate(self, ast_node: Any):
378
+ """
379
+ Remove entry from cache
380
+
381
+ Args:
382
+ ast_node: AST node to invalidate
383
+ """
384
+ key = self._hash_ast(ast_node)
385
+
386
+ if key in self._cache:
387
+ entry = self._cache.pop(key)
388
+ self.stats.memory_bytes -= entry.size_bytes
389
+ self.stats.total_entries -= 1
390
+
391
+ if self.debug:
392
+ print(f"🗑️ Cache: Invalidated {key[:8]}...")
393
+
394
+ # Remove from disk cache
395
+ if self.persistent:
396
+ self._delete_from_disk(key)
397
+
398
+ def clear(self):
399
+ """Clear entire cache"""
400
+ self._cache.clear()
401
+ self.stats = CacheStats()
402
+
403
+ if self.debug:
404
+ print("🗑️ Cache: Cleared all entries")
405
+
406
+ # Clear disk cache
407
+ if self.persistent and self.cache_dir:
408
+ for cache_file in self.cache_dir.glob('*.cache'):
409
+ cache_file.unlink()
410
+
411
+ def get_stats(self) -> Dict[str, Any]:
412
+ """
413
+ Get cache statistics
414
+
415
+ Returns:
416
+ Dictionary with cache statistics
417
+ """
418
+ self.stats.update_hit_rate()
419
+ return self.stats.to_dict()
420
+
421
+ def reset_stats(self):
422
+ """Reset statistics (keeps cache entries)"""
423
+ self.stats = CacheStats(
424
+ total_entries=len(self._cache),
425
+ memory_bytes=self.stats.memory_bytes
426
+ )
427
+
428
+ # ==================== Persistent Cache Methods ====================
429
+
430
+ def _save_to_disk(self, key: str, bytecode: Bytecode):
431
+ """Save bytecode to disk cache"""
432
+ if not self.cache_dir:
433
+ return
434
+
435
+ try:
436
+ cache_file = self.cache_dir / f"{key}.cache"
437
+ with open(cache_file, 'wb') as f:
438
+ pickle.dump(bytecode, f, protocol=pickle.HIGHEST_PROTOCOL)
439
+
440
+ if self.debug:
441
+ print(f"💾 Cache: Saved to disk {key[:8]}...")
442
+ except Exception as e:
443
+ if self.debug:
444
+ print(f"⚠️ Cache: Failed to save to disk: {e}")
445
+
446
+ def _load_from_disk(self, key: str) -> Optional[Bytecode]:
447
+ """Load bytecode from disk cache"""
448
+ if not self.cache_dir:
449
+ return None
450
+
451
+ try:
452
+ cache_file = self.cache_dir / f"{key}.cache"
453
+ if cache_file.exists():
454
+ with open(cache_file, 'rb') as f:
455
+ bytecode = pickle.load(f)
456
+
457
+ if self.debug:
458
+ print(f"💾 Cache: Loaded from disk {key[:8]}...")
459
+
460
+ return bytecode
461
+ except Exception as e:
462
+ if self.debug:
463
+ print(f"⚠️ Cache: Failed to load from disk: {e}")
464
+
465
+ return None
466
+
467
+ def _delete_from_disk(self, key: str):
468
+ """Delete cache entry from disk"""
469
+ if not self.cache_dir:
470
+ return
471
+
472
+ try:
473
+ cache_file = self.cache_dir / f"{key}.cache"
474
+ if cache_file.exists():
475
+ cache_file.unlink()
476
+ except Exception as e:
477
+ if self.debug:
478
+ print(f"⚠️ Cache: Failed to delete from disk: {e}")
479
+
480
+ # ==================== Utility Methods ====================
481
+
482
+ def size(self) -> int:
483
+ """Get current cache size (number of entries)"""
484
+ return len(self._cache)
485
+
486
+ def memory_usage(self) -> int:
487
+ """Get current memory usage in bytes"""
488
+ return self.stats.memory_bytes
489
+
490
+ def memory_usage_mb(self) -> float:
491
+ """Get current memory usage in MB"""
492
+ return self.stats.memory_bytes / (1024 * 1024)
493
+
494
+ def contains(self, ast_node: Any) -> bool:
495
+ """Check if AST node is in cache"""
496
+ key = self._hash_ast(ast_node)
497
+ return key in self._cache
498
+
499
+ def get_entry_info(self, ast_node: Any) -> Optional[Dict[str, Any]]:
500
+ """Get information about cached entry"""
501
+ key = self._hash_ast(ast_node)
502
+
503
+ if key in self._cache:
504
+ entry = self._cache[key]
505
+ return {
506
+ 'key': key,
507
+ 'timestamp': entry.timestamp,
508
+ 'access_count': entry.access_count,
509
+ 'size_bytes': entry.size_bytes,
510
+ 'instruction_count': len(entry.bytecode.instructions),
511
+ 'constant_count': len(entry.bytecode.constants)
512
+ }
513
+
514
+ return None
515
+
516
+ def get_all_keys(self) -> list:
517
+ """Get all cache keys"""
518
+ return list(self._cache.keys())
519
+
520
+ def __len__(self) -> int:
521
+ """Get cache size"""
522
+ return len(self._cache)
523
+
524
+ def __contains__(self, ast_node: Any) -> bool:
525
+ """Check if AST node is cached"""
526
+ return self.contains(ast_node)
527
+
528
+ def __repr__(self) -> str:
529
+ """String representation"""
530
+ return (f"BytecodeCache(size={len(self._cache)}/{self.max_size}, "
531
+ f"memory={self.memory_usage_mb():.2f}MB, "
532
+ f"hit_rate={self.stats.hit_rate:.1f}%)")