zexus 1.7.1 → 1.7.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 (159) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/src/__init__.py +7 -0
  4. package/src/zexus/__init__.py +1 -1
  5. package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
  6. package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
  7. package/src/zexus/__pycache__/debug_sanitizer.cpython-312.pyc +0 -0
  8. package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
  9. package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
  10. package/src/zexus/__pycache__/input_validation.cpython-312.pyc +0 -0
  11. package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
  12. package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
  13. package/src/zexus/__pycache__/module_manager.cpython-312.pyc +0 -0
  14. package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
  15. package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
  16. package/src/zexus/__pycache__/security_enforcement.cpython-312.pyc +0 -0
  17. package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
  18. package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  19. package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
  20. package/src/zexus/access_control_system/__pycache__/__init__.cpython-312.pyc +0 -0
  21. package/src/zexus/access_control_system/__pycache__/access_control.cpython-312.pyc +0 -0
  22. package/src/zexus/advanced_types.py +17 -2
  23. package/src/zexus/blockchain/__init__.py +411 -0
  24. package/src/zexus/blockchain/accelerator.py +1160 -0
  25. package/src/zexus/blockchain/chain.py +660 -0
  26. package/src/zexus/blockchain/consensus.py +821 -0
  27. package/src/zexus/blockchain/contract_vm.py +1019 -0
  28. package/src/zexus/blockchain/crypto.py +79 -14
  29. package/src/zexus/blockchain/events.py +526 -0
  30. package/src/zexus/blockchain/loadtest.py +721 -0
  31. package/src/zexus/blockchain/monitoring.py +350 -0
  32. package/src/zexus/blockchain/mpt.py +716 -0
  33. package/src/zexus/blockchain/multichain.py +951 -0
  34. package/src/zexus/blockchain/multiprocess_executor.py +338 -0
  35. package/src/zexus/blockchain/network.py +886 -0
  36. package/src/zexus/blockchain/node.py +666 -0
  37. package/src/zexus/blockchain/rpc.py +1203 -0
  38. package/src/zexus/blockchain/rust_bridge.py +421 -0
  39. package/src/zexus/blockchain/storage.py +423 -0
  40. package/src/zexus/blockchain/tokens.py +750 -0
  41. package/src/zexus/blockchain/upgradeable.py +1004 -0
  42. package/src/zexus/blockchain/verification.py +1602 -0
  43. package/src/zexus/blockchain/wallet.py +621 -0
  44. package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
  45. package/src/zexus/cli/main.py +300 -20
  46. package/src/zexus/cli/zpm.py +1 -1
  47. package/src/zexus/compiler/__pycache__/bytecode.cpython-312.pyc +0 -0
  48. package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
  49. package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
  50. package/src/zexus/compiler/__pycache__/semantic.cpython-312.pyc +0 -0
  51. package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  52. package/src/zexus/compiler/lexer.py +10 -5
  53. package/src/zexus/concurrency_system.py +79 -0
  54. package/src/zexus/config.py +54 -0
  55. package/src/zexus/crypto_bridge.py +244 -8
  56. package/src/zexus/dap/__init__.py +10 -0
  57. package/src/zexus/dap/__main__.py +4 -0
  58. package/src/zexus/dap/dap_server.py +391 -0
  59. package/src/zexus/dap/debug_engine.py +298 -0
  60. package/src/zexus/environment.py +10 -1
  61. package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
  62. package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
  63. package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
  64. package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
  65. package/src/zexus/evaluator/__pycache__/resource_limiter.cpython-312.pyc +0 -0
  66. package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
  67. package/src/zexus/evaluator/__pycache__/unified_execution.cpython-312.pyc +0 -0
  68. package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
  69. package/src/zexus/evaluator/bytecode_compiler.py +441 -37
  70. package/src/zexus/evaluator/core.py +560 -49
  71. package/src/zexus/evaluator/expressions.py +122 -49
  72. package/src/zexus/evaluator/functions.py +417 -16
  73. package/src/zexus/evaluator/statements.py +521 -118
  74. package/src/zexus/evaluator/unified_execution.py +573 -72
  75. package/src/zexus/evaluator/utils.py +14 -2
  76. package/src/zexus/event_loop.py +186 -0
  77. package/src/zexus/lexer.py +742 -486
  78. package/src/zexus/lsp/__init__.py +1 -1
  79. package/src/zexus/lsp/definition_provider.py +163 -9
  80. package/src/zexus/lsp/server.py +22 -8
  81. package/src/zexus/lsp/symbol_provider.py +182 -9
  82. package/src/zexus/module_cache.py +237 -9
  83. package/src/zexus/object.py +64 -6
  84. package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
  85. package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
  86. package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
  87. package/src/zexus/parser/parser.py +786 -285
  88. package/src/zexus/parser/strategy_context.py +407 -66
  89. package/src/zexus/parser/strategy_structural.py +117 -19
  90. package/src/zexus/persistence.py +15 -1
  91. package/src/zexus/renderer/__init__.py +15 -0
  92. package/src/zexus/renderer/__pycache__/__init__.cpython-312.pyc +0 -0
  93. package/src/zexus/renderer/__pycache__/backend.cpython-312.pyc +0 -0
  94. package/src/zexus/renderer/__pycache__/canvas.cpython-312.pyc +0 -0
  95. package/src/zexus/renderer/__pycache__/color_system.cpython-312.pyc +0 -0
  96. package/src/zexus/renderer/__pycache__/layout.cpython-312.pyc +0 -0
  97. package/src/zexus/renderer/__pycache__/main_renderer.cpython-312.pyc +0 -0
  98. package/src/zexus/renderer/__pycache__/painter.cpython-312.pyc +0 -0
  99. package/src/zexus/renderer/tk_backend.py +208 -0
  100. package/src/zexus/renderer/web_backend.py +260 -0
  101. package/src/zexus/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
  102. package/src/zexus/runtime/__pycache__/async_runtime.cpython-312.pyc +0 -0
  103. package/src/zexus/runtime/__pycache__/load_manager.cpython-312.pyc +0 -0
  104. package/src/zexus/runtime/file_flags.py +137 -0
  105. package/src/zexus/safety/__pycache__/__init__.cpython-312.pyc +0 -0
  106. package/src/zexus/safety/__pycache__/memory_safety.cpython-312.pyc +0 -0
  107. package/src/zexus/security.py +424 -34
  108. package/src/zexus/stdlib/fs.py +23 -18
  109. package/src/zexus/stdlib/http.py +289 -186
  110. package/src/zexus/stdlib/sockets.py +207 -163
  111. package/src/zexus/stdlib/websockets.py +282 -0
  112. package/src/zexus/stdlib_integration.py +369 -2
  113. package/src/zexus/strategy_recovery.py +6 -3
  114. package/src/zexus/type_checker.py +423 -0
  115. package/src/zexus/virtual_filesystem.py +189 -2
  116. package/src/zexus/vm/__init__.py +113 -3
  117. package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
  118. package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
  119. package/src/zexus/vm/__pycache__/bytecode_converter.cpython-312.pyc +0 -0
  120. package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
  121. package/src/zexus/vm/__pycache__/compiler.cpython-312.pyc +0 -0
  122. package/src/zexus/vm/__pycache__/gas_metering.cpython-312.pyc +0 -0
  123. package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
  124. package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
  125. package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
  126. package/src/zexus/vm/async_optimizer.py +14 -1
  127. package/src/zexus/vm/binary_bytecode.py +659 -0
  128. package/src/zexus/vm/bytecode.py +28 -1
  129. package/src/zexus/vm/bytecode_converter.py +26 -12
  130. package/src/zexus/vm/cabi.c +1985 -0
  131. package/src/zexus/vm/cabi.cpython-312-x86_64-linux-gnu.so +0 -0
  132. package/src/zexus/vm/cabi.h +127 -0
  133. package/src/zexus/vm/cache.py +557 -17
  134. package/src/zexus/vm/compiler.py +703 -5
  135. package/src/zexus/vm/fastops.c +15743 -0
  136. package/src/zexus/vm/fastops.cpython-312-x86_64-linux-gnu.so +0 -0
  137. package/src/zexus/vm/fastops.pyx +288 -0
  138. package/src/zexus/vm/gas_metering.py +50 -9
  139. package/src/zexus/vm/jit.py +83 -2
  140. package/src/zexus/vm/native_jit_backend.py +1816 -0
  141. package/src/zexus/vm/native_runtime.cpp +1388 -0
  142. package/src/zexus/vm/native_runtime.cpython-312-x86_64-linux-gnu.so +0 -0
  143. package/src/zexus/vm/optimizer.py +161 -11
  144. package/src/zexus/vm/parallel_vm.py +118 -42
  145. package/src/zexus/vm/peephole_optimizer.py +82 -4
  146. package/src/zexus/vm/profiler.py +38 -18
  147. package/src/zexus/vm/register_allocator.py +16 -5
  148. package/src/zexus/vm/register_vm.py +8 -5
  149. package/src/zexus/vm/vm.py +3411 -573
  150. package/src/zexus/vm/wasm_compiler.py +658 -0
  151. package/src/zexus/zexus_ast.py +63 -11
  152. package/src/zexus/zexus_token.py +13 -5
  153. package/src/zexus/zpm/installer.py +55 -15
  154. package/src/zexus/zpm/package_manager.py +1 -1
  155. package/src/zexus/zpm/registry.py +257 -28
  156. package/src/zexus.egg-info/PKG-INFO +7 -4
  157. package/src/zexus.egg-info/SOURCES.txt +116 -9
  158. package/src/zexus.egg-info/entry_points.txt +1 -0
  159. package/src/zexus.egg-info/requires.txt +4 -0
