Abstract Syntax Tree (AST)

The node classes below are documented because they are what you see when you inspect or print a parsed expression; they are not meant to be constructed by hand.

The backend-neutral expression tree and the serializers that render it.

formulate.from_root() and formulate.from_numexpr() both return an AST. Its node types (Literal, Symbol, UnaryOperator, BinaryOperator, Matrix and Call) are frozen dataclasses that hold canonical names rather than any one language’s spelling: the ROOT &&, the NumExpr & and the Python & all parse to BinaryOperator(operator="and", ...).

Rendering that tree back out is the job of AST.to_root(), AST.to_numexpr() and AST.to_python(). Each looks its node up in the tables in formulate.identifiers; a name that is missing from a table is how “this construct has no faithful equivalent here” is expressed, and raises ValueError rather than emitting something subtly different.

class formulate.AST.AST

Bases: object

Base class of every expression node.

Instances are produced by formulate.from_root() and formulate.from_numexpr(), not constructed directly, and are immutable: converting an expression never modifies it, so one parsed expression can be rendered to as many backends as needed.

str(node) gives a language-independent view of the tree in canonical names (add(x, pow(y, 2))), which is useful when debugging a conversion; use the to_* methods to get something an engine will accept.

Nodes are deliberately neither comparable nor hashable. == and hash() both raise TypeError; see __eq__().

to_numexpr() str

Render the expression as NumExpr source.

Named constants have no NumExpr spelling and are substituted by their numeric value, so pi comes back as 3.141592653589793.

Raises:

ValueError – if the expression uses a construct NumExpr has no equivalent for, such as array indexing, inf, or the element-wise TMath::Min/TMath::Max.

>>> import formulate
>>> formulate.from_root("TMath::Sqrt(x**2 + y**2)").to_numexpr()
'sqrt(((x ** 2) + (y ** 2)))'
to_root() str

Render the expression as a ROOT TTreeFormula string.

Raises:

ValueError – if the expression uses a construct ROOT has no equivalent for, such as ^ used as XOR or NumExpr’s where.

>>> import formulate
>>> formulate.from_numexpr("sqrt(x**2 + y**2)").to_root()
'TMath::Sqrt(((x ** 2) + (y ** 2)))'
to_python() str

Render the expression as plain Python, using NumPy for functions.

Function and constant names are emitted with an np. prefix, so the result is meant to be evaluated somewhere NumPy is imported as np. This backend is output-only: there is no from_python.

Raises:

ValueError – if the expression uses a construct with no NumPy equivalent that can be written as a single name, such as NumExpr’s contains.

>>> import formulate
>>> formulate.from_root("TMath::Sqrt(x**2 + y**2)").to_python()
'np.sqrt(((x ** 2) + (y ** 2)))'
property variables: OrderedSet[str]

The names the expression reads, in order of first appearance.

Named constants are excluded; see named_constants. For a TTree expression this is the set of branches that have to be read.

>>> import formulate
>>> list(formulate.from_root("x + TMath::Pi() * y").variables)
['x', 'y']
property named_constants: OrderedSet[str]

The constants the expression names, in order of first appearance.

Names are canonical rather than as written, so both TMath::E() and e_num report as exp1.

>>> import formulate
>>> list(formulate.from_root("x + TMath::Pi() * y").named_constants)
['pi']
property unnamed_constants: OrderedSet[int | float]

The numeric literals in the expression, in order of first appearance.

>>> import formulate
>>> list(formulate.from_root("2 * x + 1.5").unnamed_constants)
[2, 1.5]
class formulate.AST.Literal(value: int | float)

Bases: AST

A number written out in the expression text, such as 2 or 1.5.

class formulate.AST.Symbol(name: str)

Bases: AST

A value referred to by name: a variable, or a named constant.

Constants are held under their canonical name (pi, exp1) and are exactly those names that appear in formulate.identifiers.CONSTANTS; every other name is a variable.

class formulate.AST.UnaryOperator(operator: str, operand: AST)

Bases: AST

An operation with a single operand.

operator is one of the canonical names in formulate.identifiers.UNARY_OPERATORS: "pos", "neg", or "inv" for the logical NOT written ! in ROOT and ~ in NumExpr.

class formulate.AST.BinaryOperator(operator: str, left: AST, right: AST)

Bases: AST

An operation with two operands.

operator is one of the canonical names in formulate.identifiers.BINARY_OPERATORS"add", "lt", "and" and so on — never a backend’s spelling of it.

class formulate.AST.Matrix(var: AST, indices: tuple[AST, ...])

Bases: AST

An indexed access, var[i] or var[i][j].

ROOT writes one bracket pair per index and Python writes a single comma-separated one, so the same node renders as arr[1][2] for ROOT and arr[1, 2] for Python. NumExpr has no indexing at all and rejects it.

class formulate.AST.Call(function: str, arguments: tuple[AST, ...])

Bases: AST

A function applied to zero or more arguments.

function is a canonical name from formulate.identifiers.FUNCTIONS, which is what makes TMath::ATan2, atan2 and arctan2 the same node.