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:
- A block is a list of statements and nothing else. There is no
delimiter to get wrong, so
Classcarries abodyand the renderer owns the indentation. A body that renders to nothing is not legal Python, which is whyStatement::Passexists as a node a rule emits rather than something the renderer inserts. Typeis separate fromExpralthough Python spells them the same. Keeping them apart is what stops a rule buildingOptional[a, b]orlist[1]: the tree can only hold the forms an annotation actually takes. Where an annotation genuinely needs a value — the metadata of aType::Annotated— anExprappears inside the type, which is exactly whattyping.Annotatedis for.- There is no visibility and no modifier. Python has neither, so
crate::common::Visibilityis 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: Typeorname: Type = value.- Assign
name = value.- Class
class Name(bases): ...- Decorator
- One decorator:
@path(args). - Function
def name(params) -> returns: ...- Import
Name - 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
importline. - 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.