@@ -26,6 +26,8 @@ class FunctionEvaluatorMixin:
26
26
  # Initialize registries
27
27
  self.builtins = {}
28
28
  self._allow_coroutine_result = False
29
+ self._pending_revert_signature = None
30
+ self._tolerant_skip_counts = {}
29
31
 
30
32
  # Renderer Registry (moved from global scope to instance scope)
31
33
  self.render_registry = {
@@ -62,9 +64,44 @@ class FunctionEvaluatorMixin:
62
64
 
63
65
  return EvaluationError("Coroutine did not complete within step limit")
64
66
 
67
+ def _compute_revert_signature(self, reason_node):
68
+ if reason_node is None:
69
+ return ("none",)
70
+
71
+ try:
72
+ from .. import zexus_ast
73
+ if isinstance(reason_node, zexus_ast.StringLiteral):
74
+ return ("string", getattr(reason_node, "value", None))
75
+ if isinstance(reason_node, zexus_ast.Identifier):
76
+ return ("identifier", getattr(reason_node, "value", None))
77
+ except Exception:
78
+ pass
79
+
80
+ return ("expr", repr(reason_node))
81
+
65
82
  def eval_call_expression(self, node, env, stack_trace):
66
83
  debug_log("🚀 CallExpression node", f"Calling {node.function}")
67
84
 
85
+ if (
86
+ isinstance(node.function, zexus_ast.Identifier)
87
+ and getattr(node.function, "value", None) == "revert"
88
+ ):
89
+ if len(node.arguments) > 1:
90
+ return EvaluationError(
91
+ "revert() accepts at most one argument",
92
+ stack_trace=stack_trace,
93
+ )
94
+
95
+ debug_log(
96
+ "eval_call_expression",
97
+ "Treating call expression as revert statement",
98
+ )
99
+
100
+ reason_expr = node.arguments[0] if node.arguments else None
101
+ result = self._perform_revert(reason_expr, env, stack_trace)
102
+ self._pending_revert_signature = self._compute_revert_signature(reason_expr)
103
+ return result
104
+
68
105
  fn = self.eval_node(node.function, env, stack_trace)
69
106
  if is_error(fn):
70
107
  return fn
@@ -88,7 +125,6 @@ class FunctionEvaluatorMixin:
88
125
 
89
126
  # Check if arguments contain keyword arguments (AssignmentExpression nodes)
90
127
  # This handles syntax like: Person(name="Bob", age=25)
91
- from .. import zexus_ast
92
128
  has_keyword_args = any(isinstance(arg, zexus_ast.AssignmentExpression) for arg in node.arguments)
93
129
 
94
130
  if has_keyword_args:
@@ -149,7 +185,6 @@ class FunctionEvaluatorMixin:
149
185
  Example: Box<number> creates a specialized Box constructor with T = number
150
186
  """
151
187
  from ..object import EvaluationError, String, Map
152
- from .. import zexus_ast
153
188
 
154
189
  debug_log("_create_specialized_generic_constructor", f"Specializing with types: {type_args}")
155
190
 
@@ -332,6 +367,7 @@ class FunctionEvaluatorMixin:
332
367
  except Exception:
333
368
  pass
334
369
 
370
+ result = None
335
371
  try:
336
372
  res = self.eval_node(fn.body, new_env)
337
373
  res = _resolve_awaitable(res)
@@ -347,6 +383,10 @@ class FunctionEvaluatorMixin:
347
383
  # Store result for after-call hook
348
384
  result = EvaluationError(str(e))
349
385
  raise
386
+ except BaseException as e:
387
+ # Ensure result is set even for interrupts/SystemExit
388
+ result = EvaluationError(str(e))
389
+ raise
350
390
  finally:
351
391
  # CRITICAL: Execute deferred cleanup when function exits
352
392
  # This happens in finally block to ensure cleanup runs even on errors
@@ -466,7 +506,9 @@ class FunctionEvaluatorMixin:
466
506
  def eval_method_call_expression(self, node, env, stack_trace):
467
507
  debug_log(" MethodCallExpression node", f"{node.object}.{node.method}")
468
508
 
509
+ debug_log("DEBUG evaluating method call object", node.object)
469
510
  obj = self.eval_node(node.object, env, stack_trace)
511
+ debug_log("DEBUG object evaluated to", obj)
470
512
  if is_error(obj):
471
513
  return obj
472
514
 
@@ -625,6 +667,32 @@ class FunctionEvaluatorMixin:
625
667
  if is_error(args):
626
668
  return args
627
669
  result = obj.call_method(method_name, args)
670
+
671
+ # CRITICAL: Sync state variables back to caller's environment after internal call
672
+ # This ensures that when action A calls this.actionB(), any state changes made
673
+ # by actionB are visible to actionA's local environment
674
+ # OPTIMIZATION: Only sync for 'this' calls (internal calls within same contract)
675
+ from ..security import SmartContract
676
+ if isinstance(obj, SmartContract):
677
+ # Check if this is a 'this.method()' call by checking the caller's env
678
+ this_ref = env.get('this')
679
+ if this_ref is obj and hasattr(obj, 'storage_vars'):
680
+ # This is an internal call - sync state variables
681
+ for var_node in obj.storage_vars:
682
+ var_name = None
683
+ if hasattr(var_node, 'name'):
684
+ var_name = var_node.name.value if hasattr(var_node.name, 'value') else var_node.name
685
+ elif isinstance(var_node, dict):
686
+ var_name = var_node.get("name")
687
+ elif isinstance(var_node, str):
688
+ var_name = var_node
689
+
690
+ if var_name:
691
+ # Read the updated value from storage and refresh the caller's env
692
+ updated_value = obj.storage.get(var_name)
693
+ if updated_value is not None:
694
+ env.set(var_name, updated_value)
695
+
628
696
  # Unwrap ReturnValue if needed
629
697
  from ..object import ReturnValue
630
698
  if isinstance(result, ReturnValue):
@@ -655,6 +723,7 @@ class FunctionEvaluatorMixin:
655
723
  return self.apply_function(method_value, args, env)
656
724
 
657
725
  obj_type = obj.type() if hasattr(obj, 'type') and callable(obj.type) else type(obj).__name__
726
+ debug_log("DEBUG method dispatch failure", f"method={method_name}, obj={obj}, obj_type={obj_type}")
658
727
  return EvaluationError(f"Method '{method_name}' not supported for {obj_type}")
659
728
 
660
729
  # --- Array Helpers (Internal) ---
@@ -863,16 +932,56 @@ class FunctionEvaluatorMixin:
863
932
  except Exception as e:
864
933
  return EvaluationError(f"constant_time_compare() error: {str(e)}")
865
934
 
866
- # File I/O
935
+ # File I/O — VFS-aware helpers
936
+ # The VFS manager (with its file cache) is obtained lazily so the
937
+ # evaluator can function even when the integration layer is not loaded.
938
+ _vfs_mgr = None
939
+ def _get_vfs_manager():
940
+ nonlocal _vfs_mgr
941
+ if _vfs_mgr is None:
942
+ try:
943
+ from .integration import get_integration
944
+ _vfs_mgr = get_integration().vfs_manager
945
+ except Exception:
946
+ pass
947
+ return _vfs_mgr
948
+
949
+ def _resolve_read_path(path_str):
950
+ """Resolve a path for reading – uses VFS cache when available."""
951
+ import os
952
+ if not os.path.isabs(path_str):
953
+ path_str = os.path.join(os.getcwd(), path_str)
954
+ return os.path.normpath(path_str)
955
+
956
+ def _cached_read(real_path):
957
+ """Read via VFS cache (skips disk if file unchanged)."""
958
+ mgr = _get_vfs_manager()
959
+ if mgr is not None:
960
+ return mgr.cached_read(real_path)
961
+ with open(real_path, 'r') as f:
962
+ return f.read()
963
+
964
+ def _invalidate_file_cache(real_path):
965
+ """Invalidate VFS cache after a write."""
966
+ mgr = _get_vfs_manager()
967
+ if mgr is not None:
968
+ mgr.invalidate_cache(real_path)
969
+
867
970
  def _read_text(*a):
971
+ cap_err = _check_io_read_capability()
972
+ if cap_err: return cap_err
868
973
  if len(a) != 1 or not isinstance(a[0], String):
869
974
  return EvaluationError("file_read_text() takes exactly 1 string argument")
870
975
  return File.read_text(a[0])
871
976
 
872
977
  def _write_text(*a):
978
+ cap_err = _check_io_write_capability()
979
+ if cap_err: return cap_err
873
980
  if len(a) != 2 or not all(isinstance(x, String) for x in a):
874
981
  return EvaluationError("file_write_text() takes exactly 2 string arguments")
875
- return File.write_text(a[0], a[1])
982
+ result = File.write_text(a[0], a[1])
983
+ _invalidate_file_cache(_resolve_read_path(a[0].value))
984
+ return result
876
985
 
877
986
  def _exists(*a):
878
987
  if len(a) != 1 or not isinstance(a[0], String):
@@ -880,26 +989,38 @@ class FunctionEvaluatorMixin:
880
989
  return File.exists(a[0])
881
990
 
882
991
  def _read_json(*a):
992
+ cap_err = _check_io_read_capability()
993
+ if cap_err: return cap_err
883
994
  if len(a) != 1 or not isinstance(a[0], String):
884
995
  return EvaluationError("file_read_json() takes exactly 1 string argument")
885
996
  return File.read_json(a[0])
886
997
 
887
998
  def _write_json(*a):
999
+ cap_err = _check_io_write_capability()
1000
+ if cap_err: return cap_err
888
1001
  if len(a) != 2 or not isinstance(a[0], String):
889
1002
  return EvaluationError("file_write_json() takes path string and data")
890
- return File.write_json(a[0], a[1])
1003
+ result = File.write_json(a[0], a[1])
1004
+ _invalidate_file_cache(_resolve_read_path(a[0].value))
1005
+ return result
891
1006
 
892
1007
  def _file_append(*a):
1008
+ cap_err = _check_io_write_capability()
1009
+ if cap_err: return cap_err
893
1010
  if len(a) != 2 or not all(isinstance(x, String) for x in a):
894
1011
  return EvaluationError("file_append() takes exactly 2 string arguments")
895
- return File.append_text(a[0], a[1])
1012
+ result = File.append_text(a[0], a[1])
1013
+ _invalidate_file_cache(_resolve_read_path(a[0].value))
1014
+ return result
896
1015
 
897
1016
  def _list_dir(*a):
1017
+ cap_err = _check_io_read_capability()
1018
+ if cap_err: return cap_err
898
1019
  if len(a) != 1 or not isinstance(a[0], String):
899
1020
  return EvaluationError("file_list_dir() takes exactly 1 string argument")
900
1021
  return File.list_directory(a[0])
901
1022
 
902
- # Extended File System Operations
1023
+ # Extended File System Operations (with capability checks)
903
1024
  def _fs_is_file(*a):
904
1025
  if len(a) != 1 or not isinstance(a[0], String):
905
1026
  return EvaluationError("fs_is_file() takes exactly 1 string argument")
@@ -913,6 +1034,8 @@ class FunctionEvaluatorMixin:
913
1034
  return BooleanObj(os.path.isdir(a[0].value))
914
1035
 
915
1036
  def _fs_mkdir(*a):
1037
+ cap_err = _check_io_write_capability()
1038
+ if cap_err: return cap_err
916
1039
  if len(a) < 1 or not isinstance(a[0], String):
917
1040
  return EvaluationError("fs_mkdir() takes path string and optional parents boolean")
918
1041
  from pathlib import Path
@@ -926,16 +1049,21 @@ class FunctionEvaluatorMixin:
926
1049
  return EvaluationError(f"fs_mkdir() error: {str(e)}")
927
1050
 
928
1051
  def _fs_remove(*a):
1052
+ cap_err = _check_io_write_capability()
1053
+ if cap_err: return cap_err
929
1054
  if len(a) != 1 or not isinstance(a[0], String):
930
1055
  return EvaluationError("fs_remove() takes exactly 1 string argument")
931
1056
  import os
932
1057
  try:
933
1058
  os.remove(a[0].value)
1059
+ _invalidate_file_cache(_resolve_read_path(a[0].value))
934
1060
  return BooleanObj(True)
935
1061
  except Exception as e:
936
1062
  return EvaluationError(f"fs_remove() error: {str(e)}")
937
1063
 
938
1064
  def _fs_rmdir(*a):
1065
+ cap_err = _check_io_write_capability()
1066
+ if cap_err: return cap_err
939
1067
  if len(a) < 1 or not isinstance(a[0], String):
940
1068
  return EvaluationError("fs_rmdir() takes path string and optional recursive boolean")
941
1069
  import os
@@ -953,6 +1081,8 @@ class FunctionEvaluatorMixin:
953
1081
  return EvaluationError(f"fs_rmdir() error: {str(e)}")
954
1082
 
955
1083
  def _fs_rename(*a):
1084
+ cap_err = _check_io_write_capability()
1085
+ if cap_err: return cap_err
956
1086
  if len(a) != 2 or not all(isinstance(x, String) for x in a):
957
1087
  return EvaluationError("fs_rename() takes exactly 2 string arguments: old_path, new_path")
958
1088
  import os
@@ -963,6 +1093,8 @@ class FunctionEvaluatorMixin:
963
1093
  return EvaluationError(f"fs_rename() error: {str(e)}")
964
1094
 
965
1095
  def _fs_copy(*a):
1096
+ cap_err = _check_io_write_capability()
1097
+ if cap_err: return cap_err
966
1098
  if len(a) != 2 or not all(isinstance(x, String) for x in a):
967
1099
  return EvaluationError("fs_copy() takes exactly 2 string arguments: src, dst")
968
1100
  import shutil
@@ -983,6 +1115,8 @@ class FunctionEvaluatorMixin:
983
1115
  # Socket/TCP Primitives
984
1116
  def _socket_create_server(*a):
985
1117
  """Create TCP server: socket_create_server(host, port, handler, backlog?)"""
1118
+ cap_err = _check_network_capability()
1119
+ if cap_err: return cap_err
986
1120
  if len(a) < 3:
987
1121
  return EvaluationError("socket_create_server() requires at least 3 arguments: host, port, handler")
988
1122
 
@@ -1072,6 +1206,8 @@ class FunctionEvaluatorMixin:
1072
1206
 
1073
1207
  def _socket_create_connection(*a):
1074
1208
  """Create TCP client: socket_create_connection(host, port, timeout?)"""
1209
+ cap_err = _check_network_capability()
1210
+ if cap_err: return cap_err
1075
1211
  if len(a) < 2:
1076
1212
  return EvaluationError("socket_create_connection() requires at least 2 arguments: host, port")
1077
1213
 
@@ -1722,9 +1858,42 @@ class FunctionEvaluatorMixin:
1722
1858
  except Exception as e:
1723
1859
  return EvaluationError(f"mongo_connect() error: {str(e)}")
1724
1860
 
1861
+ # Capability-checked helpers
1862
+ def _check_network_capability():
1863
+ """Check if network operations are allowed in current context."""
1864
+ try:
1865
+ from .integration import CapabilityChecker
1866
+ if not CapabilityChecker.check_network():
1867
+ return EvaluationError("Network access denied: missing 'network.tcp' capability")
1868
+ except Exception:
1869
+ pass # If capability system isn't loaded, allow by default
1870
+ return None
1871
+
1872
+ def _check_io_read_capability():
1873
+ """Check if file read operations are allowed."""
1874
+ try:
1875
+ from .integration import CapabilityChecker
1876
+ if not CapabilityChecker.check_io_read():
1877
+ return EvaluationError("File read access denied: missing 'io.read' capability")
1878
+ except Exception:
1879
+ pass
1880
+ return None
1881
+
1882
+ def _check_io_write_capability():
1883
+ """Check if file write operations are allowed."""
1884
+ try:
1885
+ from .integration import CapabilityChecker
1886
+ if not CapabilityChecker.check_io_write():
1887
+ return EvaluationError("File write access denied: missing 'io.write' capability")
1888
+ except Exception:
1889
+ pass
1890
+ return None
1891
+
1725
1892
  # HTTP Client
1726
1893
  def _http_get(*a):
1727
1894
  """HTTP GET request: http_get(url, headers?, timeout?)"""
1895
+ cap_err = _check_network_capability()
1896
+ if cap_err: return cap_err
1728
1897
  if len(a) < 1:
1729
1898
  return EvaluationError("http_get() requires at least 1 argument: url")
1730
1899
 
@@ -1750,6 +1919,8 @@ class FunctionEvaluatorMixin:
1750
1919
 
1751
1920
  def _http_post(*a):
1752
1921
  """HTTP POST request: http_post(url, data, headers?, timeout?)"""
1922
+ cap_err = _check_network_capability()
1923
+ if cap_err: return cap_err
1753
1924
  if len(a) < 2:
1754
1925
  return EvaluationError("http_post() requires at least 2 arguments: url, data")
1755
1926
 
@@ -1778,6 +1949,8 @@ class FunctionEvaluatorMixin:
1778
1949
 
1779
1950
  def _http_put(*a):
1780
1951
  """HTTP PUT request: http_put(url, data, headers?, timeout?)"""
1952
+ cap_err = _check_network_capability()
1953
+ if cap_err: return cap_err
1781
1954
  if len(a) < 2:
1782
1955
  return EvaluationError("http_put() requires at least 2 arguments: url, data")
1783
1956
 
@@ -1803,6 +1976,8 @@ class FunctionEvaluatorMixin:
1803
1976
 
1804
1977
  def _http_delete(*a):
1805
1978
  """HTTP DELETE request: http_delete(url, headers?, timeout?)"""
1979
+ cap_err = _check_network_capability()
1980
+ if cap_err: return cap_err
1806
1981
  if len(a) < 1:
1807
1982
  return EvaluationError("http_delete() requires at least 1 argument: url")
1808
1983
 
@@ -1823,6 +1998,62 @@ class FunctionEvaluatorMixin:
1823
1998
  return _python_to_zexus(result, mark_untrusted=True)
1824
1999
  except Exception as e:
1825
2000
  return EvaluationError(f"HTTP DELETE error: {str(e)}")
2001
+
2002
+ def _http_request(*a):
2003
+ """Generic HTTP request: http_request(method, url, data?, headers?, timeout?)"""
2004
+ cap_err = _check_network_capability()
2005
+ if cap_err: return cap_err
2006
+ if len(a) < 2:
2007
+ return EvaluationError("http_request() requires at least 2 arguments: method, url")
2008
+ method_str = a[0].value if isinstance(a[0], String) else str(a[0])
2009
+ url = a[1].value if isinstance(a[1], String) else str(a[1])
2010
+ data = _zexus_to_python(a[2]) if len(a) >= 3 and a[2] != NULL else None
2011
+ headers = _zexus_to_python(a[3]) if len(a) >= 4 and isinstance(a[3], Map) else None
2012
+ timeout = a[4].value if len(a) >= 5 and isinstance(a[4], Integer) else 30
2013
+ try:
2014
+ from ..stdlib.http import HttpModule
2015
+ result = HttpModule.request(method_str, url, data, headers, timeout)
2016
+ return _python_to_zexus(result, mark_untrusted=True)
2017
+ except Exception as e:
2018
+ return EvaluationError(f"HTTP {method_str} error: {str(e)}")
2019
+
2020
+ def _http_parallel_get(*a):
2021
+ """Parallel GET requests: http_parallel_get([url1, url2, ...], headers?, timeout?)"""
2022
+ cap_err = _check_network_capability()
2023
+ if cap_err: return cap_err
2024
+ if len(a) < 1 or not isinstance(a[0], List):
2025
+ return EvaluationError("http_parallel_get() requires a list of URLs as first argument")
2026
+ urls = [e.value if isinstance(e, String) else str(e) for e in a[0].elements]
2027
+ headers = _zexus_to_python(a[1]) if len(a) >= 2 and isinstance(a[1], Map) else None
2028
+ timeout = a[2].value if len(a) >= 3 and isinstance(a[2], Integer) else 30
2029
+ try:
2030
+ from ..stdlib.http import HttpModule
2031
+ results = HttpModule.parallel_get(urls, headers, timeout)
2032
+ return List([_python_to_zexus(r, mark_untrusted=True) for r in results])
2033
+ except Exception as e:
2034
+ return EvaluationError(f"http_parallel_get() error: {str(e)}")
2035
+
2036
+ def _http_async_get(*a):
2037
+ """Non-blocking HTTP GET: http_async_get(url, headers?, timeout?) -> Future"""
2038
+ cap_err = _check_network_capability()
2039
+ if cap_err: return cap_err
2040
+ if len(a) < 1:
2041
+ return EvaluationError("http_async_get() requires at least 1 argument: url")
2042
+ url = a[0].value if isinstance(a[0], String) else str(a[0])
2043
+ headers = _zexus_to_python(a[1]) if len(a) >= 2 and isinstance(a[1], Map) else None
2044
+ timeout = a[2].value if len(a) >= 3 and isinstance(a[2], Integer) else 30
2045
+ try:
2046
+ from ..stdlib.http import HttpModule
2047
+ future = HttpModule.async_get(url, headers, timeout)
2048
+ # Wrap future so await_result() works from Zexus
2049
+ from ..object import Map as ZMap
2050
+ return ZMap({
2051
+ "type": String("HttpFuture"),
2052
+ "done": Builtin(lambda *_: BooleanObj(future.done()), "done"),
2053
+ "result": Builtin(lambda *_: _python_to_zexus(future.result(), mark_untrusted=True), "result"),
2054
+ })
2055
+ except Exception as e:
2056
+ return EvaluationError(f"http_async_get() error: {str(e)}")
1826
2057
 
1827
2058
  # Debug
1828
2059
  def _debug(*a):
@@ -1935,6 +2166,22 @@ class FunctionEvaluatorMixin:
1935
2166
  if isinstance(arg, String):
1936
2167
  return String(arg.value.lower())
1937
2168
  return EvaluationError(f"lowercase() requires a string argument")
2169
+
2170
+ def _split(*a):
2171
+ """Split string by delimiter"""
2172
+ if len(a) != 2:
2173
+ return EvaluationError(f"split() takes 2 args: string, delimiter")
2174
+
2175
+ str_obj = a[0]
2176
+ delim_obj = a[1]
2177
+
2178
+ if not isinstance(str_obj, String):
2179
+ return EvaluationError("split() first argument must be a string")
2180
+ if not isinstance(delim_obj, String):
2181
+ return EvaluationError("split() second argument must be a delimiter string")
2182
+
2183
+ parts = str_obj.value.split(delim_obj.value)
2184
+ return List([String(p) for p in parts])
1938
2185
 
1939
2186
  def _random(*a):
1940
2187
  """Generate random number. random() -> 0-1, random(max) -> 0 to max-1"""
@@ -2136,6 +2383,106 @@ class FunctionEvaluatorMixin:
2136
2383
  a[0].extend(a[1])
2137
2384
  return a[0]
2138
2385
 
2386
+ def _sort(*a):
2387
+ """Sort a list: sort(list) or sort(list, key_field) for maps
2388
+
2389
+ - sort([3, 1, 2]) -> [1, 2, 3]
2390
+ - sort([{fee: 5}, {fee: 3}], "fee") -> [{fee: 3}, {fee: 5}]
2391
+ - sort(list, "fee", true) -> descending order
2392
+ """
2393
+ if len(a) < 1:
2394
+ return EvaluationError("sort() takes 1-3 arguments: sort(list, [key], [descending])")
2395
+ if not isinstance(a[0], List):
2396
+ return EvaluationError("sort() first argument must be a list")
2397
+
2398
+ lst = a[0]
2399
+ key_field = None
2400
+ descending = False
2401
+
2402
+ if len(a) >= 2:
2403
+ if isinstance(a[1], String):
2404
+ key_field = a[1].value
2405
+ elif isinstance(a[1], BooleanObj):
2406
+ descending = a[1].value
2407
+ else:
2408
+ return EvaluationError("sort() second argument must be a key string or boolean for descending")
2409
+
2410
+ if len(a) >= 3:
2411
+ if isinstance(a[2], BooleanObj):
2412
+ descending = a[2].value
2413
+ else:
2414
+ return EvaluationError("sort() third argument must be a boolean for descending order")
2415
+
2416
+ try:
2417
+ # Make a copy of elements for non-destructive sort
2418
+ elements = list(lst.elements)
2419
+
2420
+ def get_sort_key(elem):
2421
+ if key_field is not None:
2422
+ # Sort by field in map
2423
+ if isinstance(elem, Map):
2424
+ # Try string key first
2425
+ val = elem.pairs.get(String(key_field))
2426
+ if val is None:
2427
+ # Try with key_field as-is (for internal dict keys)
2428
+ val = elem.pairs.get(key_field)
2429
+ if val is None:
2430
+ return 0 # Default for missing key
2431
+ if isinstance(val, (Integer, Float)):
2432
+ return val.value
2433
+ if isinstance(val, String):
2434
+ return val.value
2435
+ return 0
2436
+ elif isinstance(elem, dict):
2437
+ val = elem.get(key_field, 0)
2438
+ if hasattr(val, 'value'):
2439
+ return val.value
2440
+ return val
2441
+ return 0
2442
+ else:
2443
+ # Direct sort
2444
+ if isinstance(elem, (Integer, Float)):
2445
+ return elem.value
2446
+ if isinstance(elem, String):
2447
+ return elem.value
2448
+ return 0
2449
+
2450
+ elements.sort(key=get_sort_key, reverse=descending)
2451
+ return List(elements)
2452
+ except Exception as e:
2453
+ return EvaluationError(f"sort() error: {str(e)}")
2454
+
2455
+ def _slice(*a):
2456
+ """Get a slice of a list: slice(list, start, end?) -> list
2457
+
2458
+ - slice([1, 2, 3, 4], 1) -> [2, 3, 4] (from index 1 to end)
2459
+ - slice([1, 2, 3, 4], 1, 3) -> [2, 3] (from index 1 to 3, exclusive)
2460
+ - slice([1, 2, 3, 4], 0, 2) -> [1, 2] (first 2 elements)
2461
+ """
2462
+ if len(a) < 2:
2463
+ return EvaluationError("slice() takes 2-3 arguments: slice(list, start, [end])")
2464
+ if not isinstance(a[0], List):
2465
+ return EvaluationError("slice() first argument must be a list")
2466
+ if not isinstance(a[1], Integer):
2467
+ return EvaluationError("slice() start must be an integer")
2468
+
2469
+ lst = a[0]
2470
+ start = a[1].value
2471
+ end = len(lst.elements) # Default: to the end
2472
+
2473
+ if len(a) >= 3:
2474
+ if not isinstance(a[2], Integer):
2475
+ return EvaluationError("slice() end must be an integer")
2476
+ end = a[2].value
2477
+
2478
+ # Handle negative indices
2479
+ if start < 0:
2480
+ start = max(0, len(lst.elements) + start)
2481
+ if end < 0:
2482
+ end = max(0, len(lst.elements) + end)
2483
+
2484
+ return List(lst.elements[start:end])
2485
+
2139
2486
  def _reduce(*a):
2140
2487
  if len(a) < 2:
2141
2488
  return EvaluationError("reduce(arr, fn, [init])")
@@ -2151,6 +2498,28 @@ class FunctionEvaluatorMixin:
2151
2498
  return EvaluationError("filter(arr, fn)")
2152
2499
  return self._array_filter(a[0], a[1])
2153
2500
 
2501
+ def _vfs_stats(*a):
2502
+ """Return VFS file cache statistics as a Map: vfs_stats()"""
2503
+ mgr = _get_vfs_manager()
2504
+ if mgr is None:
2505
+ return Map({"error": String("VFS not initialized")})
2506
+ stats = mgr.file_cache.stats()
2507
+ return Map({
2508
+ "entries": Integer(stats["entries"]),
2509
+ "bytes": Integer(stats["bytes"]),
2510
+ "hits": Integer(stats["hits"]),
2511
+ "misses": Integer(stats["misses"]),
2512
+ "hit_rate": Float(round(stats["hit_rate"], 4)),
2513
+ })
2514
+
2515
+ def _vfs_clear_cache(*a):
2516
+ """Flush the VFS file content cache: vfs_clear_cache()"""
2517
+ mgr = _get_vfs_manager()
2518
+ if mgr is None:
2519
+ return NULL
2520
+ mgr.file_cache.clear()
2521
+ return NULL
2522
+
2154
2523
  # File object creation (for RAII using statements)
2155
2524
  def _file(*a):
2156
2525
  if len(a) == 0 or len(a) > 2:
@@ -2170,22 +2539,18 @@ class FunctionEvaluatorMixin:
2170
2539
  return EvaluationError(f"file() error: {str(e)}")
2171
2540
 
2172
2541
  def _read_file(*a):
2173
- """Read entire file contents as string"""
2542
+ """Read entire file contents as string (VFS-cached)"""
2543
+ cap_err = _check_io_read_capability()
2544
+ if cap_err: return cap_err
2174
2545
  if len(a) != 1:
2175
2546
  return EvaluationError("read_file() takes exactly 1 argument: path")
2176
2547
  if not isinstance(a[0], String):
2177
2548
  return EvaluationError("read_file() path must be a string")
2178
2549
 
2179
- import os
2180
- path = a[0].value
2181
-
2182
- # Normalize path
2183
- if not os.path.isabs(path):
2184
- path = os.path.join(os.getcwd(), path)
2550
+ path = _resolve_read_path(a[0].value)
2185
2551
 
2186
2552
  try:
2187
- with open(path, 'r') as f:
2188
- content = f.read()
2553
+ content = _cached_read(path)
2189
2554
  return String(content)
2190
2555
  except FileNotFoundError:
2191
2556
  return EvaluationError(f"File not found: {path}")
@@ -2451,8 +2816,13 @@ class FunctionEvaluatorMixin:
2451
2816
  "http_post": Builtin(_http_post, "http_post"),
2452
2817
  "http_put": Builtin(_http_put, "http_put"),
2453
2818
  "http_delete": Builtin(_http_delete, "http_delete"),
2819
+ "http_request": Builtin(_http_request, "http_request"),
2820
+ "http_parallel_get": Builtin(_http_parallel_get, "http_parallel_get"),
2821
+ "http_async_get": Builtin(_http_async_get, "http_async_get"),
2454
2822
  "read_file": Builtin(_read_file, "read_file"),
2455
2823
  "eval_file": Builtin(_eval_file, "eval_file"),
2824
+ "vfs_stats": Builtin(_vfs_stats, "vfs_stats"),
2825
+ "vfs_clear_cache": Builtin(_vfs_clear_cache, "vfs_clear_cache"),
2456
2826
  "debug": Builtin(_debug, "debug"),
2457
2827
  "debug_log": Builtin(_debug_log, "debug_log"),
2458
2828
  "debug_trace": Builtin(_debug_trace, "debug_trace"),
@@ -2461,6 +2831,7 @@ class FunctionEvaluatorMixin:
2461
2831
  "float": Builtin(_float, "float"),
2462
2832
  "uppercase": Builtin(_uppercase, "uppercase"),
2463
2833
  "lowercase": Builtin(_lowercase, "lowercase"),
2834
+ "split": Builtin(_split, "split"),
2464
2835
  "random": Builtin(_random, "random"),
2465
2836
  "persist_set": Builtin(_persist_set, "persist_set"),
2466
2837
  "persist_get": Builtin(_persist_get, "persist_get"),
@@ -2472,6 +2843,8 @@ class FunctionEvaluatorMixin:
2472
2843
  "push": Builtin(_push, "push"),
2473
2844
  "append": Builtin(_append, "append"), # Mutating list append
2474
2845
  "extend": Builtin(_extend, "extend"), # Mutating list extend
2846
+ "sort": Builtin(_sort, "sort"), # Sort list or list of maps by key
2847
+ "slice": Builtin(_slice, "slice"), # Get list slice: slice(list, start, end?)
2475
2848
  "reduce": Builtin(_reduce, "reduce"),
2476
2849
  "map": Builtin(_map, "map"),
2477
2850
  "filter": Builtin(_filter, "filter"),
@@ -3157,6 +3530,32 @@ class FunctionEvaluatorMixin:
3157
3530
  String("limit"): Integer(tx.gas_limit)
3158
3531
  })
3159
3532
 
3533
+ # generateKeypair(algorithm?)
3534
+ def _generate_keypair(*a):
3535
+ algorithm = a[0].value if len(a) > 0 and hasattr(a[0], 'value') else 'ECDSA'
3536
+ try:
3537
+ private_key, public_key = CryptoPlugin.generate_keypair(algorithm)
3538
+ return Map({
3539
+ String('private_key'): String(private_key),
3540
+ String('public_key'): String(public_key)
3541
+ })
3542
+ except Exception as e:
3543
+ return EvaluationError(f"Keypair generation error: {str(e)}")
3544
+
3545
+ # deriveAddress(public_key, [prefix])
3546
+ def _derive_address(*a):
3547
+ if len(a) < 1 or len(a) > 2:
3548
+ return EvaluationError("deriveAddress() expects 1 or 2 arguments: public_key, [prefix]")
3549
+ public_key = a[0].value if hasattr(a[0], 'value') else str(a[0])
3550
+ prefix = None
3551
+ if len(a) > 1:
3552
+ prefix = a[1].value if hasattr(a[1], 'value') else str(a[1])
3553
+ try:
3554
+ result = CryptoPlugin.derive_address(public_key, prefix=prefix)
3555
+ return String(result)
3556
+ except Exception as e:
3557
+ return EvaluationError(f"Address derivation error: {str(e)}")
3558
+
3160
3559
  self.builtins.update({
3161
3560
  "hash": Builtin(_hash, "hash"),
3162
3561
  "keccak256": Builtin(_keccak256, "keccak256"),
@@ -3164,6 +3563,8 @@ class FunctionEvaluatorMixin:
3164
3563
  "verify_sig": Builtin(_verify_sig, "verify_sig"),
3165
3564
  "tx": Builtin(_tx, "tx"),
3166
3565
  "gas": Builtin(_gas, "gas"),
3566
+ "generateKeypair": Builtin(_generate_keypair, "generateKeypair"),
3567
+ "deriveAddress": Builtin(_derive_address, "deriveAddress"),
3167
3568
  })
3168
3569
 
3169
3570
  # Register advanced feature builtins