zexus 1.6.7 → 1.7.1
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.
- package/README.md +12 -5
- package/package.json +1 -1
- package/src/zexus/__init__.py +1 -1
- package/src/zexus/capability_system.py +184 -9
- package/src/zexus/cli/main.py +130 -8
- package/src/zexus/cli/zpm.py +1 -1
- package/src/zexus/compiler/bytecode.py +124 -7
- package/src/zexus/compiler/compat_runtime.py +6 -2
- package/src/zexus/compiler/lexer.py +6 -0
- package/src/zexus/compiler/parser.py +108 -7
- package/src/zexus/compiler/semantic.py +18 -19
- package/src/zexus/compiler/zexus_ast.py +26 -1
- package/src/zexus/environment.py +103 -9
- package/src/zexus/evaluator/bytecode_compiler.py +312 -12
- package/src/zexus/evaluator/core.py +239 -5
- package/src/zexus/evaluator/expressions.py +236 -13
- package/src/zexus/evaluator/functions.py +141 -4
- package/src/zexus/evaluator/jit_integration.py +346 -0
- package/src/zexus/evaluator/resource_limiter.py +4 -4
- package/src/zexus/evaluator/statements.py +281 -12
- package/src/zexus/evaluator/unified_execution.py +488 -0
- package/src/zexus/evaluator/utils.py +4 -1
- package/src/zexus/evaluator_original.py +1 -1
- package/src/zexus/lexer.py +88 -22
- package/src/zexus/module_cache.py +2 -0
- package/src/zexus/module_manager.py +129 -1
- package/src/zexus/object.py +55 -0
- package/src/zexus/parser/parser.py +610 -142
- package/src/zexus/parser/strategy_context.py +748 -13
- package/src/zexus/parser/strategy_structural.py +250 -4
- package/src/zexus/renderer/__init__.py +46 -0
- package/src/zexus/renderer/backend.py +261 -0
- package/src/zexus/renderer/canvas.py +78 -0
- package/src/zexus/renderer/color_system.py +201 -0
- package/src/zexus/renderer/graphics.py +31 -0
- package/src/zexus/renderer/layout.py +222 -0
- package/src/zexus/renderer/main_renderer.py +66 -0
- package/src/zexus/renderer/painter.py +30 -0
- package/src/zexus/runtime/__init__.py +10 -2
- package/src/zexus/runtime/load_manager.py +368 -0
- package/src/zexus/safety/__init__.py +42 -0
- package/src/zexus/safety/memory_safety.py +499 -0
- package/src/zexus/security.py +411 -80
- package/src/zexus/vm/async_optimizer.py +66 -5
- package/src/zexus/vm/bytecode.py +45 -16
- package/src/zexus/vm/cache.py +4 -0
- package/src/zexus/vm/compiler.py +476 -0
- package/src/zexus/vm/gas_metering.py +235 -0
- package/src/zexus/vm/jit.py +281 -18
- package/src/zexus/vm/parallel_vm.py +22 -3
- package/src/zexus/vm/vm.py +695 -77
- package/src/zexus/zexus_ast.py +74 -0
- package/src/zexus/zexus_token.py +3 -0
- package/src/zexus/zpm/package_manager.py +1 -1
- package/src/zexus.egg-info/PKG-INFO +13 -6
- package/src/zexus.egg-info/SOURCES.txt +108 -11
package/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](https://python.org)
|
|
8
8
|
[](https://github.com/Zaidux/zexus-interpreter)
|
|
9
9
|
|
|
10
10
|
**A modern, security-first programming language with built-in blockchain support, VM-accelerated execution, advanced memory management, and policy-as-code**
|
|
11
11
|
|
|
12
|
-
[What's New](#-whats-new-in-
|
|
12
|
+
[What's New](#-whats-new-in-v171) • [Features](#-key-features) • [Installation](#-installation) • [Quick Start](#-quick-start) • [Keywords](#-complete-keyword-reference) • [Documentation](#-documentation) • [Examples](#-examples) • [Troubleshooting](#-getting-help--troubleshooting)
|
|
13
13
|
|
|
14
14
|
</div>
|
|
15
15
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
## 📋 Table of Contents
|
|
19
19
|
|
|
20
20
|
- [What is Zexus?](#-what-is-zexus)
|
|
21
|
-
- [What's New](#-whats-new-in-
|
|
21
|
+
- [What's New](#-whats-new-in-v171)
|
|
22
22
|
- [Key Features](#-key-features)
|
|
23
23
|
- [VM-Accelerated Performance](#-vm-accelerated-performance-new)
|
|
24
24
|
- [Security & Policy-as-Code](#-security--policy-as-code--verify-enhanced)
|
|
@@ -67,9 +67,16 @@ Zexus is a next-generation, general-purpose programming language designed for se
|
|
|
67
67
|
|
|
68
68
|
---
|
|
69
69
|
|
|
70
|
-
## 🎉 What's New in v1.
|
|
70
|
+
## 🎉 What's New in v1.7.1
|
|
71
71
|
|
|
72
|
-
### Latest Features (v1.
|
|
72
|
+
### Latest Features (v1.7.1)
|
|
73
|
+
|
|
74
|
+
✅ **FIND Keyword** - Declarative project search that resolves exact module paths with scope filtering and smart suggestions
|
|
75
|
+
✅ **LOAD Keyword & Manager** - Provider-aware configuration loader with built-in ENV, JSON, and YAML support plus caching
|
|
76
|
+
✅ **VM + Bytecode Support** - FIND/LOAD now compile to bytecode with helper bridges so scripts run identically in the VM and interpreter
|
|
77
|
+
✅ **Targeted Test Coverage** - Added regression tests that exercise both interpreter and VM paths for FIND/LOAD workflows
|
|
78
|
+
|
|
79
|
+
### Previous Features (v1.6.3)
|
|
73
80
|
|
|
74
81
|
✅ **Complete Database Ecosystem** - Production-ready database drivers
|
|
75
82
|
✅ **4 Database Drivers** - SQLite, PostgreSQL, MySQL, MongoDB fully tested
|
package/package.json
CHANGED
package/src/zexus/__init__.py
CHANGED
|
@@ -5,7 +5,8 @@ Implements fine-grained access control through capability tokens.
|
|
|
5
5
|
Plugins declare required capabilities, and the evaluator enforces access.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from typing import Set, Dict, List, Callable, Optional, Tuple
|
|
8
|
+
from typing import Set, Dict, List, Callable, Optional, Tuple, Any
|
|
9
|
+
import uuid
|
|
9
10
|
from dataclasses import dataclass, field
|
|
10
11
|
from enum import Enum
|
|
11
12
|
import time
|
|
@@ -173,6 +174,8 @@ class CapabilityManager:
|
|
|
173
174
|
self.audit_log = CapabilityAuditLog()
|
|
174
175
|
self.granted_capabilities: Dict[str, Set[str]] = {} # requester -> capabilities
|
|
175
176
|
self.required_capabilities: Dict[str, Set[str]] = {} # requester -> required caps
|
|
177
|
+
self._contexts: Dict[str, "CapabilityContext"] = {}
|
|
178
|
+
self._context_counter = 0
|
|
176
179
|
|
|
177
180
|
# Initialize with base capabilities
|
|
178
181
|
for cap in self.BASE_CAPABILITIES:
|
|
@@ -197,6 +200,19 @@ class CapabilityManager:
|
|
|
197
200
|
for cap in capabilities:
|
|
198
201
|
self.grant_capability(requester, cap)
|
|
199
202
|
|
|
203
|
+
def revoke_capability(self, requester: str, capability: str):
|
|
204
|
+
"""Revoke a specific capability from a requester."""
|
|
205
|
+
caps = self.granted_capabilities.get(requester)
|
|
206
|
+
if not caps:
|
|
207
|
+
return
|
|
208
|
+
caps.discard(capability)
|
|
209
|
+
if not caps:
|
|
210
|
+
self.granted_capabilities.pop(requester, None)
|
|
211
|
+
|
|
212
|
+
def revoke_all_capabilities(self, requester: str):
|
|
213
|
+
"""Remove all capabilities granted to a requester."""
|
|
214
|
+
self.granted_capabilities.pop(requester, None)
|
|
215
|
+
|
|
200
216
|
def check_capability(self, requester: str, capability: str,
|
|
201
217
|
context: Optional[Dict] = None) -> Tuple[bool, str]:
|
|
202
218
|
"""
|
|
@@ -210,29 +226,49 @@ class CapabilityManager:
|
|
|
210
226
|
requester=requester,
|
|
211
227
|
context=context or {}
|
|
212
228
|
)
|
|
213
|
-
|
|
214
|
-
|
|
229
|
+
|
|
230
|
+
context_obj = self._contexts.get(requester)
|
|
231
|
+
|
|
232
|
+
# 1. Context-specific policy enforcement (takes precedence)
|
|
233
|
+
if context_obj and context_obj.policy:
|
|
234
|
+
policy_level = context_obj.policy.check(capability)
|
|
235
|
+
if policy_level in (CapabilityLevel.ALLOWED, CapabilityLevel.UNRESTRICTED, CapabilityLevel.RESTRICTED):
|
|
236
|
+
reason = (
|
|
237
|
+
f"Capability {capability} allowed by context policy "
|
|
238
|
+
f"'{context_obj.policy.name}'"
|
|
239
|
+
)
|
|
240
|
+
self.audit_log.log_request(request, True, reason)
|
|
241
|
+
return True, reason
|
|
242
|
+
if policy_level == CapabilityLevel.DENY:
|
|
243
|
+
reason = (
|
|
244
|
+
f"Capability {capability} denied by context policy "
|
|
245
|
+
f"'{context_obj.policy.name}'"
|
|
246
|
+
)
|
|
247
|
+
self.audit_log.log_request(request, False, reason)
|
|
248
|
+
return False, reason
|
|
249
|
+
|
|
250
|
+
# 2. Check if policy allows all (AllowAllPolicy)
|
|
215
251
|
if isinstance(self.policy, AllowAllPolicy):
|
|
216
252
|
reason = f"Capability {capability} allowed by policy (allow-all)"
|
|
217
253
|
self.audit_log.log_request(request, True, reason)
|
|
218
254
|
return True, reason
|
|
219
|
-
|
|
220
|
-
#
|
|
255
|
+
|
|
256
|
+
# 3. Check if capability is base capability (always available)
|
|
221
257
|
if capability in self.BASE_CAPABILITIES:
|
|
222
258
|
reason = f"Base capability {capability} available"
|
|
223
259
|
self.audit_log.log_request(request, True, reason)
|
|
224
260
|
return True, reason
|
|
225
|
-
|
|
226
|
-
#
|
|
261
|
+
|
|
262
|
+
# 4. Check if requester has been explicitly granted this capability
|
|
227
263
|
if requester in self.granted_capabilities:
|
|
228
264
|
if capability in self.granted_capabilities[requester]:
|
|
229
265
|
reason = f"Capability {capability} granted to {requester}"
|
|
230
266
|
self.audit_log.log_request(request, True, reason)
|
|
231
267
|
return True, reason
|
|
232
268
|
|
|
233
|
-
#
|
|
269
|
+
# 5. Check if capability is allowed by policy
|
|
234
270
|
policy_level = self.policy.check(capability)
|
|
235
|
-
if policy_level
|
|
271
|
+
if policy_level in (CapabilityLevel.ALLOWED, CapabilityLevel.UNRESTRICTED, CapabilityLevel.RESTRICTED):
|
|
236
272
|
reason = f"Capability {capability} allowed by policy"
|
|
237
273
|
self.audit_log.log_request(request, True, reason)
|
|
238
274
|
return True, reason
|
|
@@ -291,6 +327,97 @@ class CapabilityManager:
|
|
|
291
327
|
"""Get audit statistics."""
|
|
292
328
|
return self.audit_log.get_statistics()
|
|
293
329
|
|
|
330
|
+
def create_context(
|
|
331
|
+
self,
|
|
332
|
+
*,
|
|
333
|
+
capabilities: Optional[List[str]] = None,
|
|
334
|
+
policy: Optional[CapabilityPolicy] = None,
|
|
335
|
+
name: Optional[str] = None,
|
|
336
|
+
inherit_base: bool = True,
|
|
337
|
+
) -> "CapabilityContext":
|
|
338
|
+
"""Create a scoped capability context."""
|
|
339
|
+
self._context_counter += 1
|
|
340
|
+
context_name = name or f"context::{self._context_counter}:{uuid.uuid4().hex[:8]}"
|
|
341
|
+
context = CapabilityContext(
|
|
342
|
+
manager=self,
|
|
343
|
+
name=context_name,
|
|
344
|
+
capabilities=capabilities or [],
|
|
345
|
+
policy=policy,
|
|
346
|
+
inherit_base=inherit_base,
|
|
347
|
+
)
|
|
348
|
+
self._contexts[context.name] = context
|
|
349
|
+
return context
|
|
350
|
+
|
|
351
|
+
def destroy_context(self, name: str) -> Optional["CapabilityContext"]:
|
|
352
|
+
"""Destroy a previously created context and revoke its grants."""
|
|
353
|
+
context = self._contexts.pop(name, None)
|
|
354
|
+
if context:
|
|
355
|
+
context.destroy()
|
|
356
|
+
return context
|
|
357
|
+
|
|
358
|
+
def get_context(self, name: str) -> Optional["CapabilityContext"]:
|
|
359
|
+
"""Retrieve a context by name, if it exists."""
|
|
360
|
+
return self._contexts.get(name)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class CapabilityContext:
|
|
364
|
+
"""Lightweight wrapper for scoped capability enforcement."""
|
|
365
|
+
|
|
366
|
+
def __init__(
|
|
367
|
+
self,
|
|
368
|
+
*,
|
|
369
|
+
manager: CapabilityManager,
|
|
370
|
+
name: str,
|
|
371
|
+
capabilities: Optional[List[str]] = None,
|
|
372
|
+
policy: Optional[CapabilityPolicy] = None,
|
|
373
|
+
inherit_base: bool = True,
|
|
374
|
+
) -> None:
|
|
375
|
+
self.manager = manager
|
|
376
|
+
self.name = name
|
|
377
|
+
self.policy = policy or SelectivePolicy(capabilities or [])
|
|
378
|
+
self.capabilities: Set[str] = set(capabilities or [])
|
|
379
|
+
self.inherit_base = inherit_base
|
|
380
|
+
self.created_at = time.time()
|
|
381
|
+
|
|
382
|
+
if self.capabilities:
|
|
383
|
+
self.manager.grant_capabilities(self.name, list(self.capabilities))
|
|
384
|
+
elif inherit_base:
|
|
385
|
+
# Ensure entry exists so future grants can be tracked cleanly
|
|
386
|
+
self.manager.granted_capabilities.setdefault(self.name, set())
|
|
387
|
+
|
|
388
|
+
def grant(self, capability: str) -> None:
|
|
389
|
+
"""Grant an additional capability to this context."""
|
|
390
|
+
self.manager.grant_capability(self.name, capability)
|
|
391
|
+
self.capabilities.add(capability)
|
|
392
|
+
|
|
393
|
+
def revoke(self, capability: str) -> None:
|
|
394
|
+
"""Revoke a capability from this context."""
|
|
395
|
+
self.manager.revoke_capability(self.name, capability)
|
|
396
|
+
self.capabilities.discard(capability)
|
|
397
|
+
|
|
398
|
+
def check(self, capability: str, *, context: Optional[Dict] = None) -> bool:
|
|
399
|
+
"""Check if the context can access a capability."""
|
|
400
|
+
allowed, _ = self.manager.check_capability(self.name, capability, context)
|
|
401
|
+
return allowed
|
|
402
|
+
|
|
403
|
+
def require(self, capability: str, *, context: Optional[Dict] = None) -> bool:
|
|
404
|
+
"""Require a capability, raising if unavailable."""
|
|
405
|
+
self.manager.require_capability(self.name, capability, context)
|
|
406
|
+
return True
|
|
407
|
+
|
|
408
|
+
def snapshot(self) -> Dict[str, Any]:
|
|
409
|
+
"""Return a serializable snapshot of the context state."""
|
|
410
|
+
return {
|
|
411
|
+
"name": self.name,
|
|
412
|
+
"capabilities": self.manager.get_granted_capabilities(self.name),
|
|
413
|
+
"policy": getattr(self.policy, "name", None),
|
|
414
|
+
"created_at": self.created_at,
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
def destroy(self) -> None:
|
|
418
|
+
"""Revoke all privileges associated with this context."""
|
|
419
|
+
self.manager.revoke_all_capabilities(self.name)
|
|
420
|
+
|
|
294
421
|
|
|
295
422
|
class CapabilityError(Exception):
|
|
296
423
|
"""Exception raised when capability check fails."""
|
|
@@ -370,3 +497,51 @@ CAPABILITY_SETS = {
|
|
|
370
497
|
"description": "Full system access (privileged code)"
|
|
371
498
|
}
|
|
372
499
|
}
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
_DEFAULT_MANAGER: CapabilityManager = CapabilityManager()
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def get_capability_manager() -> CapabilityManager:
|
|
506
|
+
"""Return the global capability manager singleton."""
|
|
507
|
+
return _DEFAULT_MANAGER
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def set_capability_manager(manager: CapabilityManager) -> None:
|
|
511
|
+
"""Replace the global capability manager (primarily for tests)."""
|
|
512
|
+
global _DEFAULT_MANAGER
|
|
513
|
+
_DEFAULT_MANAGER = manager
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def reset_capability_manager(policy: Optional[CapabilityPolicy] = None) -> CapabilityManager:
|
|
517
|
+
"""Reset the global capability manager to a fresh instance."""
|
|
518
|
+
manager = CapabilityManager(default_policy=policy)
|
|
519
|
+
set_capability_manager(manager)
|
|
520
|
+
return manager
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def check_capability(capability: str, requester: str, *, context: Optional[Dict] = None) -> bool:
|
|
524
|
+
"""Convenience wrapper around the global manager's check."""
|
|
525
|
+
manager = get_capability_manager()
|
|
526
|
+
allowed, _ = manager.check_capability(requester, capability, context)
|
|
527
|
+
return allowed
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
__all__ = [
|
|
531
|
+
"Capability",
|
|
532
|
+
"CapabilityLevel",
|
|
533
|
+
"CapabilityPolicy",
|
|
534
|
+
"CapabilityManager",
|
|
535
|
+
"CapabilityContext",
|
|
536
|
+
"CapabilityError",
|
|
537
|
+
"AllowAllPolicy",
|
|
538
|
+
"DenyAllPolicy",
|
|
539
|
+
"SelectivePolicy",
|
|
540
|
+
"CapabilityAuditLog",
|
|
541
|
+
"CapabilityRequest",
|
|
542
|
+
"CAPABILITY_SETS",
|
|
543
|
+
"get_capability_manager",
|
|
544
|
+
"set_capability_manager",
|
|
545
|
+
"reset_capability_manager",
|
|
546
|
+
"check_capability",
|
|
547
|
+
]
|
package/src/zexus/cli/main.py
CHANGED
|
@@ -3,10 +3,66 @@ import click
|
|
|
3
3
|
import sys
|
|
4
4
|
import os
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
from rich.
|
|
9
|
-
from rich.
|
|
6
|
+
|
|
7
|
+
try: # Optional rich dependency for colorful CLI output
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.syntax import Syntax
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
from rich.table import Table
|
|
12
|
+
_RICH_AVAILABLE = True
|
|
13
|
+
except ModuleNotFoundError: # Fallback to minimal console utilities when rich is missing
|
|
14
|
+
_RICH_AVAILABLE = False
|
|
15
|
+
|
|
16
|
+
class Console: # pragma: no cover - straightforward fallback
|
|
17
|
+
def print(self, *args, **kwargs):
|
|
18
|
+
sep = kwargs.get("sep", " ")
|
|
19
|
+
end = kwargs.get("end", "\n")
|
|
20
|
+
text = sep.join(str(arg) for arg in args)
|
|
21
|
+
sys.stdout.write(text)
|
|
22
|
+
if end is not None:
|
|
23
|
+
sys.stdout.write(end)
|
|
24
|
+
|
|
25
|
+
def rule(self, title=""):
|
|
26
|
+
line = "-" * 40
|
|
27
|
+
if title:
|
|
28
|
+
line = f"{line} {title} {line}"
|
|
29
|
+
self.print(line)
|
|
30
|
+
|
|
31
|
+
class Syntax: # pragma: no cover - fallback keeps interface compatible
|
|
32
|
+
def __init__(self, code, *_args, **_kwargs):
|
|
33
|
+
self.code = code
|
|
34
|
+
|
|
35
|
+
def __str__(self):
|
|
36
|
+
return self.code
|
|
37
|
+
|
|
38
|
+
class Panel: # pragma: no cover
|
|
39
|
+
def __init__(self, renderable, title=None):
|
|
40
|
+
self.renderable = renderable
|
|
41
|
+
self.title = title
|
|
42
|
+
|
|
43
|
+
def __str__(self):
|
|
44
|
+
if self.title:
|
|
45
|
+
return f"[{self.title}] {self.renderable}"
|
|
46
|
+
return str(self.renderable)
|
|
47
|
+
|
|
48
|
+
class Table: # pragma: no cover
|
|
49
|
+
def __init__(self, **_kwargs):
|
|
50
|
+
self.columns = []
|
|
51
|
+
self.rows = []
|
|
52
|
+
|
|
53
|
+
def add_column(self, header, **_kwargs):
|
|
54
|
+
self.columns.append(header)
|
|
55
|
+
|
|
56
|
+
def add_row(self, *columns):
|
|
57
|
+
self.rows.append(columns)
|
|
58
|
+
|
|
59
|
+
def __str__(self):
|
|
60
|
+
lines = []
|
|
61
|
+
if self.columns:
|
|
62
|
+
lines.append(" | ".join(self.columns))
|
|
63
|
+
for row in self.rows:
|
|
64
|
+
lines.append(" | ".join(str(col) for col in row))
|
|
65
|
+
return "\n".join(lines)
|
|
10
66
|
|
|
11
67
|
# Import your existing modules - UPDATED IMPORTS
|
|
12
68
|
from ..lexer import Lexer
|
|
@@ -20,6 +76,9 @@ from ..hybrid_orchestrator import orchestrator
|
|
|
20
76
|
from ..config import config
|
|
21
77
|
# Import error handling
|
|
22
78
|
from ..error_reporter import get_error_reporter, ZexusError, print_error
|
|
79
|
+
# VM and Compiler for high-performance execution
|
|
80
|
+
from ..vm.vm import VM, VMMode
|
|
81
|
+
from ..vm.compiler import compile_ast_to_bytecode, UnsupportedNodeError
|
|
23
82
|
|
|
24
83
|
console = Console()
|
|
25
84
|
|
|
@@ -91,7 +150,7 @@ def show_all_commands():
|
|
|
91
150
|
console.print("\n[bold green]💡 Tip:[/bold green] Use 'zx <command> --help' for detailed command options\n")
|
|
92
151
|
|
|
93
152
|
@click.group(invoke_without_command=True)
|
|
94
|
-
@click.version_option(version="1.
|
|
153
|
+
@click.version_option(version="1.7.1", prog_name="Zexus")
|
|
95
154
|
@click.option('--syntax-style', type=click.Choice(['universal', 'tolerable', 'auto']),
|
|
96
155
|
default='auto', help='Syntax style to use (universal=strict, tolerable=flexible)')
|
|
97
156
|
@click.option('--advanced-parsing', is_flag=True, default=True,
|
|
@@ -133,8 +192,12 @@ def cli(ctx, syntax_style, advanced_parsing, execution_mode, debug, zexus):
|
|
|
133
192
|
@cli.command()
|
|
134
193
|
@click.argument('file', type=click.Path(exists=True))
|
|
135
194
|
@click.argument('args', nargs=-1) # Accept any number of additional arguments
|
|
195
|
+
@click.option('--use-vm', is_flag=True, default=True, help='Use VM for execution (default: enabled for performance)')
|
|
196
|
+
@click.option('--vm-mode', type=click.Choice(['auto', 'stack', 'register', 'parallel']),
|
|
197
|
+
default='auto', help='VM execution mode (auto=best performance)')
|
|
198
|
+
@click.option('--no-optimize', is_flag=True, default=False, help='Disable bytecode optimizations')
|
|
136
199
|
@click.pass_context
|
|
137
|
-
def run(ctx, file, args):
|
|
200
|
+
def run(ctx, file, args, use_vm, vm_mode, no_optimize):
|
|
138
201
|
"""Run a Zexus program with hybrid execution"""
|
|
139
202
|
# Register source for error reporting
|
|
140
203
|
error_reporter = get_error_reporter()
|
|
@@ -155,6 +218,8 @@ def run(ctx, file, args):
|
|
|
155
218
|
console.print(f"🔧 [bold blue]Execution mode:[/bold blue] {execution_mode}")
|
|
156
219
|
console.print(f"📝 [bold blue]Syntax style:[/bold blue] {syntax_style}")
|
|
157
220
|
console.print(f"🎯 [bold blue]Advanced parsing:[/bold blue] {'Enabled' if advanced_parsing else 'Disabled'}")
|
|
221
|
+
if use_vm:
|
|
222
|
+
console.print(f"⚡ [bold magenta]VM Mode:[/bold magenta] {vm_mode.upper()} | Optimizations: {'OFF' if no_optimize else 'ON'}")
|
|
158
223
|
|
|
159
224
|
# Auto-detect syntax style if needed
|
|
160
225
|
if syntax_style == 'auto':
|
|
@@ -221,8 +286,65 @@ def run(ctx, file, args):
|
|
|
221
286
|
pass # Unable to determine package name
|
|
222
287
|
env.set("__PACKAGE__", package_name)
|
|
223
288
|
|
|
224
|
-
#
|
|
225
|
-
|
|
289
|
+
# Execute based on mode
|
|
290
|
+
bytecode = None
|
|
291
|
+
fallback_reason = None
|
|
292
|
+
|
|
293
|
+
if use_vm:
|
|
294
|
+
# VM EXECUTION PATH (High Performance)
|
|
295
|
+
console.print("[dim]Compiling to bytecode...[/dim]", end="")
|
|
296
|
+
try:
|
|
297
|
+
bytecode = compile_ast_to_bytecode(program, optimize=not no_optimize)
|
|
298
|
+
console.print(" [green]done[/green]")
|
|
299
|
+
console.print(f"[dim]Bytecode: {len(bytecode.instructions)} instructions, {len(bytecode.constants)} constants[/dim]")
|
|
300
|
+
except UnsupportedNodeError as e:
|
|
301
|
+
console.print(" [yellow]unsupported[/yellow]")
|
|
302
|
+
console.print(f"[bold yellow]⚠️ VM fallback:[/bold yellow] {e}")
|
|
303
|
+
if ctx.obj.get('DEBUG'):
|
|
304
|
+
import traceback
|
|
305
|
+
traceback.print_exc()
|
|
306
|
+
fallback_reason = str(e)
|
|
307
|
+
except Exception as e:
|
|
308
|
+
console.print(" [red]failed[/red]")
|
|
309
|
+
console.print(f"[bold red]Bytecode compilation error:[/bold red] {str(e)}")
|
|
310
|
+
if ctx.obj.get('DEBUG'):
|
|
311
|
+
import traceback
|
|
312
|
+
traceback.print_exc()
|
|
313
|
+
fallback_reason = str(e)
|
|
314
|
+
|
|
315
|
+
if bytecode is not None and fallback_reason is None:
|
|
316
|
+
vm_mode_enum = {
|
|
317
|
+
'auto': VMMode.AUTO,
|
|
318
|
+
'stack': VMMode.STACK,
|
|
319
|
+
'register': VMMode.REGISTER,
|
|
320
|
+
'parallel': VMMode.PARALLEL
|
|
321
|
+
}[vm_mode]
|
|
322
|
+
|
|
323
|
+
console.print(f"[dim]Initializing VM ({vm_mode} mode)...[/dim]", end="")
|
|
324
|
+
vm = VM(
|
|
325
|
+
mode=vm_mode_enum,
|
|
326
|
+
use_jit=not no_optimize,
|
|
327
|
+
max_heap_mb=1000, # 1GB heap limit
|
|
328
|
+
debug=ctx.obj.get('DEBUG', False)
|
|
329
|
+
)
|
|
330
|
+
console.print(" [green]done[/green]")
|
|
331
|
+
|
|
332
|
+
console.print("[dim]Executing on VM...[/dim]")
|
|
333
|
+
try:
|
|
334
|
+
result = vm.execute(bytecode, debug=ctx.obj.get('DEBUG', False))
|
|
335
|
+
except Exception as e:
|
|
336
|
+
console.print(f"[bold red]VM Execution error:[/bold red] {str(e)}")
|
|
337
|
+
if ctx.obj.get('DEBUG'):
|
|
338
|
+
import traceback
|
|
339
|
+
traceback.print_exc()
|
|
340
|
+
fallback_reason = str(e)
|
|
341
|
+
|
|
342
|
+
if fallback_reason is not None:
|
|
343
|
+
console.print("[bold yellow]🔄 Falling back to interpreter execution...[/bold yellow]")
|
|
344
|
+
result = evaluate(program, env, debug_mode=ctx.obj['DEBUG'])
|
|
345
|
+
elif bytecode is None:
|
|
346
|
+
# No bytecode generated but VM not requested or compile skipped
|
|
347
|
+
result = evaluate(program, env, debug_mode=ctx.obj['DEBUG'])
|
|
226
348
|
|
|
227
349
|
if result and hasattr(result, 'inspect') and result.inspect() != 'null':
|
|
228
350
|
console.print(f"\n✅ [bold green]Result:[/bold green] {result.inspect()}")
|
package/src/zexus/cli/zpm.py
CHANGED
|
@@ -23,7 +23,9 @@ from .zexus_ast import (
|
|
|
23
23
|
Program, LetStatement, ExpressionStatement, PrintStatement, ReturnStatement,
|
|
24
24
|
IfStatement, WhileStatement, Identifier, IntegerLiteral, StringLiteral,
|
|
25
25
|
Boolean as AST_Boolean, InfixExpression, PrefixExpression, CallExpression,
|
|
26
|
-
ActionStatement, BlockStatement, MapLiteral, ListLiteral, AwaitExpression
|
|
26
|
+
ActionStatement, BlockStatement, MapLiteral, ListLiteral, AwaitExpression,
|
|
27
|
+
EnumDeclaration, ImportStatement, EventDeclaration, ProtocolDeclaration,
|
|
28
|
+
EmitStatement
|
|
27
29
|
)
|
|
28
30
|
|
|
29
31
|
# --- Bytecode representation ---
|
|
@@ -47,12 +49,68 @@ class BytecodeGenerator:
|
|
|
47
49
|
|
|
48
50
|
def generate(self, program: Program) -> Bytecode:
|
|
49
51
|
self.bytecode = Bytecode()
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
statements = list(getattr(program, "statements", []) or [])
|
|
53
|
+
total = len(statements)
|
|
54
|
+
index = 0
|
|
55
|
+
while index < total:
|
|
56
|
+
stmt = statements[index]
|
|
57
|
+
|
|
58
|
+
# Pattern-match legacy import syntax: import "module" [as alias]
|
|
59
|
+
if (
|
|
60
|
+
isinstance(stmt, ExpressionStatement)
|
|
61
|
+
and isinstance(stmt.expression, Identifier)
|
|
62
|
+
and stmt.expression.value == "import"
|
|
63
|
+
):
|
|
64
|
+
module_value = None
|
|
65
|
+
alias_value = None
|
|
66
|
+
consumed = 1
|
|
67
|
+
|
|
68
|
+
if index + 1 < total:
|
|
69
|
+
module_stmt = statements[index + 1]
|
|
70
|
+
if isinstance(module_stmt, ExpressionStatement):
|
|
71
|
+
module_expr = getattr(module_stmt, "expression", None)
|
|
72
|
+
if isinstance(module_expr, StringLiteral):
|
|
73
|
+
module_value = module_expr.value
|
|
74
|
+
consumed = 2
|
|
75
|
+
elif isinstance(module_expr, Identifier):
|
|
76
|
+
module_value = module_expr.value
|
|
77
|
+
consumed = 2
|
|
78
|
+
|
|
79
|
+
if module_value is not None and index + consumed < total:
|
|
80
|
+
next_stmt = statements[index + consumed]
|
|
81
|
+
if (
|
|
82
|
+
isinstance(next_stmt, ExpressionStatement)
|
|
83
|
+
and isinstance(next_stmt.expression, Identifier)
|
|
84
|
+
and next_stmt.expression.value == "as"
|
|
85
|
+
and index + consumed + 1 < total
|
|
86
|
+
):
|
|
87
|
+
alias_stmt = statements[index + consumed + 1]
|
|
88
|
+
if isinstance(alias_stmt, ExpressionStatement) and isinstance(alias_stmt.expression, Identifier):
|
|
89
|
+
alias_value = alias_stmt.expression.value
|
|
90
|
+
consumed += 2
|
|
91
|
+
|
|
92
|
+
if module_value is not None:
|
|
93
|
+
import_node = ImportStatement(module_path=module_value, alias=alias_value)
|
|
94
|
+
self._emit_statement(
|
|
95
|
+
import_node,
|
|
96
|
+
self.bytecode,
|
|
97
|
+
is_top_level=True,
|
|
98
|
+
is_last=(index + consumed - 1 == total - 1),
|
|
99
|
+
)
|
|
100
|
+
index += consumed
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
self._emit_statement(
|
|
104
|
+
stmt,
|
|
105
|
+
self.bytecode,
|
|
106
|
+
is_top_level=True,
|
|
107
|
+
is_last=(index == total - 1),
|
|
108
|
+
)
|
|
109
|
+
index += 1
|
|
52
110
|
return self.bytecode
|
|
53
111
|
|
|
54
112
|
# Statement lowering
|
|
55
|
-
def _emit_statement(self, stmt, bc: Bytecode):
|
|
113
|
+
def _emit_statement(self, stmt, bc: Bytecode, *, is_top_level: bool = False, is_last: bool = False):
|
|
56
114
|
t = type(stmt).__name__
|
|
57
115
|
|
|
58
116
|
if t == "LetStatement":
|
|
@@ -64,8 +122,8 @@ class BytecodeGenerator:
|
|
|
64
122
|
|
|
65
123
|
if t == "ExpressionStatement":
|
|
66
124
|
self._emit_expression(stmt.expression, bc)
|
|
67
|
-
|
|
68
|
-
|
|
125
|
+
if not (is_top_level and is_last):
|
|
126
|
+
bc.add_instruction("POP", None)
|
|
69
127
|
return
|
|
70
128
|
|
|
71
129
|
if t == "PrintStatement":
|
|
@@ -125,7 +183,61 @@ class BytecodeGenerator:
|
|
|
125
183
|
bc.instructions[jump_pos] = ("JUMP_IF_FALSE", end_pos)
|
|
126
184
|
return
|
|
127
185
|
|
|
128
|
-
|
|
186
|
+
if t == "EnumDeclaration":
|
|
187
|
+
enum_name = getattr(stmt.name, "value", stmt.name)
|
|
188
|
+
members = getattr(stmt, "members", {}) or {}
|
|
189
|
+
resolved_members = {}
|
|
190
|
+
next_auto = 0
|
|
191
|
+
for key, explicit in members.items():
|
|
192
|
+
value = explicit if explicit is not None else next_auto
|
|
193
|
+
resolved_members[key] = value
|
|
194
|
+
next_auto = (value + 1) if explicit is not None else (next_auto + 1)
|
|
195
|
+
|
|
196
|
+
name_idx = bc.add_constant(enum_name)
|
|
197
|
+
members_idx = bc.add_constant(resolved_members)
|
|
198
|
+
bc.add_instruction("DEFINE_ENUM", (name_idx, members_idx))
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
if t == "ImportStatement":
|
|
202
|
+
module_idx = bc.add_constant(getattr(stmt, "module_path", None))
|
|
203
|
+
alias = getattr(stmt, "alias", None)
|
|
204
|
+
if alias is not None:
|
|
205
|
+
alias_idx = bc.add_constant(alias)
|
|
206
|
+
bc.add_instruction("IMPORT", (module_idx, alias_idx))
|
|
207
|
+
else:
|
|
208
|
+
bc.add_instruction("IMPORT", (module_idx,))
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
if t == "EventDeclaration":
|
|
212
|
+
event_name = getattr(stmt.name, "value", stmt.name)
|
|
213
|
+
name_idx = bc.add_constant(event_name)
|
|
214
|
+
bc.add_instruction("REGISTER_EVENT", name_idx)
|
|
215
|
+
return
|
|
216
|
+
|
|
217
|
+
if t == "ProtocolDeclaration":
|
|
218
|
+
proto_name = getattr(stmt.name, "value", stmt.name)
|
|
219
|
+
spec = getattr(stmt, "spec", {}) or {}
|
|
220
|
+
name_idx = bc.add_constant(proto_name)
|
|
221
|
+
spec_idx = bc.add_constant(spec)
|
|
222
|
+
bc.add_instruction("DEFINE_PROTOCOL", (name_idx, spec_idx))
|
|
223
|
+
return
|
|
224
|
+
|
|
225
|
+
if t == "EmitStatement":
|
|
226
|
+
event_name = getattr(stmt.name, "value", stmt.name)
|
|
227
|
+
name_idx = bc.add_constant(event_name)
|
|
228
|
+
if getattr(stmt, "payload", None) is not None:
|
|
229
|
+
self._emit_expression(stmt.payload, bc)
|
|
230
|
+
bc.add_instruction("EMIT_EVENT", (name_idx,))
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
if t == "StreamStatement":
|
|
234
|
+
# Streams are currently handled at runtime only; compiler no-op.
|
|
235
|
+
return
|
|
236
|
+
|
|
237
|
+
if t == "WatchStatement":
|
|
238
|
+
# Watch statements are runtime constructs; compiler emits no bytecode.
|
|
239
|
+
return
|
|
240
|
+
|
|
129
241
|
return
|
|
130
242
|
|
|
131
243
|
# Expression lowering
|
|
@@ -141,6 +253,11 @@ class BytecodeGenerator:
|
|
|
141
253
|
bc.add_instruction("LOAD_CONST", const_idx)
|
|
142
254
|
return
|
|
143
255
|
|
|
256
|
+
if typ == "FloatLiteral":
|
|
257
|
+
const_idx = bc.add_constant(float(expr.value))
|
|
258
|
+
bc.add_instruction("LOAD_CONST", const_idx)
|
|
259
|
+
return
|
|
260
|
+
|
|
144
261
|
if typ == "StringLiteral":
|
|
145
262
|
const_idx = bc.add_constant(expr.value)
|
|
146
263
|
bc.add_instruction("LOAD_CONST", const_idx)
|