@pineforge/codegen-pyodide 0.10.0 → 0.10.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.
@@ -20,7 +20,15 @@ reads only attributes established by ``CodeGen.__init__`` (``_uses_drawing``,
20
20
  from __future__ import annotations
21
21
 
22
22
  from ..ast_nodes import FuncCall, Identifier, MemberAccess
23
- from .tables import DRAWING_TYPE_TO_CPP, DRAWING_ARENA, ARRAY_VOID_METHODS as _ARRAY_VOID_METHODS
23
+ from ..symbols import method_receiver_type_name
24
+ from .tables import (
25
+ ARRAY_VOID_METHODS as _ARRAY_VOID_METHODS,
26
+ DRAWING_ARENA,
27
+ DRAWING_TYPE_TO_CPP,
28
+ MATRIX_VOID_METHODS as _MATRIX_VOID_METHODS,
29
+ )
30
+
31
+ _MAP_VOID_METHODS = frozenset({"clear", "put_all"})
24
32
 
25
33
  # ---------------------------------------------------------------------------
26
34
  # Canonical Pine v6 constructor param-name lists (positional order).
@@ -478,26 +486,90 @@ class DrawingVisitor:
478
486
  expression and so cannot be used as a function's return value / RHS.
479
487
 
480
488
  Covers drawing setters/delete/visual-noop (delegated to
481
- ``_drawing_call_is_void``) AND the Pine array MUTATOR methods whose C++
489
+ ``_drawing_call_is_void``), the Pine array MUTATOR methods whose C++
482
490
  lowering is void / an iterator (``array.push/insert/clear/set/fill/
483
- sort/reverse/concat/unshift``) — a function ending in
484
- ``array.clear(x)`` returns void in Pine, so it must emit as a statement
485
- with a default return, not ``return x.clear();``.
491
+ sort/reverse/concat/unshift``), matrix mutators whose runtime lowering
492
+ returns void, and terminal map ``clear``/``put_all`` calls. A function
493
+ ending in one of these calls must emit it as a statement with a default
494
+ return, never ``return <void-expression>;``.
486
495
  """
487
- if self._drawing_call_is_void(node):
488
- return True
489
496
  if not isinstance(node, FuncCall) or not isinstance(node.callee, MemberAccess):
490
497
  return False
491
498
  method = node.callee.member
492
- _fn, ns = self._resolve_callee(node.callee)
493
- if method not in _ARRAY_VOID_METHODS:
499
+ direct_spec = self._type_spec_from_expr(node.callee.object)
500
+ direct_receiver_name = method_receiver_type_name(direct_spec)
501
+ direct_method_info = (
502
+ getattr(self, "_func_info_map", {}).get(
503
+ f"{direct_receiver_name}.{method}"
504
+ )
505
+ if direct_receiver_name is not None
506
+ else None
507
+ )
508
+ # Exact typed user methods have dispatch precedence over every builtin
509
+ # family. Resolve that once before drawing/array/matrix/map terminal
510
+ # classification, including when a parameter named after a namespace
511
+ # (for example ``array`` or ``label``) is the authored receiver.
512
+ if direct_method_info is not None and getattr(
513
+ direct_method_info, "is_udt_method", False
514
+ ):
494
515
  return False
495
- # array.<method>(arr, ...) namespace form OR arr.<method>(...) method
496
- # form on a std::vector receiver.
497
- if ns == "array":
516
+
517
+ if self._drawing_call_is_void(node):
498
518
  return True
499
- recv_spec = self._type_spec_from_expr(node.callee.object)
500
- return recv_spec is not None and recv_spec.kind == "array"
519
+ _fn, ns = self._resolve_callee(node.callee)
520
+
521
+ if method in _MATRIX_VOID_METHODS:
522
+ # Resolve the exact matrix receiver for both established spellings:
523
+ # ``matrix.set(id, ...)`` and ``id.set(...)``.
524
+ functional_namespace = ns == "matrix" and (
525
+ direct_spec is None or direct_spec.kind != "matrix"
526
+ )
527
+ recv_spec = direct_spec
528
+ if functional_namespace:
529
+ recv_spec = (
530
+ self._type_spec_from_expr(node.args[0])
531
+ if node.args
532
+ else None
533
+ )
534
+ if recv_spec is not None and recv_spec.kind == "matrix":
535
+ return True
536
+
537
+ if method in _ARRAY_VOID_METHODS:
538
+ # array.<method>(arr, ...) namespace form OR arr.<method>(...)
539
+ # method form on a std::vector receiver.
540
+ if ns == "array":
541
+ return True
542
+ if direct_spec is not None and direct_spec.kind == "array":
543
+ return True
544
+
545
+ if method not in _MAP_VOID_METHODS:
546
+ return False
547
+ # Preserve the unsupported arbitrary-map-receiver boundary. Namespace
548
+ # functional calls are established; method calls must use a named
549
+ # global/local/parameter receiver.
550
+ if ns == "map":
551
+ # The established namespace-functional form has a positional map
552
+ # receiver. Keyword-only receiver routing remains unsupported
553
+ # and must retain its pre-KI-48g fallback output.
554
+ expected_arity = 1 if method == "clear" else 2
555
+ return len(node.args) == expected_arity and not node.kwargs
556
+ if not isinstance(node.callee.object, Identifier):
557
+ return False
558
+ name = node.callee.object.name
559
+ param_spec = getattr(self, "_current_func_param_specs", {}).get(name)
560
+ if method == "clear":
561
+ if node.args or node.kwargs:
562
+ return False
563
+ elif param_spec is not None and param_spec.kind == "map":
564
+ if not (
565
+ (len(node.args) == 1 and not node.kwargs)
566
+ or (not node.args and set(node.kwargs) == {"id2"})
567
+ ):
568
+ return False
569
+ elif len(node.args) != 1 or node.kwargs:
570
+ return False
571
+ recv_spec = param_spec or self._type_spec_from_expr(node.callee.object)
572
+ return recv_spec is not None and recv_spec.kind == "map"
501
573
 
502
574
  # ------------------------------------------------------------------
503
575
  # _uses_drawing detection + arena caps
@@ -527,6 +599,14 @@ class DrawingVisitor:
527
599
  for spec in self._collection_types.values():
528
600
  if self._spec_mentions_drawing(spec):
529
601
  return True
602
+ for specs in self._func_collection_types.values():
603
+ for spec in specs.values():
604
+ if self._spec_mentions_drawing(spec):
605
+ return True
606
+ for specs in self._block_collection_types.values():
607
+ for spec in specs.values():
608
+ if self._spec_mentions_drawing(spec):
609
+ return True
530
610
  for fields in self._udt_field_type_specs.values():
531
611
  for spec in fields.values():
532
612
  if self._spec_mentions_drawing(spec):