Skip to content

Commit 2162a32

Browse files
committed
Refactor expressions interpreter
1 parent e4ffee0 commit 2162a32

File tree

1 file changed

+150
-95
lines changed

1 file changed

+150
-95
lines changed

monic/expressions/interpreter.py

Lines changed: 150 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -534,9 +534,153 @@ def visit_Assign(self, node: ast.Assign) -> None:
534534
target = node.targets[0]
535535
self._handle_unpacking_target(target, value)
536536

537-
def _handle_unpacking_target(self, target: ast.AST, value: t.Any) -> None:
537+
def _handle_name_target(self, target: ast.Name, value: t.Any) -> None:
538+
"""Handle simple name assignment with scope handling.
539+
540+
Args:
541+
target: Name AST node
542+
value: Value to assign
543+
"""
544+
self._set_name_value(target.id, value)
545+
546+
def _handle_attribute_target(
547+
self, target: ast.Attribute, value: t.Any
548+
) -> None:
549+
"""Handle attribute assignment (e.g., self.x = value).
550+
551+
Args:
552+
target: Attribute AST node
553+
value: Value to assign
554+
"""
555+
obj = self.visit(target.value)
556+
setattr(obj, target.attr, value)
557+
558+
def _handle_subscript_target(
559+
self, target: ast.Subscript, value: t.Any
560+
) -> None:
561+
"""Handle subscript assignment (e.g., lst[0] = value).
562+
563+
Args:
564+
target: Subscript AST node
565+
value: Value to assign
566+
"""
567+
container = self.visit(target.value)
568+
index = self.visit(target.slice)
569+
container[index] = value
570+
571+
def _handle_starred_unpacking(
572+
self,
573+
target_elts: list[ast.expr],
574+
value: t.Any,
575+
star_index: int,
576+
starred_target: ast.Starred,
577+
) -> None:
578+
"""Handle starred unpacking in sequence assignments.
579+
580+
Args:
581+
target_elts: List of target elements
582+
value: Value being unpacked
583+
star_index: Index of the starred expression
584+
starred_target: The starred target node
585+
586+
Raises:
587+
ValueError: If there are not enough values to unpack
588+
UnsupportedUnpackingError: If unpacking pattern is not supported
589+
"""
590+
iter_value = iter(value)
591+
592+
# Handle elements before the starred expression
593+
before_elements = target_elts[:star_index]
594+
for tgt in before_elements:
595+
try:
596+
self._handle_unpacking_target(tgt, next(iter_value))
597+
except StopIteration as e:
598+
raise ValueError("Not enough values to unpack") from e
599+
600+
# Collect remaining elements for the starred target
601+
starred_values = list(iter_value)
602+
603+
# Calculate how many elements should be in the starred part
604+
after_star_count = len(target_elts) - star_index - 1
605+
606+
# If there are more elements after the starred part
607+
if after_star_count > 0:
608+
# Make sure there are enough elements
609+
if len(starred_values) < after_star_count:
610+
raise ValueError("Not enough values to unpack")
611+
612+
# Separate starred values
613+
starred_list = starred_values[:-after_star_count]
614+
after_star_values = starred_values[-after_star_count:]
615+
616+
# Assign starred target
617+
if isinstance(starred_target.value, ast.Name):
618+
self._set_name_value(starred_target.value.id, starred_list)
619+
620+
# Assign elements after starred
621+
after_elements = target_elts[star_index + 1 :]
622+
for tgt, val in zip(after_elements, after_star_values):
623+
self._handle_unpacking_target(tgt, val)
624+
else:
625+
# If no elements after starred, just assign the rest
626+
# to the starred target
627+
if isinstance(starred_target.value, ast.Name):
628+
self._set_name_value(starred_target.value.id, starred_values)
629+
630+
def _handle_sequence_unpacking(
631+
self,
632+
target: ast.Tuple | ast.List,
633+
value: t.Any,
634+
) -> None:
635+
"""Handle sequence (tuple/list) unpacking.
636+
637+
Args:
638+
target: Tuple or List AST node
639+
value: Value to unpack
640+
641+
Raises:
642+
ValueError: If value cannot be unpacked
643+
UnsupportedUnpackingError: If unpacking pattern is not supported
538644
"""
539-
Handle different types of unpacking targets.
645+
try:
646+
if not hasattr(value, "__iter__"):
647+
raise ValueError("Cannot unpack non-iterable value")
648+
649+
# Check for starred expressions (extended unpacking)
650+
starred_indices = [
651+
i
652+
for i, elt in enumerate(target.elts)
653+
if isinstance(elt, ast.Starred)
654+
]
655+
656+
if len(starred_indices) > 1:
657+
raise UnsupportedUnpackingError(
658+
"Cannot use multiple starred expressions in assignment"
659+
)
660+
661+
if starred_indices:
662+
# Handle starred unpacking
663+
star_index = starred_indices[0]
664+
starred_target = t.cast(ast.Starred, target.elts[star_index])
665+
self._handle_starred_unpacking(
666+
target.elts, value, star_index, starred_target
667+
)
668+
else:
669+
# Standard unpacking without starred expression
670+
value_list = list(value)
671+
if len(value_list) < len(target.elts):
672+
raise ValueError("Not enough values to unpack")
673+
elif len(value_list) > len(target.elts):
674+
raise ValueError("Too many values to unpack")
675+
676+
# Unpack each element
677+
for tgt, val in zip(target.elts, value):
678+
self._handle_unpacking_target(tgt, val)
679+
except (TypeError, ValueError) as e:
680+
raise UnsupportedUnpackingError(str(e)) from e
681+
682+
def _handle_unpacking_target(self, target: ast.AST, value: t.Any) -> None:
683+
"""Handle different types of unpacking targets.
540684
541685
Args:
542686
target: AST node representing the unpacking target
@@ -547,102 +691,13 @@ def _handle_unpacking_target(self, target: ast.AST, value: t.Any) -> None:
547691
encountered
548692
"""
549693
if isinstance(target, ast.Name):
550-
# Simple name assignment with scope handling
551-
self._set_name_value(target.id, value)
694+
self._handle_name_target(target, value)
552695
elif isinstance(target, ast.Attribute):
553-
# Handle attribute assignment (e.g., self.x = value)
554-
obj = self.visit(target.value)
555-
setattr(obj, target.attr, value)
696+
self._handle_attribute_target(target, value)
556697
elif isinstance(target, ast.Subscript):
557-
# Handle subscript assignment (e.g., lst[0] = value)
558-
container = self.visit(target.value)
559-
index = self.visit(target.slice)
560-
container[index] = value
698+
self._handle_subscript_target(target, value)
561699
elif isinstance(target, (ast.Tuple, ast.List)):
562-
# Tuple or list unpacking
563-
try:
564-
# Unpack the value
565-
if not hasattr(value, "__iter__"):
566-
raise ValueError("Cannot unpack non-iterable value")
567-
568-
iter_value = iter(value)
569-
570-
# Check for starred expressions (extended unpacking)
571-
starred_indices = [
572-
i
573-
for i, elt in enumerate(target.elts)
574-
if isinstance(elt, ast.Starred)
575-
]
576-
577-
if len(starred_indices) > 1:
578-
raise UnsupportedUnpackingError(
579-
"Cannot use multiple starred expressions in assignment"
580-
)
581-
582-
if starred_indices:
583-
# Handle starred unpacking
584-
star_index = starred_indices[0]
585-
starred_target = t.cast(
586-
ast.Starred, target.elts[star_index]
587-
)
588-
589-
# Handle elements before the starred expression
590-
before_elements = target.elts[:star_index]
591-
for tgt in before_elements:
592-
try:
593-
self._handle_unpacking_target(tgt, next(iter_value))
594-
except StopIteration as e:
595-
raise ValueError(
596-
"Not enough values to unpack"
597-
) from e
598-
599-
# Collect remaining elements for the starred target
600-
starred_values = list(iter_value)
601-
602-
# Calculate how many elements should be in the starred part
603-
# after the current group
604-
after_star_count = len(target.elts) - star_index - 1
605-
606-
# If there are more elements after the starred part
607-
if after_star_count > 0:
608-
# Make sure there are enough elements
609-
if len(starred_values) < after_star_count:
610-
raise ValueError("Not enough values to unpack")
611-
612-
# Separate starred values
613-
starred_list = starred_values[:-after_star_count]
614-
after_star_values = starred_values[-after_star_count:]
615-
616-
# Assign starred target
617-
if isinstance(starred_target.value, ast.Name):
618-
self._set_name_value(
619-
starred_target.value.id, starred_list
620-
)
621-
622-
# Assign elements after starred
623-
after_elements = target.elts[star_index + 1 :]
624-
for tgt, val in zip(after_elements, after_star_values):
625-
self._handle_unpacking_target(tgt, val)
626-
else:
627-
# If no elements after starred, just assign the rest
628-
# to the starred target
629-
if isinstance(starred_target.value, ast.Name):
630-
self._set_name_value(
631-
starred_target.value.id, starred_values
632-
)
633-
else:
634-
# Standard unpacking without starred expression
635-
value_list = list(value)
636-
if len(value_list) < len(target.elts):
637-
raise ValueError("Not enough values to unpack")
638-
elif len(value_list) > len(target.elts):
639-
raise ValueError("Too many values to unpack")
640-
641-
# Unpack each element
642-
for tgt, val in zip(target.elts, value):
643-
self._handle_unpacking_target(tgt, val)
644-
except (TypeError, ValueError) as e:
645-
raise UnsupportedUnpackingError(str(e)) from e
700+
self._handle_sequence_unpacking(target, value)
646701
else:
647702
raise UnsupportedUnpackingError(
648703
f"Unsupported unpacking target type: {type(target).__name__}"

0 commit comments

Comments
 (0)