Module

compiler.expressions

Expression compilation for Kida compiler.

Provides mixin for compiling Kida expression AST nodes to Python AST expressions.

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

Classes

ExpressionCompilationMixin 37 ▼
Mixin for compiling expressions. Host attributes and cross-mixin dependencies are declared via inl…

Mixin for compiling expressions.

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

Attributes

Name Type Description
_EXPR_DISPATCH ClassVar[dict[str, str]] —
_STORE_METHODS frozenset[str] —

Methods

Internal Methods 35 ▼
_precomputed_ref 1 ast.Name ▼
Return an ``ast.Name`` referencing a precomputed module-level binding. Non-con…
def _precomputed_ref(self, value: object) -> ast.Name

Return anast.Namereferencing a precomputed module-level binding.

Non-constant-safe values (dict, list, set, custom objects) cannot be stored inast.Constantnodes. Instead they are collected during compilation and injected into theexec() namespace as _pc_N.

Parameters
Name Type Description
value —
Returns
ast.Name
_get_filter_suggestion 1 str | None ▼
Find closest matching filter name for typo suggestions.
def _get_filter_suggestion(self, name: str) -> str | None
Parameters
Name Type Description
name —
Returns
str | None
_get_test_suggestion 1 str | None ▼
Find closest matching test name for typo suggestions.
def _get_test_suggestion(self, name: str) -> str | None
Parameters
Name Type Description
name —
Returns
str | None
_make_deferred_lambda 1 ast.Lambda ▼
Wrap an expression in a zero-arg lambda for deferred evaluation. Used by ``_is…
def _make_deferred_lambda(self, expr: ast.expr) -> ast.Lambda

Wrap an expression in a zero-arg lambda for deferred evaluation.

Used by_is_defined, _default_safe, and _null_coalesce to catchUndefinedErrorat runtime without evaluating the expression eagerly.

Parameters
Name Type Description
expr —
Returns
ast.Lambda
_is_potentially_string 1 bool ▼
Check if node could produce a string value (macro call, filter chain). Used to…
def _is_potentially_string(self, node: Node) -> bool

Check if node could produce a string value (macro call, filter chain).

Used to determine when numeric coercion is needed for arithmetic operations. Recursively checks nested expressions to catch Filter nodes inside parentheses.

This handles cases like (a | length) + (b | length) where the left/right operands are Filter nodes that need numeric coercion.

Parameters
Name Type Description
node —
Returns
bool
_wrap_coerce_numeric 1 ast.expr ▼
Wrap expression in _coerce_numeric() call for arithmetic safety. Ensures that …
def _wrap_coerce_numeric(self, expr: ast.expr) -> ast.expr

Wrap expression in _coerce_numeric() call for arithmetic safety.

Ensures that Markup objects (from macros) are converted to numbers before arithmetic operations, preventing string multiplication.

Parameters
Name Type Description
expr —
Returns
ast.expr
_compile_expr 2 ast.expr ▼
Compile expression node to Python AST expression. O(1) dict dispatch by ``type…
def _compile_expr(self, node: Node, store: bool = False) -> ast.expr

Compile expression node to Python AST expression.

O(1) dict dispatch bytype(node).__name__.

Parameters
Name Type Description
node —
store — Default:False
Returns
ast.expr
_compile_const 1 ast.expr ▼
Compile constant literal.
def _compile_const(self, node: Const) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_name 2 ast.expr ▼
Compile variable reference.
def _compile_name(self, node: Name, *, store: bool = False) -> ast.expr
Parameters
Name Type Description
node —
store — Default:False
Returns
ast.expr
_compile_tuple 2 ast.expr ▼
Compile tuple expression.
def _compile_tuple(self, node: Tuple, *, store: bool = False) -> ast.expr
Parameters
Name Type Description
node —
store — Default:False
Returns
ast.expr
_compile_list 1 ast.expr ▼
Compile list expression.
def _compile_list(self, node: List) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_list_comp 1 ast.expr ▼
Compile list comprehension.
def _compile_list_comp(self, node: ListComp) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_dict 1 ast.expr ▼
Compile dict expression.
def _compile_dict(self, node: Dict) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_getattr 1 ast.expr ▼
Compile attribute access (obj.attr).
def _compile_getattr(self, node: Getattr) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_getitem 1 ast.expr ▼
Compile subscript access (obj[key]).
def _compile_getitem(self, node: Getitem) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_slice 1 ast.expr ▼
Compile slice expression (start:stop:step).
def _compile_slice(self, node: Slice) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_test 1 ast.expr ▼
Compile test expression (is defined, is none, etc.).
def _compile_test(self, node: Test) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_func_call 1 ast.expr ▼
Compile function call expression.
def _compile_func_call(self, node: FuncCall) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_runtime_call 3 ast.Call ▼
Compile a template call through policy only for sandboxed environments.
def _compile_runtime_call(self, func: ast.expr, args: list[ast.expr], keywords: list[ast.keyword]) -> ast.Call
Parameters
Name Type Description
func —
args —
keywords —
Returns
ast.Call
_compile_filter 1 ast.expr ▼
Compile filter expression (value | filter_name).
def _compile_filter(self, node: Filter) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_binop 1 ast.expr ▼
Compile binary operation.
def _compile_binop(self, node: BinOp) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_unaryop 1 ast.expr ▼
Compile unary operation.
def _compile_unaryop(self, node: UnaryOp) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_compare 1 ast.expr ▼
Compile comparison expression.
def _compile_compare(self, node: Compare) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_boolop 1 ast.expr ▼
Compile boolean operation (and/or).
def _compile_boolop(self, node: BoolOp) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_cond_expr 1 ast.expr ▼
Compile conditional (ternary) expression.
def _compile_cond_expr(self, node: CondExpr) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_await 1 ast.expr ▼
Compile await expression.
def _compile_await(self, node: Await) -> ast.expr
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_null_coalesce 1 ast.expr ▼
Compile a ?? b to handle both None and undefined variables. Uses _null_coalesc…
def _compile_null_coalesce(self, node: NullCoalesce) -> ast.expr

