zexus 1.8.1 → 1.8.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.
@@ -67,9 +67,9 @@ class BytecodeCompiler:
67
67
  if op == Opcode.NOP and isinstance(operand, str) and operand.startswith('L'):
68
68
  labels[operand] = i
69
69
 
70
- # 2. Update jumps
70
+ # 2. Update jumps (including SETUP_TRY which also uses label targets)
71
71
  for i, (op, operand) in enumerate(self.instructions):
72
- if op in (Opcode.JUMP, Opcode.JUMP_IF_FALSE, Opcode.JUMP_IF_TRUE):
72
+ if op in (Opcode.JUMP, Opcode.JUMP_IF_FALSE, Opcode.JUMP_IF_TRUE, Opcode.SETUP_TRY):
73
73
  if isinstance(operand, str) and operand in labels:
74
74
  self.instructions[i] = (op, labels[operand])
75
75
 
@@ -155,25 +155,8 @@ class BytecodeCompiler:
155
155
 
156
156
  def _unsupported_message(self, node_type: str) -> str:
157
157
  """Return a friendly message for unsupported nodes."""
158
- hints = {
159
- "UseStatement": "Module imports",
160
- "FromStatement": "Module imports",
161
- "EntityStatement": "Entity declarations",
162
- "ContractStatement": "Contract declarations",
163
- "ActionStatement": "Action/function declarations",
164
- "FunctionStatement": "Function declarations",
165
- "IfStatement": "If statements",
166
- "WhileStatement": "While loops",
167
- "ForStatement": "For loops",
168
- "ForEachStatement": "For-each loops",
169
- "TxStatement": "Transaction blocks",
170
- "RequireStatement": "Require statements",
171
- "RevertStatement": "Revert statements",
172
- }
173
- prefix = hints.get(node_type)
174
- if prefix:
175
- return f"{prefix} are not yet supported by the VM bytecode compiler"
176
- return f"Node type '{node_type}' is not supported by the VM bytecode compiler"
158
+ msg = f"Node type '{node_type}' is not currently supported by the bytecode compiler."
159
+ return msg
177
160
 
178
161
  # ==================== Program & Statements ====================
179
162
 
@@ -360,6 +343,9 @@ class BytecodeCompiler:
360
343
  '>=': Opcode.GTE,
361
344
  '&&': Opcode.AND,
362
345
  '||': Opcode.OR,
346
+ 'and': Opcode.AND,
347
+ 'or': Opcode.OR,
348
+ '..': Opcode.ADD, # string concat alias
363
349
  }
364
350
 
365
351
  opcode = op_map.get(node.operator)
@@ -374,7 +360,7 @@ class BytecodeCompiler:
374
360
 
375
361
  if node.operator == '-':
376
362
  self._emit(Opcode.NEG)
377
- elif node.operator == '!':
363
+ elif node.operator in ('!', 'not'):
378
364
  self._emit(Opcode.NOT)
379
365
  else:
380
366
  raise NotImplementedError(f"Prefix operator {node.operator} not implemented")
@@ -382,35 +368,33 @@ class BytecodeCompiler:
382
368
  # ==================== Control Flow ====================
383
369
 
384
370
  def _compile_IfExpression(self, node):
385
- """Compile if/else statement"""
386
- # Compile condition
387
- self._compile_node(node.condition)
388
-
389
- # Jump if false
390
- else_label = self._make_label()
371
+ """Compile if/elif/else statement"""
391
372
  end_label = self._make_label()
392
-
393
- self._emit(Opcode.JUMP_IF_FALSE, else_label)
394
-
395
- # Compile consequence
373
+
374
+ # --- Main if ---
375
+ self._compile_node(node.condition)
376
+ next_label = self._make_label()
377
+ self._emit(Opcode.JUMP_IF_FALSE, next_label)
396
378
  self._compile_node(node.consequence)
397
379
  self._emit(Opcode.JUMP, end_label)
398
-
399
- # Else branch
400
- self._emit(Opcode.NOP) # Label placeholder
401
- self.instructions[-1] = (Opcode.NOP, else_label) # Mark label
402
-
380
+ self._mark_label(next_label)
381
+
382
+ # --- elif chains ---
383
+ elif_parts = getattr(node, 'elif_parts', None) or []
384
+ for elif_cond, elif_body in elif_parts:
385
+ self._compile_node(elif_cond)
386
+ next_elif_label = self._make_label()
387
+ self._emit(Opcode.JUMP_IF_FALSE, next_elif_label)
388
+ self._compile_node(elif_body)
389
+ self._emit(Opcode.JUMP, end_label)
390
+ self._mark_label(next_elif_label)
391
+
392
+ # --- else ---
403
393
  if node.alternative:
