Abstract Syntax Tree (AST)

Abstract Syntax Tree

The Abstract Syntax Tree (AST) is a representation of an FPy program.

This module contains the AST for FPy programs.

class fpy2.ast.fpyast.AMax(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: max(xs) reduce-form over xs : list[real].

Distinct AST class from Max (the variadic scalar form) so type checking is syntax-directed — isinstance(e, AMax) is the complete discriminator between the two type signatures (list[real] -> real vs real -> ... -> real). Name follows NumPy’s numpy.amax().

class fpy2.ast.fpyast.AMin(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: min(xs) reduce-form — list-form analogue of AMax.

class fpy2.ast.fpyast.Abs(arg: Expr, loc: Location | None)

Bases: UnaryOp

FPy node: absolute value

class fpy2.ast.fpyast.Acos(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: inverse cosine

class fpy2.ast.fpyast.Acosh(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: inverse hyperbolic cosine

class fpy2.ast.fpyast.Add(first: Expr, second: Expr, loc: Location | None)

Bases: BinaryOp

FPy node: addition

class fpy2.ast.fpyast.AllOf(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: all(bs) reduce-form — And to AnyOf’s Or.

class fpy2.ast.fpyast.And(args: Iterable[Expr], loc: Location | None)

Bases: NaryOp

FPy node: logical conjunction

class fpy2.ast.fpyast.AnyOf(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: any(bs) reduce-form over bs : list[bool].

List-fold counterpart of Or, as AMax is to Max. Not named Any: that would shadow typing.Any, which this module and its star-importers use in annotations.

class fpy2.ast.fpyast.AnyTypeAnn(loc: Location | None)

Bases: TypeAnn

FPy AST: any type annotation

is_equiv(other)

Check if this type annotation is equivalent to another type annotation.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Argument(name: Id, type: TypeAnn, loc: Location | None)

Bases: Ast

FPy AST: function argument

class fpy2.ast.fpyast.Asin(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: inverse sine

class fpy2.ast.fpyast.Asinh(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: inverse hyperbolic sine

class fpy2.ast.fpyast.AssertStmt(test: Expr, msg: Expr | None, loc: Location | None)

Bases: Stmt

FPy AST: assert statement

is_equiv(other) bool

Check if this statement is structurally equivalent to another statement.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two statements represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Assign(target: Id | TupleBinding, type: TypeAnn | None, expr: Expr, loc: Location | None)

Bases: Stmt

FPy AST: variable assignment

is_equiv(other)

Check if this statement is structurally equivalent to another statement.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two statements represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Ast(loc: Location | None)

Bases: ABC

FPy AST: abstract base class for all AST nodes.

format() str

Format the AST node as a string.

property loc

Get the location of the AST node.

class fpy2.ast.fpyast.Atan(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: inverse tangent

class fpy2.ast.fpyast.Atan2(func: Var | Attribute | None, first: Expr, second: Expr, loc: Location | None)

Bases: NamedBinaryOp

FPy node: atan(y / x) with correct quadrant

class fpy2.ast.fpyast.Atanh(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: inverse hyperbolic tangent

class fpy2.ast.fpyast.Attribute(value: Expr, attr: str, loc: Location | None)

Bases: Expr

FPy AST: attribute expression x.y

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.BaseFormatter

Bases: object

Abstract base class for AST formatters.

class fpy2.ast.fpyast.BinaryOp(first: Expr, second: Expr, loc: Location | None)

Bases: NaryExpr

FPy AST: binary operation

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.BoolTypeAnn(loc: Location | None)

Bases: TypeAnn

FPy AST: boolean type annotation

is_equiv(other)

Check if this type annotation is equivalent to another type annotation.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.BoolVal(val: bool, loc: Location | None)

Bases: ValueExpr

FPy AST: boolean value

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

fpy2.ast.fpyast.CANONICAL_OP_NAMES: dict[type, str] = {<class 'fpy2.ast.fpyast.AMax'>: 'max', <class 'fpy2.ast.fpyast.AMin'>: 'min', <class 'fpy2.ast.fpyast.Acos'>: 'acos', <class 'fpy2.ast.fpyast.Acosh'>: 'acosh', <class 'fpy2.ast.fpyast.AllOf'>: 'all', <class 'fpy2.ast.fpyast.AnyOf'>: 'any', <class 'fpy2.ast.fpyast.Asin'>: 'asin', <class 'fpy2.ast.fpyast.Asinh'>: 'asinh', <class 'fpy2.ast.fpyast.Atan'>: 'atan', <class 'fpy2.ast.fpyast.Atan2'>: 'atan2', <class 'fpy2.ast.fpyast.Atanh'>: 'atanh', <class 'fpy2.ast.fpyast.Cast'>: 'cast', <class 'fpy2.ast.fpyast.Cbrt'>: 'cbrt', <class 'fpy2.ast.fpyast.Ceil'>: 'ceil', <class 'fpy2.ast.fpyast.Const1_Pi'>: 'const_1_pi', <class 'fpy2.ast.fpyast.Const2_Pi'>: 'const_2_pi', <class 'fpy2.ast.fpyast.Const2_SqrtPi'>: 'const_2_sqrt_pi', <class 'fpy2.ast.fpyast.ConstE'>: 'const_e', <class 'fpy2.ast.fpyast.ConstInf'>: 'inf', <class 'fpy2.ast.fpyast.ConstLn2'>: 'const_ln2', <class 'fpy2.ast.fpyast.ConstLog10E'>: 'const_log10e', <class 'fpy2.ast.fpyast.ConstLog2E'>: 'const_log2e', <class 'fpy2.ast.fpyast.ConstNan'>: 'nan', <class 'fpy2.ast.fpyast.ConstPi'>: 'const_pi', <class 'fpy2.ast.fpyast.ConstPi_2'>: 'const_pi_2', <class 'fpy2.ast.fpyast.ConstPi_4'>: 'const_pi_4', <class 'fpy2.ast.fpyast.ConstSqrt1_2'>: 'const_sqrt1_2', <class 'fpy2.ast.fpyast.ConstSqrt2'>: 'const_sqrt2', <class 'fpy2.ast.fpyast.Copysign'>: 'copysign', <class 'fpy2.ast.fpyast.Cos'>: 'cos', <class 'fpy2.ast.fpyast.Cosh'>: 'cosh', <class 'fpy2.ast.fpyast.Digits'>: 'digits', <class 'fpy2.ast.fpyast.Dim'>: 'dim', <class 'fpy2.ast.fpyast.Empty'>: 'empty', <class 'fpy2.ast.fpyast.Enumerate'>: 'enumerate', <class 'fpy2.ast.fpyast.Erf'>: 'erf', <class 'fpy2.ast.fpyast.Erfc'>: 'erfc', <class 'fpy2.ast.fpyast.Exp'>: 'exp', <class 'fpy2.ast.fpyast.Exp2'>: 'exp2', <class 'fpy2.ast.fpyast.Expm1'>: 'expm1', <class 'fpy2.ast.fpyast.Fdim'>: 'fdim', <class 'fpy2.ast.fpyast.Floor'>: 'floor', <class 'fpy2.ast.fpyast.Fma'>: 'fma', <class 'fpy2.ast.fpyast.Fmod'>: 'fmod', <class 'fpy2.ast.fpyast.Fst'>: 'fst', <class 'fpy2.ast.fpyast.Hexnum'>: 'hexfloat', <class 'fpy2.ast.fpyast.Hypot'>: 'hypot', <class 'fpy2.ast.fpyast.IsFinite'>: 'isfinite', <class 'fpy2.ast.fpyast.IsInf'>: 'isinf', <class 'fpy2.ast.fpyast.IsNan'>: 'isnan', <class 'fpy2.ast.fpyast.IsNormal'>: 'isnormal', <class 'fpy2.ast.fpyast.Len'>: 'len', <class 'fpy2.ast.fpyast.Lgamma'>: 'lgamma', <class 'fpy2.ast.fpyast.Log'>: 'log', <class 'fpy2.ast.fpyast.Log10'>: 'log10', <class 'fpy2.ast.fpyast.Log1p'>: 'log1p', <class 'fpy2.ast.fpyast.Log2'>: 'log2', <class 'fpy2.ast.fpyast.Logb'>: 'logb', <class 'fpy2.ast.fpyast.Max'>: 'max', <class 'fpy2.ast.fpyast.Min'>: 'min', <class 'fpy2.ast.fpyast.NearbyInt'>: 'nearbyint', <class 'fpy2.ast.fpyast.Pow'>: 'pow', <class 'fpy2.ast.fpyast.Range1'>: 'range', <class 'fpy2.ast.fpyast.Range2'>: 'range', <class 'fpy2.ast.fpyast.Range3'>: 'range', <class 'fpy2.ast.fpyast.Rational'>: 'rational', <class 'fpy2.ast.fpyast.Remainder'>: 'remainder', <class 'fpy2.ast.fpyast.Round'>: 'round', <class 'fpy2.ast.fpyast.RoundAt'>: 'round_at', <class 'fpy2.ast.fpyast.RoundInt'>: 'roundint', <class 'fpy2.ast.fpyast.Signbit'>: 'signbit', <class 'fpy2.ast.fpyast.Sin'>: 'sin', <class 'fpy2.ast.fpyast.Sinh'>: 'sinh', <class 'fpy2.ast.fpyast.Size'>: 'size', <class 'fpy2.ast.fpyast.Snd'>: 'snd', <class 'fpy2.ast.fpyast.Sqrt'>: 'sqrt', <class 'fpy2.ast.fpyast.Sum'>: 'sum', <class 'fpy2.ast.fpyast.Tan'>: 'tan', <class 'fpy2.ast.fpyast.Tanh'>: 'tanh', <class 'fpy2.ast.fpyast.Tgamma'>: 'tgamma', <class 'fpy2.ast.fpyast.Trunc'>: 'trunc', <class 'fpy2.ast.fpyast.Zip'>: 'zip'}

Canonical FPy surface name for each named-operator / literal node type.

Used by the pretty-printer to render a node whose func symbol is None (i.e. a node synthesized by a rewrite pass rather than parsed from user source). The name is the operator’s public attribute name in the fpy2 package (or, for Len/Range1 etc., the Python builtin the parser resolves against). See Formatter._op_name().

class fpy2.ast.fpyast.Call(func: Var | Attribute, fn: object, args: Iterable[Expr], kwargs: Iterable[tuple[str, Expr]], loc: Location | None)

Bases: NaryExpr

FPy AST: function call

is_equiv(other)

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Cast(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: round to the current context, asserting the result is exact

class fpy2.ast.fpyast.Cbrt(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: cube-root

class fpy2.ast.fpyast.Ceil(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: ceiling

class fpy2.ast.fpyast.Compare(ops: Iterable[CompareOp], args: Iterable[Expr], loc: Location | None)

Bases: Expr

FPy AST: comparison chain

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.CompareOp(*values)

Bases: Enum

Comparison operators as an enumeration

invert()

Assuming a op b, returns op such that b op a.

symbol()

Get the symbol for the operator.

class fpy2.ast.fpyast.Const1_Pi(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: 1/π (constant)

class fpy2.ast.fpyast.Const2_Pi(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: 2/π (constant)

class fpy2.ast.fpyast.Const2_SqrtPi(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: 2/√π (constant)

class fpy2.ast.fpyast.ConstE(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: e (constant)

class fpy2.ast.fpyast.ConstInf(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: infinity (constant)

class fpy2.ast.fpyast.ConstLn2(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: ln(2) (constant)

class fpy2.ast.fpyast.ConstLog10E(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: log₁₀(e) (constant)

class fpy2.ast.fpyast.ConstLog2E(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: log₂(e) (constant)

class fpy2.ast.fpyast.ConstNan(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: NaN (constant)

class fpy2.ast.fpyast.ConstPi(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: π (constant)

class fpy2.ast.fpyast.ConstPi_2(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: π/2 (constant)

class fpy2.ast.fpyast.ConstPi_4(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: π/4 (constant)

class fpy2.ast.fpyast.ConstSqrt1_2(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: √(1/2) (constant)

class fpy2.ast.fpyast.ConstSqrt2(func: Var | Attribute | None, loc: Location | None)

Bases: NullaryOp

FPy node: √2 (constant)

class fpy2.ast.fpyast.ContextStmt(target: Id, ctx: Expr, body: StmtBlock, loc: Location | None)

Bases: Stmt

FPy AST: with statement

is_equiv(other) bool

Check if this statement is structurally equivalent to another statement.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two statements represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.ContextTypeAnn(loc: Location | None)

Bases: TypeAnn

FPy AST: context type annotation

is_equiv(other)

Check if this type annotation is equivalent to another type annotation.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Copysign(func: Var | Attribute | None, first: Expr, second: Expr, loc: Location | None)

Bases: NamedBinaryOp

FPy node: copysign

class fpy2.ast.fpyast.Cos(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: cosine

class fpy2.ast.fpyast.Cosh(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: hyperbolic cosine

class fpy2.ast.fpyast.Decnum(val: str, loc: Location | None)

Bases: RationalVal

FPy AST: decimal number

as_rational() Fraction

Returns the represented rational value as a Fraction in simplest form.

as_real() Fraction | Float

Returns the represented value as an exact real number.

Unlike as_rational, this preserves the sign of a zero: a negative-zero literal is returned as an exact Float (a Fraction cannot represent one). Prefer this when the sign of a zero matters, e.g. when lowering a literal to a runtime value.

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Digits(func: Var | Attribute | None, m: int, e: int, b: int, loc: Location | None)

Bases: RationalVal

FPy AST: scientific notation

as_rational() Fraction

Returns the represented rational value as a Fraction in simplest form.

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Dim(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: dimension operator

class fpy2.ast.fpyast.Div(first: Expr, second: Expr, loc: Location | None)

Bases: BinaryOp

FPy node: subtraction

class fpy2.ast.fpyast.EffectStmt(expr: Expr, loc: Location | None)

Bases: Stmt

FPy AST: an expression without a result

is_equiv(other) bool

Check if this statement is structurally equivalent to another statement.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two statements represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Empty(func: Var | Attribute | None, args: Iterable[Expr], loc: Location | None)

Bases: NamedNaryOp

FPy node: empty operator

class fpy2.ast.fpyast.Enumerate(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: enumerate operator

class fpy2.ast.fpyast.Erf(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: error function

class fpy2.ast.fpyast.Erfc(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: complementary error function

class fpy2.ast.fpyast.Exp(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: exponential (base e)

class fpy2.ast.fpyast.Exp2(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: exponential (base 2)

class fpy2.ast.fpyast.Expm1(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: exp(x) - 1

class fpy2.ast.fpyast.Expr(loc: Location | None)

Bases: Ast

FPy AST: expression

abstractmethod is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Fdim(func: Var | Attribute | None, first: Expr, second: Expr, loc: Location | None)

Bases: NamedBinaryOp

FPy node: max(x - y, 0)

class fpy2.ast.fpyast.Floor(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: floor

class fpy2.ast.fpyast.Fma(func: Var | Attribute | None, first: Expr, second: Expr, third: Expr, loc: Location | None)

Bases: NamedTernaryOp

FPy node: fused-multiply add

class fpy2.ast.fpyast.Fmod(func: Var | Attribute | None, first: Expr, second: Expr, loc: Location | None)

Bases: NamedBinaryOp

FPy node: fmod(x, y)

Represents the C standard library’s fmod function, defined as:

fmod(x, y) = x - trunc(x / y) * y

The result has the same sign as the dividend x.

class fpy2.ast.fpyast.ForStmt(target: Id | TupleBinding, iterable: Expr, body: StmtBlock, loc: Location | None)

Bases: Stmt

FPy AST: for statement

is_equiv(other) bool

Check if this statement is structurally equivalent to another statement.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two statements represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.ForeignVal(val: Any, loc: Location | None)

Bases: ValueExpr

FPy AST: native Python value

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Fst(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: tuple head accessor

class fpy2.ast.fpyast.FuncDef(name: str, args: Iterable[Argument], body: StmtBlock, meta: FuncMeta, *, loc: Location | None = None)

Bases: Ast

FPy AST: function definition

class fpy2.ast.fpyast.FuncMeta(free_vars: set[NamedId], ctx: Context | FPCoreContext | None, spec: Any, props: dict[str, Any], env: ForeignEnv)

Bases: object

Function definition metadata

ctx: Context | FPCoreContext | None

overriding global context (if any)

env: ForeignEnv

foreign environment associated with the function

free_vars: set[NamedId]

free variables used in the function body

props: dict[str, Any]

additional properties (if any)

spec: Any

function specification (if any)

fpy2.ast.fpyast.FuncSymbol: TypeAlias = fpy2.ast.fpyast.Var | fpy2.ast.fpyast.Attribute

FPy function symbols have the recursive form

symbol ::= Var ( symbol )
Attribute ( symbol, _ )

This type alias only shallowly checks the type

class fpy2.ast.fpyast.Hexnum(func: Var | Attribute | None, val: str, loc: Location | None)

Bases: RationalVal

FPy AST: hexadecimal number

as_rational() Fraction

Returns the represented rational value as a Fraction in simplest form.

as_real() Fraction | Float

Returns the represented value as an exact real number.

Unlike as_rational, this preserves the sign of a zero: a negative-zero literal is returned as an exact Float (a Fraction cannot represent one). Prefer this when the sign of a zero matters, e.g. when lowering a literal to a runtime value.

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Hypot(func: Var | Attribute | None, first: Expr, second: Expr, loc: Location | None)

Bases: NamedBinaryOp

FPy node: sqrt(x ** 2 + y ** 2)

class fpy2.ast.fpyast.Id

Bases: ABC

Abstract base class for identifiers.

is_equiv(other) bool

Check if this identifier is equivalent to another identifier.

This method is for compability with AST classes.

abstractmethod names() set[NamedId]

Return a set of all named identifiers contained in this identifier.

This method is for compability with AST classes.

class fpy2.ast.fpyast.If1Stmt(cond: Expr, body: StmtBlock, loc: Location | None)

Bases: Stmt

FPy AST: if statement with one branch

is_equiv(other) bool

Check if this statement is structurally equivalent to another statement.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two statements represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.IfExpr(cond: Expr, ift: Expr, iff: Expr, loc: Location | None)

Bases: Expr

FPy AST: if expression

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.IfStmt(cond: Expr, ift: StmtBlock, iff: StmtBlock, loc: Location | None)

Bases: Stmt

FPy AST: if statement (with two branches)

is_equiv(other) bool

Check if this statement is structurally equivalent to another statement.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two statements represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.IndexedAssign(var: NamedId, indices: Iterable[Expr], expr: Expr, loc: Location | None)

Bases: Stmt

FPy AST: assignment to tuple indexing

is_equiv(other) bool

Check if this statement is structurally equivalent to another statement.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two statements represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Integer(val: int, loc: Location | None)

Bases: RationalVal

FPy AST: integer

as_rational() Fraction

Returns the represented rational value as a Fraction in simplest form.

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.IsFinite(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: is the value finite?

class fpy2.ast.fpyast.IsInf(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: is the value infinite?

class fpy2.ast.fpyast.IsNan(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: is the value NaN?

class fpy2.ast.fpyast.IsNormal(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: is the value normal?

class fpy2.ast.fpyast.Len(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: length operator

class fpy2.ast.fpyast.Lgamma(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: logarithm of the absolute value of the gamma function

class fpy2.ast.fpyast.ListComp(targets: Collection[Id | TupleBinding], iterables: Collection[Expr], elt: Expr, loc: Location | None)

Bases: Expr

FPy AST: list comprehension expression

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.ListExpr(elts: Iterable[Expr], loc: Location | None)

Bases: Expr

FPy AST: list expression

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.ListRef(value: Expr, index: Expr, loc: Location | None)

Bases: Expr

FPy AST: list indexing expression

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.ListSlice(value: Expr, start: Expr | None, stop: Expr | None, loc: Location | None)

Bases: Expr

FPy AST: list slicing expression

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.ListTypeAnn(elt: TypeAnn, length: int | NamedId | None, loc: Location | None)

Bases: TypeAnn

FPy AST: native list type annotation

is_equiv(other) bool

Check if this type annotation is equivalent to another type annotation.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

length: int | NamedId | None

a known int, a symbolic NamedId, or None (unknown)

Type:

optional list size

class fpy2.ast.fpyast.Location(source: str, start_line: int, start_column: int, end_line: int, end_column: int)

Bases: object

Parse location: line and column number.

class fpy2.ast.fpyast.Log(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: logarithm (base e)

class fpy2.ast.fpyast.Log10(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: logarithm (base 10)

class fpy2.ast.fpyast.Log1p(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: log(x + 1)

class fpy2.ast.fpyast.Log2(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: logarithm (base 2)

class fpy2.ast.fpyast.Logb(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: extracts the (normalized) exponent of a number.

class fpy2.ast.fpyast.Max(func: Var | Attribute | None, args: Iterable[Expr], loc: Location | None)

Bases: NamedNaryOp

FPy node: max(x, y, …)

class fpy2.ast.fpyast.Min(func: Var | Attribute | None, args: Iterable[Expr], loc: Location | None)

Bases: NamedNaryOp

FPy node: min(x, y, …)

class fpy2.ast.fpyast.Mod(first: Expr, second: Expr, loc: Location | None)

Bases: BinaryOp

FPy node: a % b

Represents Python’s modulus operator, defined as:

a = (a // b) * b + (a % b)

The result has the same sign as the divisor b.

class fpy2.ast.fpyast.Mul(first: Expr, second: Expr, loc: Location | None)

Bases: BinaryOp

FPy node: subtraction

class fpy2.ast.fpyast.NamedBinaryOp(func: Var | Attribute | None, first: Expr, second: Expr, loc: Location | None)

Bases: BinaryOp

FPy AST: binary operation with a named function

class fpy2.ast.fpyast.NamedId(base: str, count: int | None = None)

Bases: Id

Named identifier.

A named identifier consists of a base name and an optional count to indicate its version.

names() set[NamedId]

Return a set of all named identifiers contained in this identifier.

This method is for compability with AST classes.

class fpy2.ast.fpyast.NamedNaryOp(func: Var | Attribute | None, args: Iterable[Expr], loc: Location | None)

Bases: NaryOp

FPy AST: n-ary operation with a named function

class fpy2.ast.fpyast.NamedTernaryOp(func: Var | Attribute | None, first: Expr, second: Expr, third: Expr, loc: Location | None)

Bases: TernaryOp

FPy AST: ternary operation with a named function

class fpy2.ast.fpyast.NamedUnaryOp(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: UnaryOp

FPy AST: unary operation with a named function

class fpy2.ast.fpyast.NaryExpr(loc: Location | None)

Bases: Expr

FPy AST: expression with N arguments

class fpy2.ast.fpyast.NaryOp(args: Iterable[Expr], loc: Location | None)

Bases: NaryExpr

FPy AST: n-ary operation

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.NearbyInt(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: nearest integer

class fpy2.ast.fpyast.Neg(arg: Expr, loc: Location | None)

Bases: UnaryOp

FPy node: negation

class fpy2.ast.fpyast.Not(arg: Expr, loc: Location | None)

Bases: UnaryOp

FPy node: logical negation

class fpy2.ast.fpyast.NullaryOp(func: Var | Attribute | None, loc: Location | None)

Bases: NaryExpr

FPy AST: (named) nullary operation

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Or(args: Iterable[Expr], loc: Location | None)

Bases: NaryOp

FPy node: logical disjunction

class fpy2.ast.fpyast.PassStmt(loc: Location | None)

Bases: Stmt

FPy AST: pass (skip) statement

is_equiv(other) bool

Check if this statement is structurally equivalent to another statement.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two statements represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Pow(func: Var | Attribute | None, first: Expr, second: Expr, loc: Location | None)

Bases: NamedBinaryOp

FPy node: x ** y

class fpy2.ast.fpyast.Range1(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: range(stop)

class fpy2.ast.fpyast.Range2(func: Var | Attribute | None, first: Expr, second: Expr, loc: Location | None)

Bases: NamedBinaryOp

FPy node: range(start, stop)

class fpy2.ast.fpyast.Range3(func: Var | Attribute | None, first: Expr, second: Expr, third: Expr, loc: Location | None)

Bases: NamedTernaryOp

FPy node: range(start, stop, step)

class fpy2.ast.fpyast.Rational(func: Var | Attribute | None, p: int, q: int, loc: Location | None)

Bases: RationalVal

FPy AST: rational number

as_rational() Fraction

Returns the represented rational value as a Fraction in simplest form.

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.RationalVal(loc: Location | None)

Bases: RealVal

FPy AST: abstract rational value

abstractmethod as_rational() Fraction

Returns the represented rational value as a Fraction in simplest form.

as_real() Fraction | Float

Returns the represented value as an exact real number.

Unlike as_rational, this preserves the sign of a zero: a negative-zero literal is returned as an exact Float (a Fraction cannot represent one). Prefer this when the sign of a zero matters, e.g. when lowering a literal to a runtime value.

is_integer() bool

Returns true if the represented value is an integer.

class fpy2.ast.fpyast.RealTypeAnn(ctx: Context | None, loc: Location | None)

Bases: TypeAnn

FPy AST: real type annotation

is_equiv(other)

Check if this type annotation is equivalent to another type annotation.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.RealVal(loc: Location | None)

Bases: ValueExpr

FPy AST: real value

class fpy2.ast.fpyast.Remainder(func: Var | Attribute | None, first: Expr, second: Expr, loc: Location | None)

Bases: NamedBinaryOp

FPy node: remainder(x, y)

Represents the IEEE 754 remainder function, defined as:

remainder(x, y) = x - round(x / y) * y

The result is the value closest to zero.

class fpy2.ast.fpyast.ReturnStmt(expr: Expr, loc: Location | None)

Bases: Stmt

FPy AST: return statement

is_equiv(other) bool

Check if this statement is structurally equivalent to another statement.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two statements represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Round(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: inter-format rounding

class fpy2.ast.fpyast.RoundAt(func: Var | Attribute | None, first: Expr, second: Expr, loc: Location | None)

Bases: NamedBinaryOp

FPy node: inter-format rounding with absolute position

class fpy2.ast.fpyast.RoundInt(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: round

class fpy2.ast.fpyast.Signbit(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: is the signbit 1?

class fpy2.ast.fpyast.Sin(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: sine

class fpy2.ast.fpyast.Sinh(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: hyperbolic sine

class fpy2.ast.fpyast.Size(func: Var | Attribute | None, first: Expr, second: Expr, loc: Location | None)

Bases: NamedBinaryOp

FPy node: size operator

class fpy2.ast.fpyast.Snd(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: tuple tail accessor

class fpy2.ast.fpyast.SourceId(base: str, loc: Location, count: int | None = None)

Bases: NamedId

Named identifier from the original source.

A source identifier should only be generated by the parser.

loc: Location

source location

class fpy2.ast.fpyast.Sqrt(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: square-root

class fpy2.ast.fpyast.Stmt(loc: Location | None)

Bases: Ast

FPy AST: statement

abstractmethod is_equiv(other) bool

Check if this statement is structurally equivalent to another statement.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two statements represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.StmtBlock(stmts: list[Stmt])

Bases: Ast

FPy AST: list of statements

class fpy2.ast.fpyast.Sub(first: Expr, second: Expr, loc: Location | None)

Bases: BinaryOp

FPy node: subtraction

class fpy2.ast.fpyast.Sum(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: summation

class fpy2.ast.fpyast.Tan(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: tangent

class fpy2.ast.fpyast.Tanh(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: hyperbolic tangent

class fpy2.ast.fpyast.TernaryOp(first: Expr, second: Expr, third: Expr, loc: Location | None)

Bases: NaryExpr

FPy AST: ternary operation

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Tgamma(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: gamma function

class fpy2.ast.fpyast.Trunc(func: Var | Attribute | None, arg: Expr, loc: Location | None)

Bases: NamedUnaryOp

FPy node: truncation

class fpy2.ast.fpyast.TupleBinding(elts: Iterable[Id | Self], loc: Location | None)

Bases: Ast

FPy AST: tuple binding

class fpy2.ast.fpyast.TupleExpr(elts: Iterable[Expr], loc: Location | None)

Bases: Expr

FPy AST: tuple expression

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.TupleTypeAnn(elts: list[TypeAnn], loc: Location | None)

Bases: TypeAnn

FPy AST: native tuple type annotation

is_equiv(other) bool

Check if this type annotation is equivalent to another type annotation.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.TypeAnn(loc: Location | None)

Bases: Ast

FPy AST: typing annotation

abstractmethod is_equiv(other) bool

Check if this type annotation is equivalent to another type annotation.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.UnaryOp(arg: Expr, loc: Location | None)

Bases: NaryExpr

FPy AST: unary operation

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.UnderscoreId

Bases: Id

Placeholder identifier.

When used in an assignment, the value is not bound. This identifier is illegal as a variable.

names() set[NamedId]

Return a set of all named identifiers contained in this identifier.

This method is for compability with AST classes.

class fpy2.ast.fpyast.ValueExpr(loc: Location | None)

Bases: Expr

FPy Ast: terminal expression

class fpy2.ast.fpyast.Var(name: NamedId, loc: Location | None)

Bases: Expr

FPy AST: variable

is_equiv(other) bool

Check if this expression is structuarlly equivalent to another expression.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two expressions represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.WhileStmt(cond: Expr, body: StmtBlock, loc: Location | None)

Bases: Stmt

FPy AST: while statement

is_equiv(other) bool

Check if this statement is structurally equivalent to another statement.

This is essentially a recursive equality check. The dunder method __eq__ is used to check if two statements represent exactly the same tree, e.g., id(self) == id(other).

class fpy2.ast.fpyast.Zip(func: Var | Attribute | None, args: Iterable[Expr], loc: Location | None)

Bases: NamedNaryOp

FPy node: zip operator

fpy2.ast.fpyast.get_default_formatter() BaseFormatter

Get the default formatter for FPy AST.

fpy2.ast.fpyast.set_default_formatter(formatter: BaseFormatter)

Set the default formatter for FPy AST.

Visitors

Visitor base class for FPy AST nodes.

class fpy2.ast.DefaultVisitor

Bases: Visitor

Default visitor: visits all nodes without doing anything.

class fpy2.ast.DefaultTransformVisitor

Bases: Visitor

Default visitor: visits all nodes without doing anything.

Formatter

The AST can be formatted to a string representation using the fpy2.ast.Formatter class.

Abstract base class for AST formatters.

class fpy2.ast.Formatter

Bases: BaseFormatter

“Pretty printer for FPy AST

format(ast: Ast) str

Pretty print the given AST

Analyses

class fpy2.analysis.ArraySizeInfer

Bases: object

Array size inference.

static analyze(func: FuncDef, *, partial_eval: PartialEvalInfo | None = None, type_info: TypeAnalysis | None = None) ArraySizeAnalysis

Analyze a function definition to infer array sizes.

Parameters:
  • func – Function definition to analyze.

  • partial_eval – Optional pre-computed partial-evaluation info.

  • type_info – Optional pre-computed type analysis.

class fpy2.analysis.CallGraph

Bases: object

Call graph analysis.

Builds the DAG of functions transitively called from a root FuncDef. Raises CallGraphError on recursion.

static analyze(func: FuncDef) CallGraphAnalysis

Build the call graph rooted at func.

class fpy2.analysis.ContextUse

Bases: object

Context-use analysis.

This analysis computes, for each rounding context scope in a function, the set of context-sensitive expressions evaluated under it.

Context scopes are introduced by:

  • with statements (ContextStmt), and

  • the function-level context annotation in FuncDef.

The analysis attempts partial evaluation to resolve context expressions to concrete Context values. When partial evaluation does not fully reduce a context expression, or when a function is entered without an overriding context, a fresh symbolic context variable (NamedId) is generated.

static analyze(func: FuncDef, *, def_use: DefineUseAnalysis | None = None, partial_eval: PartialEvalInfo | None = None) ContextUseAnalysis

Runs context-use analysis on a function.

Parameters:
  • func – The function to analyze.

  • def_use – Optional pre-computed definition-use analysis. If None, it is computed automatically. Ignored when partial_eval is supplied (the partial-eval info already carries its own).

  • partial_eval – Optional pre-computed partial-evaluation info. If None, it is computed from def_use. Supply it to avoid recomputing partial evaluation when the caller already has it.

class fpy2.analysis.DefineUse

Bases: object

Definition-use analysis.

This analysis computes: - the set of definitions available at the entry/exit of each block; - the set of definitions introduced at each statement; - set of all definitions for each variable; - set of all uses for each definition. - the definition referenced by each use.

Each definition has a reference its immediate previous definition(s) which represents a (possibly cyclic) graph of definitions.

class fpy2.analysis.FormatInfer

Bases: object

Format inference for FPy functions.

This analysis bounds the number format for each expression and variable definition in an FPy program. It uses two pre-analyses (TypeInfer and ContextUse) and then performs a single forward pass over the AST.

Format lattice:

FormatBound ::= None
              | SetFormat(values)     (known finite set of reals)
              | Format               (REAL_FORMAT is the scalar top)
              | TupleFormat(elts)
              | ListFormat(elt)

Join rule:

join(None, None)                 = None
join(SetFormat(a), SetFormat(b)) = SetFormat(a ∪ b)
join(SetFormat(s), fmt)          = fmt if every value in s fits in fmt
                                 = REAL_FORMAT otherwise
join(f, f)                       = f
join(f1, f2)                     = (AbstractFormat.from_format(f1)
                                    | AbstractFormat.from_format(f2)).format()
                                                  (when both abstractable)
                                 = REAL_FORMAT     (otherwise)
join(Tuple(a..), Tuple(b..))     = Tuple(join(ai, bi)..)
join(List(a), List(b))           = List(join(a, b))

This rule is applied at all control-flow merge points (phi nodes), including branch merges (if/if1) and loop back-edges (while/for).

Loops are handled in one of two modes:

  • Bounded iteration: when a for loop’s iterable has a statically-known length (per ArraySizeAnalysis), the analysis drives the phi update for exactly that many body executions. This mirrors runtime semantics and avoids any widening fall-back — important for the exact-arithmetic lattice, which has infinite ascending chains.

  • Fixpoint + widening: while loops and for loops over symbolic-length iterables iterate body + join until phi bounds stop changing. The AbstractFormat-mediated scalar join introduces infinite ascending chains when exact arithmetic (+/-/* under REAL) is applied to a phi’d value, so the fixpoint runs at most loop_iter_limit iterations before switching joins to widen-mode (distinct scalar Formats fall back to REAL_FORMAT) to force convergence.

Usage:

from fpy2.analysis import FormatInfer

info = FormatInfer.analyze(func)
for d, fmt in info.by_def.items():
    print(d.name, '->', fmt)
DEFAULT_LOOP_ITER_LIMIT = 10

Default number of body+join iterations a loop fixpoint runs before switching to widen-mode (joins of distinct scalar Formats fall back to REAL_FORMAT to force convergence). Tunable via the loop_iter_limit parameter of analyze().

DEFAULT_RANGE_SET_THRESHOLD = 16

Default maximum element count for which a concrete range(...) pins its exact value set as a SetFormat. Larger ranges use the bounded integer format A(inf, 0, b) instead. Tunable via the range_set_threshold parameter of analyze().

DEFAULT_SET_FORMAT_THRESHOLD = 256

Default maximum size of a SetFormat produced by set arithmetic or joins. Beyond this, the set collapses to the covering bounded AbstractFormat — a sound superset that bounds the otherwise combinatorial growth of repeated set operations (e.g. a multiplicative reduction). Tunable via the set_format_threshold parameter of analyze().

static analyze(func: FuncDef, *, def_use: DefineUseAnalysis | None = None, type_info: TypeAnalysis | None = None, ctx_use: ContextUseAnalysis | None = None, array_size: ArraySizeAnalysis | None = None, pre_cache: PreAnalysisCache | None = None, fn_fmt: FunctionFormat | None = None, loop_iter_limit: int = 10, range_set_threshold: int = 16, set_format_threshold: int = 256) FormatAnalysis

Performs format analysis on an FPy function.

Walks into every Call whose fn is a known Function and recurses to produce a graph of FormatAnalysis mirroring the call graph (FormatAnalysis.by_call). Structural pre-analyses (def-use, types, context scopes, array sizes) are shared across all sub-analyses via a PreAnalysisCache — each FuncDef is structurally analyzed at most once for the lifetime of a top-level call to analyze(). FormatInfer itself re-runs per instantiation so each callee sees the caller’s active rounding context.

The call graph must be acyclic; recursion (direct or mutual) is not handled by the per-call-site sub-analysis. A CallGraph acyclicity check runs at this entry and raises CallGraphError if a cycle is reachable.

Parameters:
  • func – The FuncDef AST node to analyse.

  • def_use – Optional pre-computed definition-use analysis.

  • type_info – Optional pre-computed type analysis.

  • ctx_use – Optional pre-computed context-use analysis.

  • array_size – Optional pre-computed array-size analysis. Used to recognise for loops whose iterable has a statically-known length, so the phi update can be driven exactly that many times instead of iterating to a fixpoint. This is strictly more precise — important for the exact-arithmetic lattice (+/-/* under REAL) which has infinite ascending chains.

  • pre_cache – Optional PreAnalysisCache shared with sub-analyses. Useful when a caller wants to amortize structural analysis across multiple independent analyze() invocations on a shared corpus. When None, a fresh cache is allocated for this call (and its recursive descents).

  • fn_fmt – Format-level signature to instantiate the function at. Supplies the incoming rounding context (used to pin the function’s outermost scope when symbolic) and a per-parameter initial format (used to bypass the declared parameter types — typically derived from the caller’s call-site argument formats). The ret_fmt slot is treated as expected and overwritten in the result. None falls back to the legacy behaviour: symbolic outer scopes degrade to REAL_FORMAT and parameter formats come from the declared types.

  • loop_iter_limit – Number of loop body+join iterations to run before forcing joins of distinct scalar Formats to widen to REAL_FORMAT. Only applies to loops without a known iteration count (while loops, and for loops over symbolic-size iterables).

  • range_set_threshold – Maximum element count for which a concrete range(...) pins its exact value set as a SetFormat. Larger ranges fall back to the bounded integer format A(inf, 0, b).

  • set_format_threshold – Maximum size of a SetFormat produced by set arithmetic or joins before it collapses to the covering bounded AbstractFormat. Bounds the combinatorial growth of repeated set operations.

Returns:

A FormatAnalysis whose by_def, by_expr, and by_call describe the formats inferred at every definition site, expression, and reachable sub-call.

Raises:
  • TypeError – If func is not a FuncDef.

  • CallGraphError – If the call graph reachable from func contains a cycle (FPy forbids recursion).

class fpy2.analysis.LiveVars

Bases: object

Live variable analysis for the FPy AST.

static analyze(ast: FuncDef | Expr)

Analyze the live variables in a function.

class fpy2.analysis.PartialEval

Bases: object

Partial evaluation — records the statically-known Value of each expression under the active rounding context.

Coverage: literals, free-variable lookups, all operator kinds (nullary through n-ary, plus Compare), context-constructor Call s, Attribute access, tuple / list literals, list indexing / slicing, and SSA definitions via Assign. Operators that raise inside the interpreter (e.g. inexact Cast, division by zero) are silently dropped: PE is best-effort and never crashes the analysis for a single edge case.

static apply(func: FuncDef, *, def_use: DefineUseAnalysis | None = None)

Run partial evaluation on func and return the PartialEvalInfo. Pass an existing def_use= to avoid recomputing the def-use analysis.

class fpy2.analysis.Purity

Bases: object

Pure function analysis.

A function is considered pure if it has no side effects.

static analyze(func: FuncDef, def_use: DefineUseAnalysis | None = None) bool

Analyze the given function and return True if it is pure, False otherwise.

static analyze_expr(expr: Expr, def_use: DefineUseAnalysis) bool

Analyze the given expression and return True if it is pure, False otherwise.

class fpy2.analysis.Reachability

Bases: object

This class performs a reachability analysis on an FPy program.

It classifies statements each statement as - “possibly reachable”: there exists an execution path from the entry to the statement. - “unreachable”: there is no execution path from the entry to the statement.

The dataflow equation for reachability is:

OUT[s] = |_| IN[s]

The resulting ReachabilityAnalysis has four fields: - has_entry: is the statement reachable? - has_exit: is there a reachable path through the statement? - ret_stmts: all return statements in the program - `has_fallthrough: is there a path that does not end in a return statement

The analysis may optionally assert three properties: - check_all_reachable: every statement in the program is reachable - check_no_fallthrough: every program path ends at a return statement - check_single_exit: the program has only one return statement

static analyze(func: FuncDef, *, check_all_reachable: bool = False, check_no_fallthrough: bool = False, check_single_exit: bool = False)

Run the analysis. Each check_* flag opts into one of the three optional assertions documented on the class.

class fpy2.analysis.ReachingDefs

Bases: object

Reaching definitions analysis.

This analysis computes: - the set of definitions available at the entry/exit of each block; - the set of definitions introduced at each statement; - set of all definitions for each variable;

Each definition has a reference its immediate previous definition(s) which represents a (possibly cyclic) graph of definitions.

class fpy2.analysis.SyntaxCheck

Bases: object

Syntax checker for the FPy AST.

Basic syntax check to eliminate malformed FPy programs that the parser can’t detect.

Rules enforced:

Variables:

  • any variables must be defined before it is used;

If statements:

  • any variable must be defined along both branches when used after the if statement

Function calls:

  • the function part of a call must be either a variable or an attribute access (e.g., obj.method)

static check(func: FuncDef, *, free_vars: set[NamedId] | None = None, ignore_unknown: bool = True, allow_wildcard: bool = False)

Analyzes the function for syntax errors.

Returns the subset of free_vars that are relevant to FPy.

class fpy2.analysis.TypeInfer

Bases: object

Type inference for the FPy language.

FPy is not statically typed, but compilation may require statically determining the types throughout the program. The FPy type inference algorithm is a Hindley-Milner based algorithm.

static check(func: FuncDef, def_use: DefineUseAnalysis | None = None) TypeAnalysis

Analyzes the function for type errors.

Produces a type signature for the function if it is well-typed and a mapping from definition to type.

static infer_primitive(prim: Primitive) FunctionType

Returns the type signature of a primitive.

Transformations

class fpy2.transform.ConstFold

Bases: object

Constant folding and propagation.

Substitutes any expression whose value is statically known with a literal AST node — operators, Var and Attribute lookups, Compare chains, context-constructor Call s, tuple / list literals, list indexing and slicing. Static values come from PartialEval; pass an existing analysis via partial_eval= to avoid re-running it.

Excluded: values that don’t have an FPy literal form (Python types, functions, modules, containers whose elements aren’t literal-emittable) — left as the original AST.

static apply(func: FuncDef, *, def_use: DefineUseAnalysis | None = None, partial_eval: PartialEvalInfo | None = None, enable_context: bool = True, enable_op: bool = True) FuncDef

Apply constant folding to func. Pass cached def_use / partial_eval to share with other passes. Set enable_context=False to skip Context folds or enable_op=False to skip everything else.

static apply_with_status(func: FuncDef, *, def_use: DefineUseAnalysis | None = None, partial_eval: PartialEvalInfo | None = None, enable_context: bool = True, enable_op: bool = True) tuple[FuncDef, bool]

Same as apply() but also returns a changed flag — True iff at least one substitution occurred.

class fpy2.transform.CopyPropagate

Bases: object

Copy propagation.

This transform replaces any variable that is assigned another variable.

static apply(func: FuncDef, *, names: set[NamedId] | None = None) FuncDef

Applies copy propagation to the given AST.

static apply_with_status(func: FuncDef, *, names: set[NamedId] | None = None) tuple[FuncDef, bool]

Same as apply() but also returns a changed flag — True iff at least one alias was propagated.

class fpy2.transform.DeadCodeEliminate

Bases: object

Dead code elimination.

  • removes unused assignments / phi defs / free variables

  • rewrites unused TupleBinding leaves to UnderscoreId; drops the whole assign when every leaf becomes _ and the RHS is pure

  • removes never-executed branches (if False:, while False:)

  • collapses trivially-true / trivially-false if / while

  • removes assert True and pure EffectStmt s

  • removes self-assignments (x = x) and stray pass

  • drops empty bodies and unused with-block targets

class fpy2.transform.ForBundling

Bases: object

Transformation pass to bundle updated variables in for loops.

This pass rewrites the IR to bundle updated variables in for loops into a single variable. This transformation ensures there is only one phi node per while loop.

class fpy2.transform.ForUnpack

Bases: object

Transformation pass to move any tuple unpacking in a for loop to its body:

for x, y in iterable:
    ...

becomes:

for t in iterable:
    x, y = t
    ...

where t is a fresh variable.

static apply(func: FuncDef) FuncDef

Apply the transformation to the given function definition.

class fpy2.transform.ForUnroll

Bases: object

Unrolling for for loops.

A for loop over an iterable is rewritten to consume k = times + 1 consecutive elements per iteration of the rewritten loop. The iterable is materialized once and indexed; loop-control and index arithmetic run under the exact integer context while the iterable and body keep their ambient rounding (see the module docstring). When the array-size analysis proves the iterable’s length, the remainder handling is resolved at compile time.

static apply(func: FuncDef, where: int | None = None, times: int = 1, strategy: ForUnrollStrategy = ForUnrollStrategy.PEEL, reaching_defs: ReachingDefsAnalysis | None = None, array_size: ArraySizeAnalysis | None = None, temp_id: NamedId | None = None, len_id: NamedId | None = None, idx_id: NamedId | None = None)

Apply the transformation.

Parameters:
  • where (int | None) – The index of the for loop to unroll. If None, unroll all for loops.

  • times (int) – Number of extra body copies per iteration; the unroll factor is k = times + 1 elements consumed per rewritten iteration. times == 0 leaves the loop unchanged.

  • strategy (ForUnrollStrategy) – How to handle a length that is not a multiple of k (see ForUnrollStrategy). Defaults to PEEL, which is correct for any length; STRICT instead requires divisibility.

  • reaching_defs (ReachingDefsAnalysis | None) – Pre-computed reaching-definitions analysis (for fresh names).

  • array_size (ArraySizeAnalysis | None) – Pre-computed array-size analysis, used to discharge the remainder check when an iterable’s length is statically known.

class fpy2.transform.FuncInline

Bases: object

Function inlining.

static apply(func: FuncDef, *, def_use: DefineUseAnalysis | None = None, funcs: Iterable[FuncDef] | None = None, recursive: bool = True) FuncDef

Applies function inlining to func returning the transformed function.

Raises CallGraphError if the call graph reachable from func contains a cycle (FPy forbids recursion; inlining a recursive call would not terminate).

class fpy2.transform.IfBundling

Bases: object

Transformation pass to pack mutated variables in if statements.

This pass rewrites the AST to pack mutated variables in an if statement into a single mutated variable. This ensures a convenient translation into more functional languages.

class fpy2.transform.LiftContext

Bases: object

Transformation pass to lift context expressions to the top-level.

class fpy2.transform.Monomorphize

Bases: object

Monomorphize pass.

This pass overrides type or context variables with more specific types.

class fpy2.transform.RenameTarget

Bases: object

Renames targets, that is, left-hand side of assignments, in an FPy program.

static apply(func: FuncDef, rename: dict[NamedId, NamedId])

Renames targets in the function.

static apply_block(block: StmtBlock, rename: dict[NamedId, NamedId])

Renames targets in a block.

class fpy2.transform.RoundElim

Bases: object

Rewrite expressions whose implicit rounding to the active context is provably an identity. Arithmetic ops (Add/Sub/Mul/Abs/Neg) hoist into with fp.REAL: preambles; explicit Round / Cast nodes collapse to their argument.

See the module docstring for the rewrite shape, the greedy-outermost policy, and the soundness invariants.

static apply(func: FuncDef) FuncDef

Apply the transformation to a FuncDef. Returns a new FuncDef; the input is not mutated.

Runs DefineUse, ContextUse, and FormatInfer internally — these are pre-analyses the rewrite needs to decide eliminability per expression.

class fpy2.transform.SimplifyIf

Bases: object

Control flow simplification:

Transforms if statements into if expressions. The inner block is hoisted into the outer block and each phi variable is made explicit with an if expression.

class fpy2.transform.SplitLoop

Bases: object

Split loop transformation.

This transformation rewrites a single for loop:

for x1, ..., xk in xs:
    BODY[x1, ..., xk]

into a nested loop:

t1 = xs
t2 = factor
with Z:
    n = len(t1)
    assert n % t2 == 0
for i in range(0, n, t2):
    with Z:
        slice = t1[i:i+t2]
    for x1, ..., xk in slice:
        BODY[x1, ..., xk]
class fpy2.transform.SubstVar

Bases: object

Replaces occurence of variables with expressions.

This transformation is the basis for: - copy propagation - constant propagation

static apply(func: FuncDef, def_use: DefineUseAnalysis, subst: Mapping[AssignDef, Expr])

Given a substitution from variable definitions to expressions, replaces all occurences of the variables with the corresponding expressions. The original definition will not be renamed.

class fpy2.transform.WhileBundling

Bases: object

Transformation pass to bundle updated variables in while loops.

This pass rewrites the IR to bundle updated variables in while loops into a single variable. This transformation ensures there is only one phi node per while loop.

class fpy2.transform.WhileUnroll

Bases: object

Unrolling for while loops.

static apply(func: FuncDef, where: int | None = None, times: int = 1)

Apply the transformation.

Parameters:
  • where (int | None) – The index of the while loop to unroll. If None, unroll all while loops.

  • times (int) – The number of times to unroll the loop.

class fpy2.transform.ZipElim

Bases: object

Rewrite zip(...) iterables into indexed loops over the source vectors. See the module docstring for the patterns recognized and the ordering constraint with fpy2.transform.ForUnpack.

static apply(func: FuncDef) FuncDef

Apply the transformation to a FuncDef. Returns a new FuncDef; the input is not mutated.