TileFoundry Spec - Parser¶
The Parser accepts authored Python functions and produces HIR or TIR through one typed API.
1. Public API¶
Authored DSL source imports its authoring surface with from tilefoundry.dsl import *.
Tensor[...] is parser annotation syntax, not the IR TensorType carrier.
@module executes its Python class body and finalizes the collected Function declarations,
child Modules, and ordinary methods. Module authoring is two-phase: class execution records
Function, specialization, and converter declarations; finalization attaches all child Modules
and puts them in parser scope before parsing Functions in source order. @func produces an HIR
Function; @prim_func produces TIR (tir §1.3).
Explicit PrimFunction targets survive deferred parsing.
specialize and converter register variants and weight converters on an existing HIR Function.
def parse_function(
fn: FunctionType, context: FuncParserContext
) -> hir.Function | tir.PrimFunction: ...
1.1 HIR Return Contracts¶
An ordinary HIR function and a specialization variant MAY omit -> return-type;
an ordinary function then records its inferred body.type as Function.return_type.
When either declares an annotation, the parser MUST require
types_compatible(annotation, body.type). This is directional compatibility,
not a parser-only equality relation: an annotation with layout=None leaves
layout unconstrained according to the shared type rule.
A pass HIR function is a dispatch prototype and MUST declare -> return-type.
That annotation is its Function.return_type and callable return type. Every
variant body MUST be compatible with that base return type; its own IR return
type remains the exact base type so all variants share one dispatch signature.
Tensor[...] without a storage slot constructs storage=GMEM, including in a
return annotation. It is not an unspecified-storage spelling. Consequently an
SMEM body under -> Tensor[...] must explicitly return a GMEM result; the
parser reports both the annotation and inferred body type, with the authored
function location, when they are incompatible. tuple[...] annotations are
accepted and apply this same compatibility rule recursively to every field.
1.2 Source Span Metadata¶
Every parser-authored Call reachable from a Function body carries SourceSpanMetadata for
the AST expression that constructed it. A parent match fills only Calls without a span, so it
cannot replace a more precise child span. Traversal follows Call operands and IR Tuple
values, but does not assign source identity to shared lexical Var values. Source spans use
physical source-file coordinates with a one-based start column.
1.3 Tuple Binding Metadata¶
For a, b = producer(...), detached TupleGetItem values with scalar inputs 0 and 1
carry the respective target Name spans (a and b) and matching BindingMetadata; later
reads do not replace that identity. A multi-carry loop's derived projections carry the for
statement span and their carry binding name.
1.4 Context and Diagnostics¶
FuncParserContext carries the dialect, Function role, closure, topology scope, target, and
optional base/key for one parse. Call types inferred while parsing use that target's rules
(visitor-registry §4), so in a Module that
declares its Target a type the parser records is the type the built Module later infers. A
pending Module supplies its declared Target to the parser's current call frame.
FunctionRole is ROOT, VARIANT, or CONVERTER.
ParseError is the single authored-source diagnostic type and includes source location and
recursive parse situation. These are the only public parser symbols.
Instance authoring and call contracts follow core-ir §1.3 and §2.1. Numeric subscripts become Constant expressions; static index checks run only in type inference. Call construction MUST NOT project an indexed slot's return type before validation. Retained TIR calls produce unit-valued statements checked in the enclosing Module context.
1.5 Mesh declarations and region captures¶
Mesh declarations are values. Mesh(...) constructs the compile-time domain
object used by layout sugar and by a MeshRegion; it is not itself a runtime
expression in the HIR value graph. A with Mesh(...) statement uses that value
to delimit an execution domain and does not describe the placement of its
result. An as name binding is lexical: it is available inside the with
body and expires when the statement ends. When a body changes a name visible
before the with, or binds a new name in its live-out (the conservative set of
names read after it), each escaping name is rebound to the enclosing MeshRegion
result (a tuple region with TupleGetItem projections when several names escape);
unchanged captures and new names used
only inside the body remain local to the scope. Names read inside either a
MeshRegion or LoopRegion but bound outside it are captured as args, with a
fresh params binding used by the body. LoopRegion places
carry slots first and excludes its own induction and carry names from captures.
Capture is performed one region boundary at a time, so nested regions pass a
value through each door.
In an authored HIR function that opens a mesh, every authored runtime Call
runs inside a mesh scope. This includes operation and function calls, operator
expressions, tensor slices, and explicit tuple subscripts, even for UMAT scalar
results. An unscoped Call raises ParseError at its source expression and directs
the author to move it inside with Mesh(...). Function parameters, Python
constants, compile-time mesh declarations, and parser-generated result projections
do not require an execution scope. A function-level mesh= covers the whole body.
HIR functions that open no mesh remain logical programs; TIR is unaffected by
this restriction.
A function call inside a mesh supplies that execution scope to its callee. The callee may omit its own mesh or declare a region that composes with the call-site scope (hir §1.1). The parser checks authored call sites lexically; it does not exempt an unscoped function call because its callee contains a mesh. Authors may place the orchestration calls inside a mesh and keep each helper's operations in the inherited or explicitly declared scope.
When a captured name denotes a tile window slice(iv, iv + step, 1), the parser
captures the induction Expr and reconstructs the window with its new parameter.
Tile windows support window ± c (translate start/stop), window * c, and c * window
(scale start, stop, and stride, preserving the element count). Division,
floor division, remainder, and operations between two windows MUST raise
ParseError; a Python slice MUST NOT enter the HIR expression graph.
Non-window index arithmetic MUST reject / with a diagnostic directing the
author to integer division //, rather than returning a float.
A loop body holds with Mesh(...) statements, and the loop carries what one
binds. A with exports changed names visible on entry, including loop carry
parameters, and new names read by following statements. Bindings produced by
nested loops or mesh scopes follow the same rule. A TIR loop
bound that is not a literal is read as the dimension arithmetic the loop was
lowered from, so a bound naming a mesh coordinate reads back as it was printed.
2. Syntax and Rules¶
Negative integer and floating-point literals are single Constant values.
Negating a name or a runtime expression constructs a Unary(NEG) Call.
Tuple subscripting lowers stages[index] to TupleGetItem(stages, index).
Literal negative indices are normalized against the tuple arity before lowering.
A non-literal scalar index is accepted only when type inference can establish one
common field type for every possible result.
2.1 Syntax¶
; root: function
; literal: Python ast.Constant syntax, e.g. 1, "bf16", or None
; name: Python variable name; primary: name/attribute base for calls and subscripts
; expression: Python syntax composed from literals, names, primaries, and operators
; runtime-expression: expression lowered to a TileFoundry IR Expr
mesh-axis ::= identifier
| identifier '.' identifier
dim-expr ::= integer-literal
| identifier
| primary '.' identifier
| dim-expr ('+' | '-' | '*' | '//' | '%') dim-expr
| (identifier | primary '.' identifier) '(' (dim-expr (',' dim-expr)*)?
')'
layout-dims ::= '(' ((expression '@' ('(' mesh-axis (',' mesh-axis)* ')' | mesh-axis) |
dim-expr) (',' (expression '@' ('(' mesh-axis (',' mesh-axis)* ')' |
mesh-axis) | dim-expr))*)? ')'
layout-strides ::= '(' (dim-expr (',' dim-expr)*)? ')'
placed-layout ::= '(' layout-dims ',' layout-strides ')'
| layout-dims
plain-layout ::= '(' (dim-expr (',' dim-expr)*)? ')'
literal ::= None
| Ellipsis
| boolean-literal
| integer-literal
| float-literal
| complex-literal
| string-literal
| bytes-literal
primary ::= identifier
| expression '.' identifier
sequence ::= '(' (expression (',' expression)*)? ')'
| '[' (expression (',' expression)*)? ']'
| '{' (expression (',' expression)*)? '}'
dict ::= '{' (expression ':' expression (',' expression ':' expression)*)? '}'
binary-operation ::= expression ('+' | '-' | '*' | '/' | '//' | '%' | '**') expression
unary-operation ::= ('+' | '-' | 'not') expression
call ::= expression '(' ((expression | keyword-name '=' expression) (','
(expression | keyword-name '=' expression))*)? ')'
slice ::= (expression)? ':' (expression)? (':' expression)?
subscript ::= expression '[' expression ']'
expression ::= literal
| primary
| sequence
| dict
| binary-operation
| unary-operation
| call
| slice
| subscript
composed-layout ::= (placed-layout | plain-layout | expression) '+' dim-expr
| ((placed-layout | plain-layout | expression) '+' dim-expr |
placed-layout | plain-layout | expression) '|' expression
placement-operand ::= '(' layout-dims ',' layout-strides ')'
| layout-dims
value-states ::= '{' 'B' '(' ')' '}'
| '{' mesh-axis '@' 'P' '(' string-literal ')' (',' mesh-axis '@' 'P' '('
string-literal ')')* '}'
stated-layout ::= (composed-layout | placement-operand | expression) '@' value-states
shape ::= '(' (dim-expr (',' dim-expr)*)? ')'
| identifier
| primary '.' identifier
tensor-shape-layout ::= stated-layout
| placed-layout
| shape
dtype ::= string-literal
| primary
layout ::= None
| primary
| call
| stated-layout
| placed-layout
| plain-layout
| composed-layout
storage ::= string-literal
| primary
tensor-optional-slot ::= layout
| storage
tensor ::= tensor-head '[' '(' (tensor-shape-layout ',' dtype | shape ',' dtype ','
tensor-optional-slot | tensor-shape-layout ',' dtype ','
tensor-optional-slot | shape ',' dtype ',' tensor-optional-slot ','
tensor-optional-slot) ')' ']'
tuple-type ::= 'tuple' '[' '(' type-annotation (',' type-annotation)* ')' ']'
| 'tuple' '[' type-annotation ']'
scalar-type ::= primary
| call
type-annotation ::= tensor
| tuple-type
| scalar-type
signature ::= (name ':' type-annotation (',' name ':' type-annotation)*)?
return-type ::= type-annotation
if ::= if cond-node block (block)?
while ::= while cond-node block
loop-iterator ::= 'tf' '.' 'tile' '(' (expression ',' expression | expression ',' expression
',' expression) ')'
| 'range' '(' (expression | expression ',' expression | expression ','
expression ',' expression) ')'
loop-carry-statement ::= expression '=' expression
| 'for' name 'in' expression ':' loop-carry
| statement
loop-carry ::= (loop-carry-statement (newline loop-carry-statement)*)?
loop-header ::= 'for' identifier 'in' loop-iterator ':' loop-carry
loop-body ::= (statement (newline statement)*)?
for ::= 'for' name 'in' expression ':' (block | loop-body)
mesh-context ::= ('Mesh' | primary '.' 'Mesh') '(' (expression | ('layout' | 'names') '='
expression) (',' (expression | ('layout' | 'names') '=' expression))*
')'
| expression
with ::= 'with' mesh-context ('as' identifier)? ':' block
slice-endpoint-binary ::= index-endpoint dim-op index-endpoint
mesh-coordinate ::= identifier '.' string-literal
index-endpoint ::= literal
| primary
| slice-endpoint-binary
| mesh-coordinate
| runtime-expression
| expression
ceildiv-expression ::= ceildiv '(' index-endpoint ',' index-endpoint ')'
variadic-inputs ::= '[' (runtime-expression (',' runtime-expression)*)? ']'
| '(' (runtime-expression (',' runtime-expression)*)? ')'
| listcomp
| generatorexp
| expression
op-value ::= primary '(' ((expression | keyword-name '=' expression) (',' (expression |
keyword-name '=' expression))*)? ')'
op-call ::= (primary | expression '[' expression ']') '(' ((expression | keyword-name
'=' expression) (',' (expression | keyword-name '=' expression))*)? ')'
launch ::= callee '(' ')'
index-slice ::= (index-endpoint)? ':' (index-endpoint)? (':' index-endpoint)?
subscript-index ::= '(' ((index-slice | index-endpoint) (',' (index-slice |
index-endpoint))*)? ')'
| index-slice
| index-endpoint
subscript-expression ::= runtime-expression '[' subscript-index ']'
matmul-expression ::= runtime-expression '@' runtime-expression
binary-expression ::= runtime-expression ('+' | '-' | '*' | '/' | '//' | '%') runtime-expression
| runtime-expression ('==' | '!=' | '<' | '<=' | '>' | '>=')
runtime-expression
| runtime-expression ('and' | 'or') runtime-expression
constant ::= boolean-literal
| integer-literal
| float-literal
| '-' numeric-literal
unary-expression ::= unary-op runtime-expression
name ::= identifier
tuple-expression ::= '(' (runtime-expression (',' runtime-expression)*)? ')'
runtime-expression ::= ceildiv-expression
| op-call
| launch
| subscript-expression
| matmul-expression
| binary-expression
| constant
| unary-expression
| mesh-coordinate
| name
| tuple-expression
| tensor
| primary '.' identifier
tuple-assignment ::= '(' identifier (',' identifier)* ')' '=' runtime-expression
where-annotation ::= 'where' '(' ')'
statement ::= if
| while
| for
| with
| tuple-assignment
| identifier '=' (runtime-expression | expression)
| identifier ':' (where-annotation | type-annotation) ('='
(runtime-expression | expression))?
| 'return' (runtime-expression)?
| runtime-expression
| 'pass'
block ::= (statement (newline statement)*)?
function ::= 'def' name '(' signature ')' ('->' return-type)? ':' block
2.2 Rules¶
| Owner | Situation | Rule | Statement | Source |
|---|---|---|---|---|
| binary_expression, matmul_expression, op_call, unary_expression | expression, slice_endpoint, subscript_index, variadic_input | AuthoredCallScopeRule | An authored HIR call must run inside a mesh if its function opens one. | src/tilefoundry/parser/pattern_nodes.py |
| binary_expression, matmul_expression, op_call, unary_expression | expression, slice_endpoint, subscript_index, variadic_input | CallBindingRule | A call must bind its arguments into a Call tuple. | src/tilefoundry/parser/pattern_nodes.py |
| binary_expression, matmul_expression, op_call, unary_expression | expression, slice_endpoint, subscript_index, variadic_input | CallTypeInferenceRule | A call's result type must be inferred from its binding. | src/tilefoundry/parser/pattern_nodes.py |
| call | call_attribute, expression, layout, mesh_layout, slice_endpoint, static_item, static_key, static_layout, static_mesh, static_operand, static_owner, static_slice, subscript_index, type_constructor | InlineMeshCallRule | Inside an open mesh scope, Mesh constructors open a with header unless used as a ShardLayout mesh value. | src/tilefoundry/parser/pattern_nodes.py |
| call | call_attribute, expression, layout, mesh_layout, slice_endpoint, static_item, static_key, static_layout, static_mesh, static_operand, static_owner, static_slice, subscript_index, type_constructor | StaticCallConstructionRule | A static call has a callable target and constructs a valid Python value. | src/tilefoundry/parser/pattern_nodes.py |
| composed_layout | call_attribute, layout, tensor_optional_slot | ComposedLayoutConstructionRule | A checked composition constructs its inner, offset, and outer layout. | src/tilefoundry/parser/pattern_nodes.py |
| composed_layout | call_attribute, layout, tensor_optional_slot | ComposedLayoutInnerRule | A composed layout's inner is a Swizzle or LayoutBase. | src/tilefoundry/parser/pattern_nodes.py |
| composed_layout | call_attribute, layout, tensor_optional_slot | ComposedLayoutOffsetRule | A composed layout has one offset before its inner layout; compound offsets are parenthesized. | src/tilefoundry/parser/pattern_nodes.py |
| composed_layout | call_attribute, layout, tensor_optional_slot | ComposedLayoutOuterRule | A composed layout's outer is a plain Layout without mesh placement. | src/tilefoundry/parser/pattern_nodes.py |
| dim_expr | call_attribute, dim_expr, layout, layout_extent, layout_offset, mesh_layout, tensor_dim_expr, tensor_optional_slot, tensor_shape | ShapeDimRule | A shape dimension must be an integer, DimVar, or expression. | src/tilefoundry/parser/ast_pattern.py |
| dtype | call_attribute, tensor_dtype | CanonicalDTypeRule | A dtype must resolve to a canonical DType. | src/tilefoundry/parser/ast_pattern.py |
| function | function | FunctionDialectRule | A function kind and constructed value must agree with the active dialect. | src/tilefoundry/parser/pattern_nodes.py |
| function | function | FunctionRegistrationRule | A validated function must be registered exactly once in its owning scope. | src/tilefoundry/parser/pattern_nodes.py |
| function | function | FunctionReturnCompatibilityRule | A HIR body with a return annotation must satisfy that annotation; a dispatch prototype must declare one, and each variant body must satisfy the prototype return contract. | src/tilefoundry/parser/pattern_nodes.py |
| function | function | FunctionRoleValidationRule | A root, variant, or converter must satisfy its role before registration. | src/tilefoundry/parser/pattern_nodes.py |
| function | function | FunctionSignatureRule | A function must construct an ordered parameter tuple. | src/tilefoundry/parser/pattern_nodes.py |
| if, while | loop_statement, statement | TirOnlyStatementRule | A TIR-only statement must appear in a prim_func. | src/tilefoundry/parser/pattern_nodes.py |
| index_slice | subscript_index | TileWindowSliceBoundRule | A tile window cannot be used as a slice bound. | src/tilefoundry/parser/pattern_nodes.py |
| layout, plain_layout | call_attribute, mesh_layout, tensor_optional_slot | LayoutPositionRule | A layout must be legal for its parser position. | src/tilefoundry/parser/ast_pattern.py |
| layout, plain_layout | call_attribute, mesh_layout, tensor_optional_slot | LayoutShapeRule | A layout must have a valid non-boolean shape. | src/tilefoundry/parser/ast_pattern.py |
| loop_header | loop_header | TileIteratorQualificationRule | A tiled loop uses tf.tile rather than a bare tile call. | src/tilefoundry/parser/pattern_nodes.py |
| mesh_axis | call_attribute, layout, mesh_layout, tensor_optional_slot, tensor_shape | BareMeshAxisRule | A bare Mesh placement names a one-axis mesh. | src/tilefoundry/parser/pattern_nodes.py |
| mesh_axis | call_attribute, layout, mesh_layout, tensor_optional_slot, tensor_shape | MeshAxisBindingRule | A placement axis resolves to a lexical Mesh binding. | src/tilefoundry/parser/pattern_nodes.py |
| mesh_axis | call_attribute, layout, mesh_layout, tensor_optional_slot, tensor_shape | MeshAxisNameRule | A named placement axis exists in its lexical Mesh. | src/tilefoundry/parser/pattern_nodes.py |
| mesh_context | mesh_context | SelectionBindingRule | A mesh selection names a lexical Mesh binding or its constant slice. | src/tilefoundry/parser/pattern_nodes.py |
| mesh_context | mesh_context | SelectionConstructionRule | A checked selection constructs a Mesh with its topology and offset, enters its execution scope, and defines its lexical binding. | src/tilefoundry/parser/pattern_nodes.py |
| mesh_context | mesh_context | SelectionCoverRule | A mesh selection layout covers each selection-local index exactly once. | src/tilefoundry/parser/pattern_nodes.py |
| mesh_context | mesh_context | SelectionLayoutFormRule | A mesh selection layout is a shape tuple or a shape/stride tuple pair. | src/tilefoundry/parser/pattern_nodes.py |
| mesh_context | mesh_context | SelectionLevelRule | A mesh selection names exactly one topology level. | src/tilefoundry/parser/pattern_nodes.py |
| mesh_context | mesh_context | SelectionSizeRule | A mesh selection layout has positive static extents, static strides, matching shape/stride and name ranks, and the same size as its selection. | src/tilefoundry/parser/pattern_nodes.py |
| mesh_context | mesh_context | SelectionStrideRule | Each new mesh selection axis maps to one fixed physical stride. | src/tilefoundry/parser/pattern_nodes.py |
| module | module_finalization | ModuleFinalizationRule | A module declaration must contain valid unique members and a resolvable entry. | src/tilefoundry/parser/ast_pattern.py |
| module | module_function | ModuleFunctionRegistrationRule | A validated module function must be recorded in declaration order. | src/tilefoundry/parser/ast_pattern.py |
| module | module_function | ModuleFunctionValidationRule | A module function must satisfy its root, variant, or converter role before mutation. | src/tilefoundry/parser/ast_pattern.py |
| op_call | expression, slice_endpoint, subscript_index, variadic_input | CallVariadicInputFormRule | A variadic call must use one explicit list, tuple, or supported static list comprehension. | src/tilefoundry/parser/pattern_nodes.py |
| op_value | call_attribute | OpValueAttributeRule | Each op-value keyword names an attribute of its resolved op schema. | src/tilefoundry/parser/pattern_nodes.py |
| op_value | call_attribute | OpValueConstructionRule | An op value constructs valid attributes using its resolved schema. | src/tilefoundry/parser/pattern_nodes.py |
| op_value | call_attribute | OpValueKeywordOnlyRule | An op value takes only keyword attributes. | src/tilefoundry/parser/pattern_nodes.py |
| op_value | call_attribute | OpValueUniqueKeywordRule | An op-value keyword is supplied at most once. | src/tilefoundry/parser/pattern_nodes.py |
| placed_layout, stated_layout | call_attribute, mesh_layout, tensor_optional_slot, tensor_shape | LayoutStrideRankRule | A stated stride tuple must have the rank of the layout it addresses. | src/tilefoundry/parser/pattern_nodes.py |
| placed_layout, stated_layout | call_attribute, mesh_layout, tensor_optional_slot, tensor_shape | MeshAxisBoundOnceRule | A placement binds each mesh axis at most once. | src/tilefoundry/parser/pattern_nodes.py |
| placed_layout, stated_layout | call_attribute, mesh_layout, tensor_optional_slot, tensor_shape | PlacementAnswerRule | Placement sugar states both the shape as written and the layout it implies. | src/tilefoundry/parser/pattern_nodes.py |
| placed_layout, stated_layout | call_attribute, mesh_layout, tensor_optional_slot, tensor_shape | PlacementConstructionRule | A placement must construct a valid shard layout. Without splits, partials or stated strides, it uses row-major strides; Broadcast states do not affect this default. A composed operand is retained directly without canonical split decomposition. | src/tilefoundry/parser/pattern_nodes.py |
| placed_layout, stated_layout | call_attribute, mesh_layout, tensor_optional_slot, tensor_shape | PlacementLevelRule | A placement's meshes cannot name the same topology level. | src/tilefoundry/parser/pattern_nodes.py |
| placed_layout, stated_layout | call_attribute, mesh_layout, tensor_optional_slot, tensor_shape | PlacementMeshResolutionRule | A placement's mesh must be a lexical mesh binding. | src/tilefoundry/parser/pattern_nodes.py |
| placed_layout, stated_layout | call_attribute, mesh_layout, tensor_optional_slot, tensor_shape | PlacementScopeRule | A placement requires and covers the current mesh scope, or the function mesh in a signature; each referenced axis must map uniquely to an axis of that scope. Unstated axes are Broadcast. | src/tilefoundry/parser/pattern_nodes.py |
| reference | annotation, call_attribute, expression, slice_endpoint, subscript_index, tensor_dtype, tensor_optional_slot, type_annotation, variadic_input | LexicalMeshReferenceRule | Inside an open mesh scope, an external Mesh or mesh-bearing ShardLayout cannot stand in for a lexical Mesh binding. | src/tilefoundry/parser/ast_pattern.py |
| shape | tensor_shape | ShapeTupleRule | A shape must construct a tuple of dimensions. | src/tilefoundry/parser/ast_pattern.py |
| stated_layout | call_attribute, tensor_optional_slot, tensor_shape | StatedLayoutOperandRule | States attach to placement dimensions, a plain Layout, or a ComposedLayout whose outer is a plain Layout. | src/tilefoundry/parser/pattern_nodes.py |
| storage | call_attribute, tensor_optional_slot | StorageValueRule | Storage must resolve to a StorageKind. | src/tilefoundry/parser/ast_pattern.py |
| tensor | annotation, call_attribute, expression, slice_endpoint, subscript_index, type_annotation, variadic_input | TensorLayoutStorageRule | A tensor type must contain compatible layout and storage values. | src/tilefoundry/parser/ast_pattern.py |
| tensor | annotation, call_attribute, expression, slice_endpoint, subscript_index, type_annotation, variadic_input | TensorPositionRule | A tensor type's storage must be legal for its dialect and position. | src/tilefoundry/parser/ast_pattern.py |
| value_states | call_attribute, tensor_optional_slot, tensor_shape | ValueStatesFormRule | Layout states are either a single B() or one or more axis @ P(nonempty string) entries; empty states, axis @ B(), and mixing B() with other entries are rejected. | src/tilefoundry/parser/pattern_nodes.py |
| where_annotation | annotation | WhereTopologyStatesRule | Where layout states constrain named topologies with topology @ B() or topology @ P(nonempty string); they do not bind lexical mesh axes. | src/tilefoundry/parser/pattern_nodes.py |
A mesh-axis used by placement sugar MUST resolve to a mesh binding in the
current lexical scope. A module or closure name that resolves to a Mesh does
not become a placement binding. Such an external value remains valid as the
context expression of with ... as ... outside an open mesh scope, or as the
value supplied to @func(mesh=...); the resulting lexical binding is the name
placement sugar may use.
3. Implementation Overview¶
| Component | Responsibility |
|---|---|
| Parser API and Context | Receives authored Functions and carries dialect, role, scope, and recursion inputs. |
| Executable Pattern Graph | Composes concrete AST elements into the Function root pattern. |
| Match and Construction | Matches recursively into AstMatch, then constructs owner values on return. |
| Ordered Rules | Validates and normalizes each owner value after construction. |
| Module Build | Lets Python execute the class body, collects declarations, resolves child Modules first, then parses Functions in source order and finalizes the Module. |
| Pattern Visitor | Traverses the same graph to render this section's generated grammar and constraints. |
| Refusal | Carries the reason from the pattern that claimed a node and then refused it, so a report names a cause rather than the absence of a match. |
classDiagram
ParserAPI --> FuncParserContext
ParserAPI --> FunctionPattern
AstPattern <|.. Element
Element o-- AstPattern
Element o-- AstRule
AstPattern --> AstMatch
AstPattern --> MatchFailure
MatchFailure <|.. PatternFailure
MatchFailure <|.. ChoiceFailure
ChoiceFailure o-- MatchFailure : causes
ParseError <.. MatchFailure
PatternVisitor ..> AstPattern
ParserAPI ..> ModuleBuild
flowchart TD
API["parse_function(fn, context)"] --> AST["Extract FunctionDef AST"]
AST --> ROOT["FunctionPattern.match"]
ROOT --> TREE["AstMatch tree"]
TREE --> BACKWARD["construct children, then apply Rules"]
BACKWARD --> FUNCTION["HIR Function / TIR PrimFunction"]
FUNCTION --> MODULE{"Module authoring context?"}
MODULE -->|yes| FINALIZE["defer declaration"]
FINALIZE --> CHILDREN["attach child Modules and bind module scope"]
CHILDREN --> ORDERED["parse roots in source order; then variants/converters"]
ORDERED --> BUILT["construct final Module and verify"]
BUILT --> RETURN
MODULE -->|no| RETURN["return standalone result"]
flowchart TD
TRY["alternative.match(node)"] --> OUT{"outcome"}
OUT -->|"AstMatch"| WIN["choice accepts it; pending refusals are discarded"]
OUT -->|"MatchFailure"| CLAIM["claimed the node and refused: reason recorded"]
OUT -->|"None"| PASS["did not recognize the node: nothing recorded"]
PASS --> NEXT["try the next alternative"]
CLAIM --> NEXT
NEXT --> DONE{"any refusal recorded?"}
DONE -->|no| SILENT["return None: no alternative recognized this node"]
DONE -->|yes| COLLECT["ChoiceFailure over the claimants"]
COLLECT --> UP["travels up unchanged; combinators add no wrapping"]
UP --> RENDER["render(): a sole claimant is the whole report"]
RENDER --> RAISE["ParseError with source location"]
SILENT --> RAISE
A pattern MUST establish that a node is its own before it refuses with a reason. That claim is
what makes the reason trustworthy: it says no remaining alternative can accept this node, so
the refusal is the author's mistake and not another pattern's turn. A callee resolving to an op
schema is such a claim, and a wrong argument count after it is an error. A callee that does not
resolve is not a claim: it may be a bare name or a foreign namespace that another alternative
owns, so the pattern returns None and says nothing. Reasons are never reconstructed from the
AST after the fact; an inspection outside the refusing pattern cannot see which step it failed
at, and becomes a second, divergent copy of that knowledge.
Being last in one choice is not a claim either, because that choice may itself be an
alternative in another. parse_node is the single place where no alternative remains, so it
is the only place that MAY describe a node from its shape rather than from a pattern's
statement, and it does so only when the shape says something worth reading.
None and a MatchFailure differ only for a choice; every other combinator returns either one
unchanged, so a refusal keeps the identity and the wording of the pattern that produced it all
the way to parse_node. Nothing is wrapped, filtered, or re-described on the way up. A
ChoiceFailure records only the alternatives that claimed the node, which is normally one, and
it renders as that sole claimant. Two claimants mean two patterns claim overlapping shapes; the
report states both rather than choosing between them, because the ambiguity is in the grammar
and not in the report.
Pattern combinators serve both runtime matching and Spec traversal. AstMatch separates syntax
matching from object construction, while each Rule reads the recursive context after its owner
value exists. Module class control flow remains Python execution; no Module AST grammar exists.