404
394
  self._compile_node(node.alternative)
405
- else:
406
- # Push null for else branch
407
- null_idx = self._add_constant(None)
408
- self._emit(Opcode.LOAD_CONST, null_idx)
409
-
410
- # End label
411
- self._emit(Opcode.NOP)
412
- self.instructions[-1] = (Opcode.NOP, end_label)
413
-
395
+
396
+ self._mark_label(end_label)
397
+
414
398
  _compile_IfStatement = _compile_IfExpression
415
399
 
416
400
  def _compile_TernaryExpression(self, node):
@@ -508,7 +492,84 @@ class BytecodeCompiler:
508
492
  self.instructions[exit_jump_idx] = (Opcode.JUMP_IF_FALSE, end_label)
509
493
 
510
494
  self.loop_stack.pop()
511
-
495
+
496
+ def _compile_ForEachStatement(self, node):
497
+ """Compile for-each loop: for item in iterable { ... }
498
+
499
+ Strategy: index-based iteration using GET_LENGTH + INDEX.
500
+ Generates equivalent of:
501
+ _iter = <iterable>
502
+ _index = 0
503
+ loop_start:
504
+ if _index >= len(_iter): goto end
505
+ item = _iter[_index]
506
+ <body>
507
+ continue_label:
508
+ _index = _index + 1
509
+ goto loop_start
510
+ end:
511
+ """
512
+ # --- Compile iterable and store in a hidden temp ---
513
+ self._compile_node(node.iterable)
514
+ iter_var = self._make_temp_name("iter")
515
+ iter_const = self._add_constant(iter_var)
516
+ self._emit(Opcode.STORE_NAME, iter_const)
517
+
518
+ # --- Initialise index = 0 ---
519
+ index_var = self._make_temp_name("idx")
520
+ index_const = self._add_constant(index_var)
521
+ zero_const = self._add_constant(0)
522
+ self._emit(Opcode.LOAD_CONST, zero_const)
523
+ self._emit(Opcode.STORE_NAME, index_const)
524
+
525
+ # --- Labels ---
526
+ start_label = self._make_label()
527
+ end_label = self._make_label()
528
+ continue_label = self._make_label()
529
+
530
+ self.loop_stack.append({
531
+ 'start': start_label,
532
+ 'end': end_label,
533
+ 'continue': continue_label,
534
+ })
535
+
536
+ # --- Condition: index < len(iterable) ---
537
+ self._mark_label(start_label)
538
+ self._emit(Opcode.LOAD_NAME, index_const) # push index
539
+ self._emit(Opcode.LOAD_NAME, iter_const) # push iterable
540
+ self.instructions.append(("GET_LENGTH", None)) # push len(iterable)
541
+ self._emit(Opcode.LT) # index < length
542
+ exit_jump_idx = len(self.instructions)
543
+ self._emit(Opcode.JUMP_IF_FALSE, None) # jump to end if false
544
+
545
+ # --- Extract item: item = iterable[index] ---
546
+ self._emit(Opcode.LOAD_NAME, iter_const) # push iterable
547
+ self._emit(Opcode.LOAD_NAME, index_const) # push index
548
+ self._emit(Opcode.INDEX) # iterable[index]
549
+ item_name = node.item.value if hasattr(node.item, 'value') else str(node.item)
550
+ item_const = self._add_constant(item_name)
551
+ self._emit(Opcode.STORE_NAME, item_const) # store as loop variable
552
+
553
+ # --- Body ---
554
+ self._compile_node(node.body)
555
+
556
+ # --- Continue: index += 1 ---
557
+ self._mark_label(continue_label)
558
+ one_const = self._add_constant(1)
559
+ self._emit(Opcode.LOAD_NAME, index_const)
560
+ self._emit(Opcode.LOAD_CONST, one_const)
561
+ self._emit(Opcode.ADD)
562
+ self._emit(Opcode.STORE_NAME, index_const)
563
+
564
+ # --- Jump back ---
565
+ self._emit(Opcode.JUMP, start_label)
566
+
567
+ # --- End ---
568
+ self._mark_label(end_label)
569
+ self.instructions[exit_jump_idx] = (Opcode.JUMP_IF_FALSE, end_label)
570
+
571
+ self.loop_stack.pop()
572
+
512
573
  # ==================== Function Calls ====================
