#!/usr/bin/env python3.9 # -*- coding: utf-8 -*- """ JFinal For - For Directive """ from typing import Optional from .Stat import Stat, StatList from ..Scope import Scope from ...Env import Env class For(Stat): """For loop directive""" def __init__(self, var_name: str, iter_expr: str, body: Stat): """ Initialize for directive Args: var_name: Loop variable name iter_expr: Expression to iterate over body: Loop body """ self._var_name = var_name self._iter_expr = iter_expr self._body = body def exec(self, env: Env, scope: Scope, writer) -> None: """ Execute for loop Args: env: Template environment scope: Execution scope writer: Output writer """ try: # Wrap dict values to allow attribute access def wrap_dict(d): """Wrap a dict to allow attribute access""" if isinstance(d, dict): # Create a wrapper that allows both dot access and bracket access class DictWrapper: def __init__(self, data): self.__dict__ = data def __getitem__(self, key): return data[key] return DictWrapper(d) return d # Create wrapped vars similar to SimpleExprList.eval() wrapped_vars = {} for key, value in scope._data.items(): if isinstance(value, dict): wrapped_vars[key] = wrap_dict(value) elif isinstance(value, list): # Wrap dictionaries in lists wrapped_vars[key] = [wrap_dict(item) for item in value] else: wrapped_vars[key] = value # Get the iterable from the expression iterable = eval(self._iter_expr, {}, wrapped_vars) # Convert iterable to list for easier manipulation items = list(iterable) size = len(items) # Create a ForLoopInfo class to hold loop information class ForLoopInfo: def __init__(self, size, index, outer=None): self._size = size self._index = index self._outer = outer @property def size(self): return self._size @property def index(self): return self._index @property def count(self): return self._index + 1 @property def first(self): return self._index == 0 @property def last(self): return self._index == self._size - 1 @property def odd(self): return self._index % 2 == 0 @property def even(self): return self._index % 2 == 1 @property def outer(self): return self._outer # Get outer for info if exists outer_for = scope._data.get("for") # Iterate over the items for index, item in enumerate(items): # Create for loop info for_loop = ForLoopInfo(size, index, outer_for) # Create a new scope for this iteration loop_scope = Scope(scope._data.copy(), scope._shared_objects.copy()) loop_scope.set_parent(scope) loop_scope._data[self._var_name] = item loop_scope._data["for"] = for_loop # Execute the loop body if self._body: self._body.exec(env, loop_scope, writer) except Exception as e: # Handle errors gracefully writer.write(f"Error in for loop: {e}") def __repr__(self) -> str: return f"For(var={self._var_name}, expr={self._iter_expr})"