Source code for ufl.precedence
"""Precedence handling."""
# Copyright (C) 2008-2016 Martin Sandve Alnæs
#
# This file is part of UFL (https://www.fenicsproject.org)
#
# SPDX-License-Identifier: LGPL-3.0-or-later
import warnings
# FIXME: This code is crap...
[docs]def parstr(child, parent, pre="(", post=")", format=str):
"""Parstr."""
# Execute when needed instead of on import, which leads to all
# kinds of circular trouble. Fixing this could be an optimization
# of str(expr) though.
if not hasattr(parent, "_precedence"):
assign_precedences(build_precedence_list())
# We want child to be evaluated fully first, and if the parent has
# higher precedence we later wrap in ().
s = format(child)
# Operators where operands are always parenthesized because
# precedence is not defined below
if parent._precedence == 0:
return pre + s + post
# If parent operator binds stronger than child, must parenthesize
# child
# FIXME: Is this correct for all possible positions of () in a + b + c?
# FIXME: Left-right rule
if parent._precedence > child._precedence: # parent = indexed, child = terminal
return pre + s + post
# Nothing needed
return s
[docs]def build_precedence_list():
"""Build precedence list."""
from ufl.classes import (
Abs,
BesselFunction,
Division,
Indexed,
IndexSum,
MathFunction,
Operator,
Power,
Product,
Sum,
Terminal,
)
# TODO: Fill in other types...
# Power <= Transposed
precedence_list = []
# Default operator behaviour: should always add parentheses
precedence_list.append((Operator,))
precedence_list.append((Sum,))
# sum_i a + b = (sum_i a) + b != sum_i (a + b), sum_i binds
# stronger than +, but weaker than product
precedence_list.append((IndexSum,))
precedence_list.append(
(
Product,
Division,
)
)
# NB! Depends on language!
precedence_list.append((Power, MathFunction, BesselFunction, Abs))
precedence_list.append((Indexed,))
# Default terminal behaviour: should never add parentheses
precedence_list.append((Terminal,))
return precedence_list
[docs]def build_precedence_mapping(precedence_list):
"""Given a precedence list, build a dict with class->int mappings.
Utility function used by some external code.
"""
from ufl.classes import Expr, abstract_classes, all_ufl_classes
pm = {}
missing = set()
# Assign integer values for each precedence level
k = 0
for p in precedence_list:
for c in p:
pm[c] = k
k += 1
# Check for missing classes, fill in subclasses
for c in all_ufl_classes:
if c not in abstract_classes and c not in pm:
b = c.__bases__[0]
while b is not Expr:
if b in pm:
pm[c] = pm[b]
break
b = b.__bases__[0]
if c not in pm:
missing.add(c)
return pm, missing
[docs]def assign_precedences(precedence_list):
"""Given a precedence list, assign ints to class._precedence."""
pm, missing = build_precedence_mapping(precedence_list)
for c, p in sorted(pm.items(), key=lambda x: x[0].__name__):
c._precedence = p
if missing:
warnings.warn(
"Missing precedence levels for classes:\n"
+ "\n".join(f" {c}" for c in sorted(missing))
)