zexus 1.6.3 → 1.6.4
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/package.json +1 -1
- package/src/zexus/__init__.py +1 -1
- package/src/zexus/cli/main.py +1 -1
- package/src/zexus/cli/zpm.py +1 -1
- package/src/zexus/evaluator/core.py +10 -19
- package/src/zexus/evaluator/functions.py +48 -21
- package/src/zexus/evaluator/statements.py +16 -9
- package/src/zexus/parser/parser.py +17 -3
- package/src/zexus/parser/strategy_context.py +54 -3
- package/src/zexus/security.py +43 -25
- package/src/zexus/zexus_ast.py +3 -2
- package/src/zexus/zpm/package_manager.py +1 -1
- package/src/zexus.egg-info/PKG-INFO +1 -1
- package/src/zexus.egg-info/SOURCES.txt +16 -0
package/package.json
CHANGED
package/src/zexus/__init__.py
CHANGED
package/src/zexus/cli/main.py
CHANGED
|
@@ -91,7 +91,7 @@ def show_all_commands():
|
|
|
91
91
|
console.print("\n[bold green]💡 Tip:[/bold green] Use 'zx <command> --help' for detailed command options\n")
|
|
92
92
|
|
|
93
93
|
@click.group(invoke_without_command=True)
|
|
94
|
-
@click.version_option(version="1.6.
|
|
94
|
+
@click.version_option(version="1.6.4", prog_name="Zexus")
|
|
95
95
|
@click.option('--syntax-style', type=click.Choice(['universal', 'tolerable', 'auto']),
|
|
96
96
|
default='auto', help='Syntax style to use (universal=strict, tolerable=flexible)')
|
|
97
97
|
@click.option('--advanced-parsing', is_flag=True, default=True,
|
package/src/zexus/cli/zpm.py
CHANGED
|
@@ -517,30 +517,21 @@ class Evaluator(ExpressionEvaluatorMixin, StatementEvaluatorMixin, FunctionEvalu
|
|
|
517
517
|
if is_error(obj):
|
|
518
518
|
return obj
|
|
519
519
|
|
|
520
|
-
#
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
# This could be either a property name (obj.prop) or an index variable (arr[i])
|
|
524
|
-
# We need to check if it's being used as an index (numeric) or property (string)
|
|
525
|
-
# First try to evaluate it as an identifier (variable lookup)
|
|
526
|
-
prop_result = self.eval_identifier(node.property, env)
|
|
527
|
-
if not is_error(prop_result) and not isinstance(prop_result, type(NULL)):
|
|
528
|
-
# Successfully found a variable, use its value as the property/index
|
|
529
|
-
property_name = prop_result.value if hasattr(prop_result, 'value') else str(prop_result)
|
|
530
|
-
else:
|
|
531
|
-
# Not found as variable, treat as literal property name (for obj.prop syntax)
|
|
532
|
-
property_name = node.property.value
|
|
533
|
-
elif isinstance(node.property, zexus_ast.IntegerLiteral):
|
|
534
|
-
# Direct integer index like arr[0]
|
|
535
|
-
property_name = node.property.value
|
|
536
|
-
elif isinstance(node.property, zexus_ast.PropertyAccessExpression):
|
|
537
|
-
# Nested property access - evaluate it
|
|
520
|
+
# Determine property name based on whether it's computed (obj[expr]) or literal (obj.prop)
|
|
521
|
+
if hasattr(node, 'computed') and node.computed:
|
|
522
|
+
# Computed property (obj[expr]) - always evaluate the expression
|
|
538
523
|
prop_result = self.eval_node(node.property, env, stack_trace)
|
|
539
524
|
if is_error(prop_result):
|
|
540
525
|
return prop_result
|
|
541
526
|
property_name = prop_result.value if hasattr(prop_result, 'value') else str(prop_result)
|
|
527
|
+
elif isinstance(node.property, zexus_ast.Identifier):
|
|
528
|
+
# Literal property (obj.prop) - use the identifier name directly
|
|
529
|
+
property_name = node.property.value
|
|
530
|
+
elif isinstance(node.property, zexus_ast.IntegerLiteral):
|
|
531
|
+
# Direct integer index like arr[0] (for backwards compatibility)
|
|
532
|
+
property_name = node.property.value
|
|
542
533
|
else:
|
|
543
|
-
#
|
|
534
|
+
# Fallback: evaluate the property expression
|
|
544
535
|
prop_result = self.eval_node(node.property, env, stack_trace)
|
|
545
536
|
if is_error(prop_result):
|
|
546
537
|
return prop_result
|
|
@@ -354,42 +354,67 @@ class FunctionEvaluatorMixin:
|
|
|
354
354
|
|
|
355
355
|
elif isinstance(fn, EntityDefinition):
|
|
356
356
|
debug_log(" Creating entity instance (old format)")
|
|
357
|
-
# Entity constructor: Person("Alice", 30)
|
|
357
|
+
# Entity constructor: Person("Alice", 30) or Person{name: "Alice", age: 30}
|
|
358
358
|
# Create instance with positional arguments mapped to properties
|
|
359
359
|
from ..object import EntityInstance, String, Integer
|
|
360
360
|
|
|
361
361
|
values = {}
|
|
362
|
-
# Map positional arguments to property names
|
|
363
|
-
if isinstance(fn.properties, dict):
|
|
364
|
-
prop_names = list(fn.properties.keys())
|
|
365
|
-
else:
|
|
366
|
-
prop_names = [prop['name'] for prop in fn.properties]
|
|
367
362
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
363
|
+
# Special case: If single argument is a Map, use it as field values
|
|
364
|
+
# This handles Entity{field: value} syntax which becomes Entity(Map{...})
|
|
365
|
+
if len(args) == 1 and isinstance(args[0], Map):
|
|
366
|
+
debug_log(" Single Map argument detected - using as field values")
|
|
367
|
+
map_arg = args[0]
|
|
368
|
+
# Extract key-value pairs from the Map
|
|
369
|
+
for key, value in map_arg.pairs.items():
|
|
370
|
+
# Convert key to string if it's a String object
|
|
371
|
+
key_str = key.value if isinstance(key, String) else str(key)
|
|
372
|
+
values[key_str] = value
|
|
373
|
+
else:
|
|
374
|
+
# Map positional arguments to property names
|
|
375
|
+
if isinstance(fn.properties, dict):
|
|
376
|
+
prop_names = list(fn.properties.keys())
|
|
377
|
+
else:
|
|
378
|
+
prop_names = [prop['name'] for prop in fn.properties]
|
|
379
|
+
|
|
380
|
+
for i, arg in enumerate(args):
|
|
381
|
+
if i < len(prop_names):
|
|
382
|
+
values[prop_names[i]] = arg
|
|
371
383
|
|
|
372
384
|
return EntityInstance(fn, values)
|
|
373
385
|
|
|
374
386
|
# Handle SecurityEntityDefinition (from security.py with methods support)
|
|
375
387
|
from ..security import EntityDefinition as SecurityEntityDef, EntityInstance as SecurityEntityInstance
|
|
388
|
+
from ..object import String
|
|
376
389
|
if isinstance(fn, SecurityEntityDef):
|
|
377
390
|
debug_log(" Creating entity instance (with methods)")
|
|
378
391
|
|
|
379
392
|
values = {}
|
|
380
|
-
# Map positional arguments to property names, INCLUDING INHERITED PROPERTIES
|
|
381
|
-
# Use get_all_properties() to get the full property list in correct order
|
|
382
|
-
if hasattr(fn, 'get_all_properties'):
|
|
383
|
-
# Get all properties (parent + child) in correct order
|
|
384
|
-
all_props = fn.get_all_properties()
|
|
385
|
-
prop_names = list(all_props.keys())
|
|
386
|
-
else:
|
|
387
|
-
# Fallback for old-style properties
|
|
388
|
-
prop_names = list(fn.properties.keys()) if isinstance(fn.properties, dict) else [prop['name'] for prop in fn.properties]
|
|
389
393
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
394
|
+
# Special case: If single argument is a Map, use it as field values
|
|
395
|
+
# This handles Entity{field: value} syntax which becomes Entity(Map{...})
|
|
396
|
+
if len(args) == 1 and isinstance(args[0], Map):
|
|
397
|
+
debug_log(" Single Map argument detected - using as field values")
|
|
398
|
+
map_arg = args[0]
|
|
399
|
+
# Extract key-value pairs from the Map
|
|
400
|
+
for key, value in map_arg.pairs.items():
|
|
401
|
+
# Convert key to string if it's a String object
|
|
402
|
+
key_str = key.value if isinstance(key, String) else str(key)
|
|
403
|
+
values[key_str] = value
|
|
404
|
+
else:
|
|
405
|
+
# Map positional arguments to property names, INCLUDING INHERITED PROPERTIES
|
|
406
|
+
# Use get_all_properties() to get the full property list in correct order
|
|
407
|
+
if hasattr(fn, 'get_all_properties'):
|
|
408
|
+
# Get all properties (parent + child) in correct order
|
|
409
|
+
all_props = fn.get_all_properties()
|
|
410
|
+
prop_names = list(all_props.keys())
|
|
411
|
+
else:
|
|
412
|
+
# Fallback for old-style properties
|
|
413
|
+
prop_names = list(fn.properties.keys()) if isinstance(fn.properties, dict) else [prop['name'] for prop in fn.properties]
|
|
414
|
+
|
|
415
|
+
for i, arg in enumerate(args):
|
|
416
|
+
if i < len(prop_names):
|
|
417
|
+
values[prop_names[i]] = arg
|
|
393
418
|
|
|
394
419
|
debug_log(f" Entity instance created with {len(values)} properties: {list(values.keys())}")
|
|
395
420
|
# Use create_instance to handle dependency injection
|
|
@@ -1977,6 +2002,8 @@ class FunctionEvaluatorMixin:
|
|
|
1977
2002
|
return Integer(len(arg.value))
|
|
1978
2003
|
if isinstance(arg, List):
|
|
1979
2004
|
return Integer(len(arg.elements))
|
|
2005
|
+
if isinstance(arg, Map):
|
|
2006
|
+
return Integer(len(arg.pairs))
|
|
1980
2007
|
# Handle Python list (shouldn't happen, but defensive)
|
|
1981
2008
|
if isinstance(arg, list):
|
|
1982
2009
|
return Integer(len(arg))
|
|
@@ -757,15 +757,23 @@ class StatementEvaluatorMixin:
|
|
|
757
757
|
if is_error(obj):
|
|
758
758
|
return obj
|
|
759
759
|
|
|
760
|
-
#
|
|
761
|
-
if hasattr(node.name
|
|
762
|
-
|
|
763
|
-
else:
|
|
764
|
-
# Evaluate property expression
|
|
760
|
+
# Determine property key based on whether it's computed (obj[expr]) or literal (obj.prop)
|
|
761
|
+
if hasattr(node.name, 'computed') and node.name.computed:
|
|
762
|
+
# Computed property (obj[expr]) - evaluate the expression
|
|
765
763
|
prop_result = self.eval_node(node.name.property, env, stack_trace)
|
|
766
764
|
if is_error(prop_result):
|
|
767
765
|
return prop_result
|
|
768
766
|
prop_key = prop_result.value if hasattr(prop_result, 'value') else str(prop_result)
|
|
767
|
+
else:
|
|
768
|
+
# Literal property (obj.prop) - use the identifier name directly
|
|
769
|
+
if hasattr(node.name.property, 'value'):
|
|
770
|
+
prop_key = node.name.property.value
|
|
771
|
+
else:
|
|
772
|
+
# Fallback: evaluate it
|
|
773
|
+
prop_result = self.eval_node(node.name.property, env, stack_trace)
|
|
774
|
+
if is_error(prop_result):
|
|
775
|
+
return prop_result
|
|
776
|
+
prop_key = prop_result.value if hasattr(prop_result, 'value') else str(prop_result)
|
|
769
777
|
|
|
770
778
|
# Evaluate value first
|
|
771
779
|
value = self.eval_node(node.value, env, stack_trace)
|
|
@@ -1708,11 +1716,10 @@ class StatementEvaluatorMixin:
|
|
|
1708
1716
|
|
|
1709
1717
|
# Pass the AST nodes as storage_vars, not the storage dict
|
|
1710
1718
|
contract = SmartContract(node.name.value, node.storage_vars, actions)
|
|
1711
|
-
|
|
1719
|
+
# Deploy with evaluated storage values to avoid storing AST nodes
|
|
1720
|
+
contract.deploy(evaluated_storage_values=storage)
|
|
1712
1721
|
|
|
1713
|
-
#
|
|
1714
|
-
for var_name, init_val in storage.items():
|
|
1715
|
-
contract.storage.set(var_name, init_val)
|
|
1722
|
+
# Storage values are now set during deploy(), no need to set again
|
|
1716
1723
|
|
|
1717
1724
|
# Check if contract has a constructor and execute it
|
|
1718
1725
|
if 'constructor' in actions:
|
|
@@ -27,6 +27,7 @@ precedences = {
|
|
|
27
27
|
SLASH: PRODUCT, STAR: PRODUCT, MOD: PRODUCT,
|
|
28
28
|
LPAREN: CALL,
|
|
29
29
|
LBRACKET: CALL,
|
|
30
|
+
LBRACE: CALL, # Entity{...} constructor syntax
|
|
30
31
|
DOT: CALL,
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -103,6 +104,7 @@ class UltimateParser:
|
|
|
103
104
|
ASSIGN: self.parse_assignment_expression,
|
|
104
105
|
LAMBDA: self.parse_lambda_infix, # support arrow-style lambdas: params => body
|
|
105
106
|
LPAREN: self.parse_call_expression,
|
|
107
|
+
LBRACE: self.parse_constructor_call_expression, # Entity{field: value} syntax
|
|
106
108
|
LBRACKET: self.parse_index_expression,
|
|
107
109
|
DOT: self.parse_method_call_expression,
|
|
108
110
|
}
|
|
@@ -1506,7 +1508,7 @@ class UltimateParser:
|
|
|
1506
1508
|
arguments = self.parse_expression_list(RPAREN)
|
|
1507
1509
|
return MethodCallExpression(object=left, method=method, arguments=arguments)
|
|
1508
1510
|
else:
|
|
1509
|
-
return PropertyAccessExpression(object=left, property=method)
|
|
1511
|
+
return PropertyAccessExpression(object=left, property=method, computed=False)
|
|
1510
1512
|
|
|
1511
1513
|
def parse_export_statement(self):
|
|
1512
1514
|
token = self.cur_token
|
|
@@ -1709,7 +1711,7 @@ class UltimateParser:
|
|
|
1709
1711
|
return None
|
|
1710
1712
|
|
|
1711
1713
|
field_name = Identifier(self.cur_token.literal)
|
|
1712
|
-
target = PropertyAccessExpression(obj_name, field_name)
|
|
1714
|
+
target = PropertyAccessExpression(obj_name, field_name, computed=False)
|
|
1713
1715
|
|
|
1714
1716
|
# Expect assignment
|
|
1715
1717
|
if not self.expect_peek(ASSIGN):
|
|
@@ -2728,6 +2730,18 @@ class UltimateParser:
|
|
|
2728
2730
|
exp.arguments = self.parse_expression_list(RPAREN)
|
|
2729
2731
|
return exp
|
|
2730
2732
|
|
|
2733
|
+
def parse_constructor_call_expression(self, function):
|
|
2734
|
+
"""Parse constructor call with map literal syntax: Entity{field: value, ...}
|
|
2735
|
+
|
|
2736
|
+
This converts Entity{a: 1, b: 2} into Entity({a: 1, b: 2})
|
|
2737
|
+
"""
|
|
2738
|
+
# Current token is LBRACE, parse it as a map literal
|
|
2739
|
+
map_literal = self.parse_map_literal()
|
|
2740
|
+
|
|
2741
|
+
# Create a call expression with the map as the single argument
|
|
2742
|
+
exp = CallExpression(function=function, arguments=[map_literal])
|
|
2743
|
+
return exp
|
|
2744
|
+
|
|
2731
2745
|
def parse_prefix_expression(self):
|
|
2732
2746
|
expression = PrefixExpression(operator=self.cur_token.literal, right=None)
|
|
2733
2747
|
self.next_token()
|
|
@@ -2800,7 +2814,7 @@ class UltimateParser:
|
|
|
2800
2814
|
# Expect closing bracket
|
|
2801
2815
|
if not self.expect_peek(RBRACKET):
|
|
2802
2816
|
return None
|
|
2803
|
-
return PropertyAccessExpression(object=left, property=index_expr)
|
|
2817
|
+
return PropertyAccessExpression(object=left, property=index_expr, computed=True)
|
|
2804
2818
|
|
|
2805
2819
|
def _lookahead_token_after_matching_paren(self):
|
|
2806
2820
|
"""Character-level lookahead: detect if the matching ')' is followed by '=>' (arrow).
|
|
@@ -1533,9 +1533,40 @@ class ContextStackParser:
|
|
|
1533
1533
|
default_val = BooleanLiteral(True)
|
|
1534
1534
|
elif val_token.type == FALSE:
|
|
1535
1535
|
default_val = BooleanLiteral(False)
|
|
1536
|
+
elif val_token.type == LBRACE:
|
|
1537
|
+
# Map literal: {}
|
|
1538
|
+
# Find matching RBRACE
|
|
1539
|
+
map_start = current_idx
|
|
1540
|
+
depth = 1
|
|
1541
|
+
current_idx += 1
|
|
1542
|
+
while current_idx < brace_end and depth > 0:
|
|
1543
|
+
if tokens[current_idx].type == LBRACE:
|
|
1544
|
+
depth += 1
|
|
1545
|
+
elif tokens[current_idx].type == RBRACE:
|
|
1546
|
+
depth -= 1
|
|
1547
|
+
current_idx += 1
|
|
1548
|
+
# Parse the map literal
|
|
1549
|
+
map_tokens = tokens[map_start:current_idx]
|
|
1550
|
+
default_val = self._parse_map_literal(map_tokens)
|
|
1551
|
+
elif val_token.type == LBRACKET:
|
|
1552
|
+
# List literal: []
|
|
1553
|
+
# Find matching RBRACKET
|
|
1554
|
+
list_start = current_idx
|
|
1555
|
+
depth = 1
|
|
1556
|
+
current_idx += 1
|
|
1557
|
+
while current_idx < brace_end and depth > 0:
|
|
1558
|
+
if tokens[current_idx].type == LBRACKET:
|
|
1559
|
+
depth += 1
|
|
1560
|
+
elif tokens[current_idx].type == RBRACKET:
|
|
1561
|
+
depth -= 1
|
|
1562
|
+
current_idx += 1
|
|
1563
|
+
# Parse the list literal
|
|
1564
|
+
list_tokens = tokens[list_start:current_idx]
|
|
1565
|
+
default_val = self._parse_list_literal(list_tokens)
|
|
1536
1566
|
elif val_token.type == IDENT:
|
|
1537
1567
|
default_val = Identifier(val_token.literal)
|
|
1538
|
-
|
|
1568
|
+
current_idx += 1
|
|
1569
|
+
# Note: current_idx already advanced for LBRACE and LBRACKET cases
|
|
1539
1570
|
|
|
1540
1571
|
# Use AstNodeShim for compatibility with evaluator
|
|
1541
1572
|
storage_vars.append(AstNodeShim(
|
|
@@ -3932,6 +3963,24 @@ class ContextStackParser:
|
|
|
3932
3963
|
if i < n and tokens[i].type == RPAREN:
|
|
3933
3964
|
i += 1 # Skip RPAREN
|
|
3934
3965
|
return CallExpression(Identifier(name), args, type_args=type_args)
|
|
3966
|
+
|
|
3967
|
+
# Check for constructor call with map literal: Entity{field: value, ...}
|
|
3968
|
+
elif i < n and tokens[i].type == LBRACE:
|
|
3969
|
+
# Parse the map literal as the single argument
|
|
3970
|
+
start = i
|
|
3971
|
+
depth = 1
|
|
3972
|
+
i += 1 # Skip LBRACE
|
|
3973
|
+
# Find matching RBRACE
|
|
3974
|
+
while i < n and depth > 0:
|
|
3975
|
+
if tokens[i].type == LBRACE:
|
|
3976
|
+
depth += 1
|
|
3977
|
+
elif tokens[i].type == RBRACE:
|
|
3978
|
+
depth -= 1
|
|
3979
|
+
i += 1
|
|
3980
|
+
# Parse the map literal tokens (including braces)
|
|
3981
|
+
map_literal = self._parse_map_literal(tokens[start:i])
|
|
3982
|
+
return CallExpression(Identifier(name), [map_literal], type_args=type_args)
|
|
3983
|
+
|
|
3935
3984
|
else:
|
|
3936
3985
|
return Identifier(name)
|
|
3937
3986
|
|
|
@@ -3997,7 +4046,8 @@ class ContextStackParser:
|
|
|
3997
4046
|
# Property access: expr.name
|
|
3998
4047
|
current_expr = PropertyAccessExpression(
|
|
3999
4048
|
object=current_expr,
|
|
4000
|
-
property=Identifier(name_token.literal)
|
|
4049
|
+
property=Identifier(name_token.literal),
|
|
4050
|
+
computed=False
|
|
4001
4051
|
)
|
|
4002
4052
|
continue
|
|
4003
4053
|
|
|
@@ -4052,7 +4102,8 @@ class ContextStackParser:
|
|
|
4052
4102
|
prop_expr = self._parse_expression(inner_tokens) if inner_tokens else Identifier('')
|
|
4053
4103
|
current_expr = PropertyAccessExpression(
|
|
4054
4104
|
object=current_expr,
|
|
4055
|
-
property=prop_expr
|
|
4105
|
+
property=prop_expr,
|
|
4106
|
+
computed=True
|
|
4056
4107
|
)
|
|
4057
4108
|
continue
|
|
4058
4109
|
|
package/src/zexus/security.py
CHANGED
|
@@ -734,8 +734,10 @@ class StorageBackend:
|
|
|
734
734
|
class InMemoryBackend(StorageBackend):
|
|
735
735
|
def __init__(self):
|
|
736
736
|
self.data = {}
|
|
737
|
-
def set(self, key, value):
|
|
738
|
-
|
|
737
|
+
def set(self, key, value):
|
|
738
|
+
self.data[key] = value
|
|
739
|
+
def get(self, key):
|
|
740
|
+
return self.data.get(key)
|
|
739
741
|
def delete(self, key):
|
|
740
742
|
if key in self.data: del self.data[key]
|
|
741
743
|
|
|
@@ -954,8 +956,24 @@ class SmartContract:
|
|
|
954
956
|
|
|
955
957
|
print(f" 🔗 Contract Address: {new_address}")
|
|
956
958
|
|
|
957
|
-
#
|
|
958
|
-
|
|
959
|
+
# Copy initial storage values from the template contract
|
|
960
|
+
# This ensures instances get the evaluated initial values
|
|
961
|
+
initial_storage = {}
|
|
962
|
+
for var_node in self.storage_vars:
|
|
963
|
+
var_name = None
|
|
964
|
+
if hasattr(var_node, 'name'):
|
|
965
|
+
var_name = var_node.name.value if hasattr(var_node.name, 'value') else var_node.name
|
|
966
|
+
elif isinstance(var_node, dict):
|
|
967
|
+
var_name = var_node.get("name")
|
|
968
|
+
|
|
969
|
+
if var_name:
|
|
970
|
+
# Get the initial value from the template contract's storage
|
|
971
|
+
value = self.storage.get(var_name)
|
|
972
|
+
if value is not None:
|
|
973
|
+
initial_storage[var_name] = value
|
|
974
|
+
|
|
975
|
+
# Deploy the instance with the copied initial values
|
|
976
|
+
instance.deploy(evaluated_storage_values=initial_storage)
|
|
959
977
|
instance.parent_contract = self
|
|
960
978
|
|
|
961
979
|
print(f" Available actions: {list(self.actions.keys())}")
|
|
@@ -964,32 +982,32 @@ class SmartContract:
|
|
|
964
982
|
def __call__(self, *args):
|
|
965
983
|
return self.instantiate(args)
|
|
966
984
|
|
|
967
|
-
def deploy(self):
|
|
968
|
-
"""Deploy the contract and initialize persistent storage
|
|
985
|
+
def deploy(self, evaluated_storage_values=None):
|
|
986
|
+
"""Deploy the contract and initialize persistent storage
|
|
987
|
+
|
|
988
|
+
Args:
|
|
989
|
+
evaluated_storage_values: Optional dict of evaluated initial values
|
|
990
|
+
"""
|
|
969
991
|
# Checks if we should reset storage or strictly load existing
|
|
970
992
|
# For simplicity in this VM, subsequent runs act like "loading" if DB exists
|
|
971
993
|
self.is_deployed = True
|
|
972
994
|
|
|
973
|
-
#
|
|
974
|
-
|
|
975
|
-
var_name
|
|
976
|
-
default_value = None
|
|
977
|
-
|
|
978
|
-
if hasattr(var_node, 'initial_value'):
|
|
979
|
-
var_name = var_node.name.value if hasattr(var_node.name, 'value') else var_node.name
|
|
980
|
-
default_value = var_node.initial_value
|
|
981
|
-
elif isinstance(var_node, dict) and "initial_value" in var_node:
|
|
982
|
-
var_name = var_node.get("name")
|
|
983
|
-
default_value = var_node["initial_value"]
|
|
984
|
-
|
|
985
|
-
if var_name:
|
|
986
|
-
# ONLY set if not already in DB (Persistence Logic)
|
|
995
|
+
# If evaluated values are provided, use them (from evaluator)
|
|
996
|
+
if evaluated_storage_values:
|
|
997
|
+
for var_name, value in evaluated_storage_values.items():
|
|
987
998
|
if self.storage.get(var_name) is None:
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
999
|
+
self.storage.set(var_name, value)
|
|
1000
|
+
else:
|
|
1001
|
+
# Fallback: Initialize storage with NULL for declared variables
|
|
1002
|
+
for var_node in self.storage_vars:
|
|
1003
|
+
var_name = None
|
|
1004
|
+
if hasattr(var_node, 'name'):
|
|
1005
|
+
var_name = var_node.name.value if hasattr(var_node.name, 'value') else var_node.name
|
|
1006
|
+
elif isinstance(var_node, dict):
|
|
1007
|
+
var_name = var_node.get("name")
|
|
1008
|
+
|
|
1009
|
+
if var_name and self.storage.get(var_name) is None:
|
|
1010
|
+
self.storage.set(var_name, Null)
|
|
993
1011
|
|
|
994
1012
|
def call_method(self, action_name, args):
|
|
995
1013
|
"""Call a contract action - similar to EntityInstance.call_method"""
|
package/src/zexus/zexus_ast.py
CHANGED
|
@@ -592,12 +592,13 @@ class LiteralPattern:
|
|
|
592
592
|
return f"LiteralPattern({self.value})"
|
|
593
593
|
|
|
594
594
|
class PropertyAccessExpression(Expression):
|
|
595
|
-
def __init__(self, object, property):
|
|
595
|
+
def __init__(self, object, property, computed=False):
|
|
596
596
|
self.object = object
|
|
597
597
|
self.property = property
|
|
598
|
+
self.computed = computed # True for obj[expr], False for obj.prop
|
|
598
599
|
|
|
599
600
|
def __repr__(self):
|
|
600
|
-
return f"PropertyAccessExpression(object={self.object}, property={self.property})"
|
|
601
|
+
return f"PropertyAccessExpression(object={self.object}, property={self.property}, computed={self.computed})"
|
|
601
602
|
|
|
602
603
|
class AssignmentExpression(Expression):
|
|
603
604
|
def __init__(self, name, value):
|
|
@@ -23,7 +23,7 @@ class PackageManager:
|
|
|
23
23
|
self.installer = PackageInstaller(self.zpm_dir)
|
|
24
24
|
self.publisher = PackagePublisher(self.registry)
|
|
25
25
|
|
|
26
|
-
def init(self, name: str = None, version: str = "1.6.
|
|
26
|
+
def init(self, name: str = None, version: str = "1.6.4") -> Dict:
|
|
27
27
|
"""Initialize a new Zexus project with package.json"""
|
|
28
28
|
if self.config_file.exists():
|
|
29
29
|
print(f"⚠️ {self.config_file} already exists")
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
.gitattributes
|
|
2
2
|
.gitignore
|
|
3
3
|
CHANGELOG.md
|
|
4
|
+
FIX_SUMMARY.md
|
|
4
5
|
LICENSE
|
|
5
6
|
PUBLISH_TO_NPM.md
|
|
6
7
|
README.md
|
|
@@ -13,6 +14,7 @@ any.zx
|
|
|
13
14
|
check_verify_ast.py
|
|
14
15
|
comprehensive_test.zx
|
|
15
16
|
crypto.zx
|
|
17
|
+
debug_parse.py
|
|
16
18
|
debug_persist_ultimate.zx
|
|
17
19
|
demo_backend_server.zx
|
|
18
20
|
demo_backend_simple.zx
|
|
@@ -30,8 +32,21 @@ setup.py
|
|
|
30
32
|
setup_stdlib.sh
|
|
31
33
|
shared_config.json
|
|
32
34
|
test_const_time_debug.zx
|
|
35
|
+
test_contract_assignment.zx
|
|
36
|
+
test_contract_debug.zx
|
|
37
|
+
test_contract_map.zx
|
|
33
38
|
test_data.json
|
|
39
|
+
test_entity_debug.zx
|
|
40
|
+
test_map_assignment.zx
|
|
41
|
+
test_map_debug.zx
|
|
42
|
+
test_map_len.zx
|
|
43
|
+
test_map_persistence.zx
|
|
44
|
+
test_nested_map_assignment.zx
|
|
45
|
+
test_simple_contract.zx
|
|
34
46
|
test_sqlite_python.py
|
|
47
|
+
test_state_variable_type.zx
|
|
48
|
+
test_storage_init.zx
|
|
49
|
+
test_storage_types.zx
|
|
35
50
|
ultimate_test.zx
|
|
36
51
|
zexus.json
|
|
37
52
|
zpics
|
|
@@ -348,6 +363,7 @@ examples/test_postgres.zx
|
|
|
348
363
|
examples/test_sqlite.zx
|
|
349
364
|
examples/token_contract.zx
|
|
350
365
|
examples/ziver_chain_test.zx
|
|
366
|
+
issues/ISSUSE1.md
|
|
351
367
|
linguist-submission/SUBMISSION_INSTRUCTIONS.md
|
|
352
368
|
linguist-submission/grammars.yml
|
|
353
369
|
linguist-submission/languages.yml
|