513
574
 
514
575
  def _compile_CallExpression(self, node):
@@ -653,6 +714,37 @@ class BytecodeCompiler:
653
714
  _compile_FunctionStatement = _compile_ActionStatement
654
715
  _compile_PureFunctionStatement = _compile_ActionStatement
655
716
 
717
+ def _compile_LambdaExpression(self, node):
718
+ """Compile lambda/anonymous function expression.
719
+
720
+ Lambda differs from ActionStatement: it has no name and its value should
721
+ be *pushed* onto the stack (not stored) so it can be used inline, e.g.
722
+ as an argument to a higher-order function.
723
+ """
724
+ from .bytecode import Bytecode
725
+
726
+ func_compiler = self.__class__()
727
+ func_compiler._compile_node(node.body)
728
+
729
+ # Implicit return of last expression value
730
+ null_idx = func_compiler._add_constant(None)
731
+ func_compiler._emit(Opcode.LOAD_CONST, null_idx)
732
+ func_compiler._emit(Opcode.RETURN)
733
+
734
+ func_compiler._resolve_labels()
735
+ func_bytecode = Bytecode(func_compiler.instructions, func_compiler.constants)
736
+
737
+ params = [p.value if hasattr(p, 'value') else str(p) for p in node.parameters]
738
+ func_desc = {
739
+ "bytecode": func_bytecode,
740
+ "params": params,
741
+ "is_async": False,
742
+ "name": "<lambda>",
743
+ }
744
+
745
+ func_idx = self._add_constant(func_desc)
746
+ self._emit(Opcode.LOAD_CONST, func_idx)
747
+
656
748
  def _compile_ConstStatement(self, node):
657
749
  """Compile const declaration (same as Let for current VM)"""
658
750
  self._compile_LetStatement(node)
@@ -967,7 +1059,8 @@ class BytecodeCompiler:
967
1059
 
968
1060
  def _compile_AwaitExpression(self, node):
969
1061
  """Compile await expression"""
970
- self._compile_node(node.argument)
1062
+ expr = getattr(node, 'expression', None) or getattr(node, 'argument', None)
1063
+ self._compile_node(expr)
971
1064
  self._emit(Opcode.AWAIT)
972
1065
 
973
1066
  def _compile_ThisExpression(self, node):
@@ -1206,6 +1299,334 @@ class BytecodeCompiler:
1206
1299
  # Alias MatchExpression to logic (it's similar enough for basic cases)
1207
1300
  _compile_MatchExpression = _compile_PatternStatement
1208
1301
 
