# control_flow

URL: /kida/api/compiler/statements/control_flow/
Section: statements
Description: Control flow statement compilation for Kida compiler.

Provides mixin for compiling control flow statements (if, for).

Uses inline TYPE_CHECKING declarations for host attributes.
See: plan/rfc-mixin-protocol-typing.md

---

> For a complete page index, fetch /kida/llms.txt.

Open LLM text
(/kida/api/compiler/statements/control_flow/index.txt)

Share with AI

Ask Claude
(https://claude.ai/new?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fkida%2Fapi%2Fcompiler%2Fstatements%2Fcontrol_flow%2Findex.txt)

Ask ChatGPT
(https://chatgpt.com/?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fkida%2Fapi%2Fcompiler%2Fstatements%2Fcontrol_flow%2Findex.txt)

Ask Gemini
(https://gemini.google.com/app?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fkida%2Fapi%2Fcompiler%2Fstatements%2Fcontrol_flow%2Findex.txt)

Ask Copilot
(https://copilot.microsoft.com/?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fkida%2Fapi%2Fcompiler%2Fstatements%2Fcontrol_flow%2Findex.txt)

Module

#
`compiler.statements.control_flow`

Control flow statement compilation for Kida compiler.

Provides mixin for compiling control flow statements (if, for).

Uses inline TYPE_CHECKING declarations for host attributes.
See: plan/rfc-mixin-protocol-typing.md

1Class

## Classes

`ControlFlowMixin`

9

▼

Mixin for compiling control flow statements.

Host attributes and cross-mixin dependencies are decl…

Mixin for compiling control flow statements.

Host attributes and cross-mixin dependencies are declared via inline
TYPE_CHECKING blocks.

#### Methods

Internal Methods
9

▼

`_wrap_with_scope`

2

`list[ast.stmt]`

▼

Wrap statements with scope push/pop for block-scoped variables.

When source_no…

`def _wrap_with_scope(self, body_stmts: list[ast.stmt], source_nodes: Any = None) -> list[ast.stmt]`

Wrap statements with scope push/pop for block-scoped variables.

When source_nodes is provided and contains no Set, Let, Capture, or
Export nodes, the scope push/pop is skipped entirely — avoiding
unnecessary dict allocation and list append/pop per block entry.

Generates (when scoping is needed):
_scope_stack.append({})
... body statements ...
_scope_stack.pop()

##### Parameters

Name
Type
Description

`body_stmts`
`—`

`source_nodes`
`—`

Default:`None`

##### Returns

`list[ast.stmt]`

`_compile_break`

1

`list[ast.stmt]`

▼

Compile {% break %} loop control.

Part of RFC: kida-modern-syntax-features.

`def _compile_break(self, node: Node) -> list[ast.stmt]`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`list[ast.stmt]`

`_compile_continue`

1

`list[ast.stmt]`

▼

Compile {% continue %} loop control.

Part of RFC: kida-modern-syntax-features.

`def _compile_continue(self, node: Node) -> list[ast.stmt]`

Compile {% continue %} loop control.

Part of RFC: kida-modern-syntax-features.

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`list[ast.stmt]`

`_compile_while`

1

`list[ast.stmt]`

▼

Compile {% while cond %}...{% end %} loop.

**Generates:**
while condition:
…

`def _compile_while(self, node: While) -> list[ast.stmt]`

Compile {% while cond %}...{% end %} loop.

Generates:
while condition:
... body ...

Part of RFC: kida-2.0-moonshot (While Loops).

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`list[ast.stmt]`

`_compile_if`

1

`list[ast.stmt]`

▼

Compile {% if %} conditional.

`def _compile_if(self, node: If) -> list[ast.stmt]`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`list[ast.stmt]`

`_uses_loop_variable`

1

`bool`

▼

Check if any node in the tree references the 'loop' variable.

This enables laz…

`def _uses_loop_variable(self, nodes: Any) -> bool`

Check if any node in the tree references the 'loop' variable.

This enables lazy LoopContext optimization: when loop.index, loop.first,
etc. are not used, we can skip creating the LoopContext wrapper and
iterate directly over the items (1.80x faster per benchmark).

##### Parameters

Name
Type
Description

`nodes`
`—`

A node or sequence of nodes to check

##### Returns

`bool`

True if 'loop' is referenced anywhere in the tree

`_compile_for`

1

`list[ast.stmt]`

▼

Compile {% for %} loop with optional LoopContext.

Generates one of three forms…

`def _compile_for(self, node: For) -> list[ast.stmt]`

Compile {% for %} loop with optional LoopContext.

Generates one of three forms based on loop.* usage and {% empty %} presence:

When loop.* IS used (requires materialized list):
_iter_source = iterable
_loop_items = list(_iter_source) if _iter_source is not None else []
if _loop_items:
loop = _LoopContext(_loop_items)
for item in loop:
... body ...
else:
... empty block ...

When loop.* is NOT used and has {% empty %} (sentinel pattern):
_iter_source = iterable
_had_items = False
if _iter_source is not None:
for item in _iter_source:
_had_items = True
... body ...
if not _had_items:
... empty block ...

When loop.* is NOT used and no {% empty %} (direct iteration):
_iter_source = iterable
if _iter_source is not None:
for item in _iter_source:
... body ...

Optimization: Loop variables are tracked as locals and accessed
directly (O(1) LOAD_FAST) instead of through ctx dict lookup.

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`list[ast.stmt]`

`_compile_async_for`

1

`list[ast.stmt]`

▼

Compile {% async for %} loop with AsyncLoopContext.

Unlike sync _compile_for()…

`def _compile_async_for(self, node: AsyncFor) -> list[ast.stmt]`

Compile {% async for %} loop with AsyncLoopContext.

Unlike sync _compile_for(), this does NOT call list() on the iterable.
Instead it generates ast.AsyncFor for native async iteration and uses
a boolean flag for {% empty %} detection.

When compiling for sync functions (_async_mode=False), emits a pass
placeholder. The Template-level guard prevents sync rendering of async
templates, so this code path is unreachable at runtime.

Generates (async mode):
_had_items_N = False
loop = _AsyncLoopContext() # if loop.* used
async for item in iterable:
_had_items_N = True
loop.advance(item) # if loop.* used
... body ...
if not _had_items_N:
... empty block ...

Part of RFC: rfc-async-rendering.

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`list[ast.stmt]`

`_extract_names`

1

`list[str]`

▼

Extract variable names from a target expression.

`def _extract_names(self, node: Node) -> list[str]`

##### Parameters

Name
Type
Description

`node`
`—`

##### Returns

`list[str]`
