Skip to main content

Module python

Module python 

Source
Expand description

The Python concrete syntax tree. Spec §1.

Python has no braces, no declarations and no type syntax of its own — an annotation is an ordinary expression. The tree therefore differs from the other four in three ways, and each is Python’s rather than a simplification:

  1. A block is a list of statements and nothing else. There is no delimiter to get wrong, so Class carries a body and the renderer owns the indentation. A body that renders to nothing is not legal Python, which is why Statement::Pass exists as a node a rule emits rather than something the renderer inserts.
  2. Type is separate from Expr although Python spells them the same. Keeping them apart is what stops a rule building Optional[a, b] or list[1]: the tree can only hold the forms an annotation actually takes. Where an annotation genuinely needs a value — the metadata of a Type::Annotated — an Expr appears inside the type, which is exactly what typing.Annotated is for.
  3. There is no visibility and no modifier. Python has neither, so crate::common::Visibility is not used here; a leading underscore is a convention and is part of the identifier.

§What is here, and what the second pass added

The tree started as the types an IDL file becomes and nothing else. The wire codecs are the second pass, and they are what Function, Statement::If, Statement::For, Statement::While, Statement::Break, Statement::Return, Statement::Set, Expr::Attribute, Expr::Index, Expr::Unary, Expr::Paren and Expr::List arrived for: crates/lang-rules/src/emit/python.rs builds every one of them out of the shared walk, and each has a renderer test in zerodds_lang_render::python.

What is here is still bounded by that rule in the other direction — every form has a renderer test, so no arm of zerodds_lang_render::python is code that only claims to work. A field or a variant with no test and no producer would be exactly that, which is why Class has no metaclass= and Function has no *args, no **kwargs and no async.

§A binding and a store are two statements, not one

Python spells both x = y. They are apart here — Statement::Assign binds a name and Statement::Set writes into a place — because only one of them can be either. RED = 0 inside an enum.IntEnum is a member and has to be a name; v.field = x and v[i] = x are the codec’s, and neither is a name a scope binds. A single node taking an arbitrary target would let a materialiser declare an enumeration member called v[0], and nothing between it and the file would notice.

Structs§

Annotated
name: Type or name: Type = value.
Assign
name = value.
Class
class Name(bases): ...
Decorator
One decorator: @path(args).
Function
def name(params) -> returns: ...
ImportName
One name in a from ... import ....
Keyword
name=value, in a call, a decorator or a class header.
Module
A Python source file.
Param
One parameter of a Function.

Enums§

Arg
One argument of a call or a decorator.
Expr
An expression.
Import
One import line.
Statement
One statement of a module or a class body.
Type
A type annotation.

Constants§

RESERVED
Every name an escape has to move out of the way of.

Functions§

escape_name
The name an IDL identifier takes in Python.