1302
+ # ==================== Debug / Verify / Watch ====================
1303
+
1304
+ def _compile_DebugStatement(self, node):
1305
+ """Compile debug statement — prints the value (conditional on condition if present)."""
1306
+ if node.condition:
1307
+ skip_label = self._make_label()
1308
+ self._compile_node(node.condition)
1309
+ self._emit(Opcode.JUMP_IF_FALSE, skip_label)
1310
+ self._compile_node(node.value)
1311
+ self._emit(Opcode.PRINT)
1312
+ end_label = self._make_label()
1313
+ self._emit(Opcode.JUMP, end_label)
1314
+ self._mark_label(skip_label)
1315
+ self._mark_label(end_label)
1316
+ else:
1317
+ self._compile_node(node.value)
1318
+ self._emit(Opcode.PRINT)
1319
+
1320
+ def _compile_VerifyStatement(self, node):
1321
+ """Compile verify statement.
1322
+
1323
+ Simple assertion form: verify condition, "message"
1324
+ Compiles to: if (!condition) { require(false, message) }
1325
+ Complex forms: delegate to the evaluator via VM-builtin call.
1326
+ """
1327
+ if node.condition and not node.mode:
1328
+ # Simple assertion: verify cond, msg
1329
+ self._compile_node(node.condition)
1330
+ ok_label = self._make_label()
1331
+ self._emit(Opcode.JUMP_IF_TRUE, ok_label)
1332
+ # Verification failed — emit require(false, message)
1333
+ false_idx = self._add_constant(False)
1334
+ self._emit(Opcode.LOAD_CONST, false_idx)
1335
+ if node.message:
1336
+ self._compile_node(node.message)
1337
+ else:
1338
+ msg_idx = self._add_constant("Verification failed")
1339
+ self._emit(Opcode.LOAD_CONST, msg_idx)
1340
+ self._emit(Opcode.REQUIRE)
1341
+ self._mark_label(ok_label)
1342
+ else:
1343
+ # Complex form — delegate to higher-level evaluator
1344
+ self._emit_vm_builtin_call("__verify__", node)
1345
+
1346
+ def _compile_WatchStatement(self, node):
1347
+ """Compile watch statement — reactive state management.
1348
+
1349
+ In a bytecode context, watch compiles the reaction body inline.
1350
+ Full reactivity tracking requires runtime support; here we compile the
1351
+ body so it can at least execute once (eager evaluation).
1352
+ """
1353
+ if node.watched_expr:
1354
+ self._compile_node(node.watched_expr)
1355
+ self._emit(Opcode.POP)
1356
+ self._compile_node(node.reaction)
1357
+
1358
+ def _compile_NullishExpression(self, node):
1359
+ """Compile nullish coalescing: left ?? right"""
1360
+ self._compile_node(node.left)
1361
+ self._emit(Opcode.DUP)
1362
+ # If not null, skip to end
1363
+ end_label = self._make_label()
1364
+ null_idx = self._add_constant(None)
1365
+ self._emit(Opcode.LOAD_CONST, null_idx)
1366
+ self._emit(Opcode.NEQ)
1367
+ self._emit(Opcode.JUMP_IF_TRUE, end_label)
1368
+ # Left was null, pop it and use right
1369
+ self._emit(Opcode.POP)
1370
+ self._compile_node(node.right)
1371
+ self._mark_label(end_label)
1372
+
1373
+ def _compile_ThrowStatement(self, node):
1374
+ """Compile throw statement"""
1375
+ expr = getattr(node, 'message', None) or getattr(node, 'expression', None)
1376
+ if expr:
1377
+ self._compile_node(expr)
1378
+ else:
1379
+ self._emit(Opcode.LOAD_CONST, self._add_constant("Unknown error"))
1380
+ self._emit(Opcode.THROW)
1381
+
1382
+ def _compile_EnumStatement(self, node):
1383
+ """Compile enum declaration"""
1384
+ self._emit_vm_builtin_call("__define_enum__", node)
1385
+
1386
+ def _compile_StringInterpolationExpression(self, node):
1387
+ """Compile string interpolation: `hello {name}`
1388
+
1389
+ Compiles each part and concatenates with ADD.
1390
+ """
1391
+ parts = node.parts if hasattr(node, 'parts') else []
1392
+ if not parts:
1393
+ self._emit(Opcode.LOAD_CONST, self._add_constant(""))
1394
+ return
1395
+
1396
+ self._compile_node(parts[0])
1397
+ for part in parts[1:]:
1398
+ self._compile_node(part)
1399
+ self._emit(Opcode.ADD)
1400
+
1401
+ # ==================== ActionLiteral / AsyncExpression / ProtectStatement ====================
1402
+
1403
+ def _compile_ActionLiteral(self, node):
1404
+ """Compile action literal (anonymous function expression).
1405
+
1406
+ ActionLiteral is semantically identical to LambdaExpression — an
1407
+ anonymous function with parameters and a body. It pushes a function
1408
+ descriptor onto the stack so it can be passed as an argument or
1409
+ assigned to a variable.
1410
+ """
1411
+ from .bytecode import Bytecode
1412
+
1413
+ func_compiler = self.__class__()
1414
+ func_compiler._compile_node(node.body)
1415
+
1416
+ # Implicit return null if execution flows off the end
1417
+ null_idx = func_compiler._add_constant(None)
1418
+ func_compiler._emit(Opcode.LOAD_CONST, null_idx)
1419
+ func_compiler._emit(Opcode.RETURN)
1420
+
1421
+ func_compiler._resolve_labels()
1422
+ func_bytecode = Bytecode(func_compiler.instructions, func_compiler.constants)
1423
+
1424
+ params = [p.value if hasattr(p, 'value') else str(p) for p in node.parameters]
1425
+ func_desc = {
1426
+ "bytecode": func_bytecode,
1427
+ "params": params,
1428
+ "is_async": False,
1429
+ "name": "<action>",
1430
+ }
1431
+
1432
+ func_idx = self._add_constant(func_desc)
1433
+ self._emit(Opcode.LOAD_CONST, func_idx)
1434
+
1435
+ def _compile_AsyncExpression(self, node):
1436
+ """Compile async expression: ``async <expr>``
1437
+
1438
+ Compiles the inner expression, then emits SPAWN to schedule it
1439
+ asynchronously. The resulting task handle is left on the stack.
1440
+ """
1441
+ self._compile_node(node.expression)
1442
+ self._emit(Opcode.SPAWN)
1443
+
1444
+ def _compile_ProtectStatement(self, node):
1445
+ """Compile protect() security guardrail.
1446
+
1447
+ protect(target, rules) is a high-level security directive.
1448
+ We compile target and rules, then call the VM builtin __protect__.
1449
+ If the builtin is not registered at runtime the VM will silently
1450
+ skip (same behaviour as NativeStatement etc.).
1451
+ """
1452
+ # Compile target
1453
+ self._compile_node(node.target)
1454
+ # Compile rules
1455
+ self._compile_node(node.rules)
1456
+ # Call builtin
1457
+ name_idx = self._add_constant("__protect__")
1458
+ self._emit(Opcode.CALL_NAME, (name_idx, 2))
1459
+ self._emit(Opcode.POP)
1460
+
1461
+ # ==================== Additional commonly-used nodes ====================
1462
+
1463
+ def _compile_SandboxStatement(self, node):
1464
+ """Compile sandbox statement — wraps body in an isolated scope."""
1465
+ self._compile_node(node.body)
1466
+
1467
+ def _compile_MiddlewareStatement(self, node):
1468
+ """Compile middleware registration."""
1469
+ self._emit_vm_builtin_call("__middleware__", node)
1470
+
1471
+ def _compile_AuthStatement(self, node):
1472
+ """Compile auth statement."""
1473
+ self._emit_vm_builtin_call("__auth__", node)
1474
+
1475
+ def _compile_ThrottleStatement(self, node):
1476
+ """Compile throttle statement."""
1477
+ self._emit_vm_builtin_call("__throttle__", node)
1478
+
1479
+ def _compile_CacheStatement(self, node):
1480
+ """Compile cache statement."""
1481
+ self._emit_vm_builtin_call("__cache__", node)
1482
+
1483
+ def _compile_SealStatement(self, node):
1484
+ """Compile seal statement."""
1485
+ self._emit_vm_builtin_call("__seal__", node)
1486
+
1487
+ def _compile_StreamStatement(self, node):
1488
+ """Compile stream statement."""
1489
+ self._emit_vm_builtin_call("__stream__", node)
1490
+
1491
+ def _compile_LogStatement(self, node):
1492
+ """Compile log statement — same as print."""
1493
+ if hasattr(node, 'message'):
1494
+ self._compile_node(node.message)
1495
+ elif hasattr(node, 'value'):
1496
+ self._compile_node(node.value)
1497
+ elif hasattr(node, 'expression'):
1498
+ self._compile_node(node.expression)
1499
+ else:
1500
+ self._emit(Opcode.LOAD_CONST, self._add_constant(""))
1501
+ self._emit(Opcode.PRINT)
1502
+
1503
+ def _compile_ImmutableStatement(self, node):
1504
+ """Compile immutable declaration — treat as const."""
1505
+ self._compile_LetStatement(node)
1506
+
1507
+ def _compile_ValidateStatement(self, node):
1508
+ """Compile validate statement."""
1509
+ self._emit_vm_builtin_call("__validate__", node)
1510
+
1511
+ def _compile_SanitizeStatement(self, node):
1512
+ """Compile sanitize statement."""
1513
+ self._emit_vm_builtin_call("__sanitize__", node)
1514
+
1515
+ def _compile_InjectStatement(self, node):
1516
+ """Compile inject statement (dependency injection)."""
1517
+ self._emit_vm_builtin_call("__inject__", node)
1518
+
1519
+ def _compile_InterfaceStatement(self, node):
1520
+ """Compile interface statement — define type contract."""
1521
+ self._emit_vm_builtin_call("__interface__", node)
1522
+
1523
+ def _compile_TypeAliasStatement(self, node):
1524
+ """Compile type alias — no-op at runtime."""
1525
+ pass
1526
+
1527
+ def _compile_ModuleStatement(self, node):
1528
+ """Compile module statement."""
1529
+ if hasattr(node, 'body'):
1530
+ self._compile_node(node.body)
1531
+
1532
+ def _compile_PackageStatement(self, node):
1533
+ """Compile package statement."""
1534
+ if hasattr(node, 'body'):
1535
+ self._compile_node(node.body)
1536
+
1537
+ def _compile_TrailStatement(self, node):
1538
+ """Compile trail/audit-trail statement."""
1539
+ self._emit_vm_builtin_call("__trail__", node)
1540
+
1541
+ def _compile_EmbeddedCodeStatement(self, node):
1542
+ """Compile embedded code — no-op in VM (Python-only)."""
1543
+ pass
1544
+
1545
+ def _compile_EmbeddedLiteral(self, node):
1546
+ """Compile embedded literal — push as string constant."""
1547
+ code = getattr(node, 'code', '') or getattr(node, 'value', '') or ''
1548
+ self._emit(Opcode.LOAD_CONST, self._add_constant(code))
1549
+
1550
+ def _compile_ExternalDeclaration(self, node):
1551
+ """Compile external declaration — no-op (type hint only)."""
1552
+ pass
1553
+
1554
+ def _compile_ImportLogStatement(self, node):
1555
+ """Compile import log statement — no-op at compile time."""
1556
+ pass
1557
+
1558
+ def _compile_ExactlyStatement(self, node):
1559
+ """Compile exactly statement (type-exact check)."""
1560
+ self._emit_vm_builtin_call("__exactly__", node)
1561
+
1562
+ def _compile_FindExpression(self, node):
1563
+ """Compile find expression."""
1564
+ self._emit_vm_builtin_call("__find__", node, discard_result=False)
1565
+
1566
+ def _compile_LoadExpression(self, node):
1567
+ """Compile load expression."""
1568
+ self._emit_vm_builtin_call("__load__", node, discard_result=False)
1569
+
1570
+ def _compile_DestructurePattern(self, node):
1571
+ """Compile destructure pattern — push null (placeholder)."""
1572
+ self._emit(Opcode.LOAD_CONST, self._add_constant(None))
1573
+
1574
+ def _compile_AtomicStatement(self, node):
1575
+ """Compile atomic statement — executes body/expr as a single unit.
1576
+
1577
+ In the current VM, atomic simply compiles the inner body or expression
1578
+ directly. True atomicity would require runtime locking which the VM
1579
+ can add later.
1580
+ """
1581
+ if node.body:
1582
+ self._compile_node(node.body)
1583
+ elif node.expr:
1584
+ self._compile_node(node.expr)
1585
+ self._emit(Opcode.POP)
1586
+
1587
+ # ==================== Channel / Concurrency statements ====================
1588
+
1589
+ def _compile_ChannelStatement(self, node):
1590
+ """Compile channel declaration: channel<T>[capacity] name
1591
+
1592
+ Emits a call to the ``__create_channel__`` VM builtin which creates a
1593
+ :class:`Channel` object and stores it in the environment.
1594
+ """
1595
+ # Push channel name
1596
+ ch_name = node.name.value if hasattr(node.name, 'value') else str(node.name)
1597
+ self._emit(Opcode.LOAD_CONST, self._add_constant(ch_name))
1598
+
1599
+ # Push element type (string or None)
1600
+ elem_type = None
1601
+ if hasattr(node, 'element_type') and node.element_type is not None:
1602
+ elem_type = node.element_type.value if hasattr(node.element_type, 'value') else str(node.element_type)
1603
+ self._emit(Opcode.LOAD_CONST, self._add_constant(elem_type))
1604
+
1605
+ # Push capacity
1606
+ if hasattr(node, 'capacity') and node.capacity is not None:
1607
+ self._compile_node(node.capacity)
1608
+ else:
1609
+ self._emit(Opcode.LOAD_CONST, self._add_constant(0))
1610
+
1611
+ # Call __create_channel__(name, element_type, capacity)
1612
+ fn_idx = self._add_constant("__create_channel__")
1613
+ self._emit(Opcode.CALL_NAME, (fn_idx, 3))
1614
+ self._emit(Opcode.POP) # discard result (channel stored internally)
1615
+
1616
+ def _compile_SendStatement(self, node):
1617
+ """Compile send statement: send(channel, value)"""
1618
+ self._compile_node(node.channel_expr)
1619
+ self._compile_node(node.value_expr)
1620
+ fn_idx = self._add_constant("send")
1621
+ self._emit(Opcode.CALL_NAME, (fn_idx, 2))
1622
+ self._emit(Opcode.POP)
1623
+
1624
+ def _compile_ReceiveStatement(self, node):
1625
+ """Compile receive statement: value = receive(channel)"""
1626
+ self._compile_node(node.channel_expr)
1627
+ fn_idx = self._add_constant("receive")
1628
+ self._emit(Opcode.CALL_NAME, (fn_idx, 1))
1629
+
1209
1630
  # ==================== Fallback for unsupported nodes ====================
1210
1631
 
1211
1632
  def __getattr__(self, name):