Compile a ?? b to handle both None and undefined variables.

Uses _null_coalesce helper to catch UndefinedError for undefined variables. Part of RFC: kida-modern-syntax-features.

The helper is called as:

_null_coalesce(lambda: a, lambda: b)

This allows:

  • a ?? b to return b if a is undefined (UndefinedError)
  • a ?? b to return b if a is None
  • a ?? b to return a if a is any other value (including falsy: 0, '', [])
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_optional_getattr 1 ast.expr ▼
Compile obj?.attr using walrus operator to avoid double evaluation. obj?.attr …
def _compile_optional_getattr(self, node: OptionalGetattr) -> ast.expr

Compile obj?.attr using walrus operator to avoid double evaluation.

obj?.attr compiles to: '' if (_oc := obj) is None else (_oc_val := _getattr_none(_oc, 'attr')) if _oc_val is not None else ''

The double check ensures that:

  1. If obj is None, return ''
  2. If obj.attr is None, return '' (for output) but preserve None for ??

For null coalescing to work, we need a different approach: the optional chain preserves None so ?? can check it, but for direct output, None becomes ''.

Actually, we return None but rely on the caller to handle None → '' conversion. For output, the expression is wrapped differently.

Simplified: Return None when short-circuiting, let output handle conversion.

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

Parameters
Name Type Description
node —
Returns
ast.expr
_compile_optional_getitem 1 ast.expr ▼
Compile obj?[key] using walrus operator to avoid double evaluation. obj?[key] …
def _compile_optional_getitem(self, node: OptionalGetitem) -> ast.expr

Compile obj?[key] using walrus operator to avoid double evaluation.

obj?[key] compiles to: None if (_oc := obj) is None else _getitem_none(_oc, key)

Routes through_getitem_none so Mapping receivers return Noneon missing keys (v0.8.0+ semantics), while Sequence/out-of-range accesses still raise understrict_undefined.

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

Parameters
Name Type Description
node —
Returns
ast.expr
_compile_optional_method_call 3 ast.expr ▼
Compile obj?.method(*args, **kwargs) with short-circuit. When obj is None or o…
def _compile_optional_method_call(self, opt_getattr: OptionalGetattr, args: Sequence[Any], kwargs: dict[str, Any]) -> ast.expr

Compile obj?.method(*args, **kwargs) with short-circuit.

When obj is None or obj.method is UNDEFINED, return None without calling. Uses _optional_call(callee, *args, **kwargs) helper.

Parameters
Name Type Description
opt_getattr —
args —
kwargs —
Returns
ast.expr
_compile_range 1 ast.expr ▼
Compile range literal to range() call. 1..10 → range(1, 11) # inclusiv…
def _compile_range(self, node: Range) -> ast.expr

Compile range literal to range() call.

1..10 → range(1, 11) # inclusive 1...11 → range(1, 11) # exclusive 1..10 by 2 → range(1, 11, 2)

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

Parameters
Name Type Description
node —
Returns
ast.expr
_compile_inlined_filter 1 ast.Call ▼
Compile inlined filter to direct method call. Generates: _str(value).method(*a…
def _compile_inlined_filter(self, node: InlinedFilter) -> ast.Call

Compile inlined filter to direct method call.

Generates: _str(value).method(*args)

This replaces filter dispatch overhead with a direct method call, providing ~5-10% speedup for filter-heavy templates.

Parameters
Name Type Description
node —
Returns
ast.Call
_compile_optional_filter 1 ast.expr ▼
Compile expr ?| filter — skip filter if value is None. ``expr ?| upper`` compi…
def _compile_optional_filter(self, node: OptionalFilter) -> ast.expr

Compile expr ?| filter — skip filter if value is None.

expr ?| uppercompiles to::

None if (_of_N := expr) is None else _filters['upper'](_of_N)
Parameters
Name Type Description
node —
Returns
ast.expr
_compile_pipeline 1 ast.expr ▼
Compile pipeline: expr |> filter1 |> filter2. Pipelines compile to nested filt…
def _compile_pipeline(self, node: Pipeline) -> ast.expr

Compile pipeline: expr |> filter1 |> filter2.

Pipelines compile to nested filter calls using the _filters dict, exactly like regular filter chains. The difference is purely syntactic.

expr |> a |> b(x) → _filters['b'](_filters['a'](expr), x)

Validates filter existence at compile time (same as Filter nodes).

Parameters
Name Type Description
node —
Returns
ast.expr
_compile_safe_pipeline 1 ast.expr ▼
Compile safe pipeline: expr ?|> filter1 ?|> filter2. None-propagating: each st…
def _compile_safe_pipeline(self, node: SafePipeline) -> ast.expr

Compile safe pipeline: expr ?|> filter1 ?|> filter2.

None-propagating: each step checks for None before applying the filter.

expr ?|> a ?|> b(x)compiles to::

_sp_2 = None if (_sp_1 := expr) is None else _filters['a'](_sp_1)
None if _sp_2 is None else _filters['b'](_sp_2, x)
Parameters
Name Type Description
node —
Returns
ast.expr

Functions

_is_constant_safe 1 bool ▼
Return True if *value* can be stored in an ``ast.Constant`` node.
def _is_constant_safe(value: object) -> bool
Parameters
Name Type Description
value object
Returns
bool