66"""
77
88
9- import typing
10- from typing import Any , Callable , Dict , Optional , Type
9+ from typing import Any , Callable , Dict , List , Optional , Type
1110
1211from mathics .core .atoms import Complex , Integer , Rational , Real , String , SymbolI
1312from mathics .core .convert .expression import to_expression_with_specialization
@@ -70,6 +69,35 @@ def _boxed_string(string: str, **options):
7069 return StyleBox (String (string ), ** options )
7170
7271
72+ def compare_precedence (
73+ element : BaseElement , precedence : Optional [int ] = None
74+ ) -> Optional [int ]:
75+ """
76+ compare the precedence of the element regarding a precedence value.
77+ If both precedences are equal, return 0. If precedence of the
78+ first element is higher, return 1, otherwise -1.
79+ If precedences cannot be compared, return None.
80+ """
81+ while element .has_form ("HoldForm" , 1 ):
82+ element = element .elements [0 ]
83+
84+ if precedence is None :
85+ return None
86+ if element .has_form (("Infix" , "Prefix" , "Postfix" ), 3 , None ):
87+ element_prec = element .elements [2 ].value
88+ elif element .has_form ("PrecedenceForm" , 2 ):
89+ element_prec = element .elements [1 ].value
90+ # For negative values, ensure that the element_precedence is at least the precedence. (Fixes #332)
91+ elif isinstance (element , (Integer , Real )) and element .value < 0 :
92+ element_prec = precedence
93+ else :
94+ element_prec = builtins_precedence .get (element .get_head_name ())
95+
96+ if element_prec is None :
97+ return None
98+ return 0 if element_prec == precedence else (1 if element_prec > precedence else - 1 )
99+
100+
73101# 640 = sys.int_info.str_digits_check_threshold.
74102# Someday when 3.11 is the minimum version of Python supported,
75103# we can replace the magic value 640 below with sys.int.str_digits_check_threshold.
@@ -211,7 +239,6 @@ def do_format_element(
211239 Applies formats associated to the expression and removes
212240 superfluous enclosing formats.
213241 """
214-
215242 from mathics .core .definitions import OutputForms
216243
217244 evaluation .inc_recursion_depth ()
@@ -234,6 +261,7 @@ def do_format_element(
234261 if include_form :
235262 expr = Expression (form , expr )
236263 return expr
264+
237265 # Repeated and RepeatedNull confuse the formatter,
238266 # so we need to hardlink their format rules:
239267 if head is SymbolRepeated :
@@ -279,8 +307,8 @@ def format_expr(expr):
279307
280308 formatted = format_expr (expr ) if isinstance (expr , EvalMixin ) else None
281309 if formatted is not None :
282- do_format = element_formatters .get (type (formatted ), do_format_element )
283- result = do_format (formatted , evaluation , form )
310+ do_format_fn = element_formatters .get (type (formatted ), do_format_element )
311+ result = do_format_fn (formatted , evaluation , form )
284312 if include_form and result is not None :
285313 result = Expression (form , result )
286314 return result
@@ -297,8 +325,8 @@ def format_expr(expr):
297325 # just return it as it is.
298326 if len (expr .get_elements ()) != 1 :
299327 return expr
300- do_format = element_formatters .get (type (element ), do_format_element )
301- result = do_format (expr , evaluation , form )
328+ do_format_fn = element_formatters .get (type (element ), do_format_element )
329+ result = do_format_fn (expr , evaluation , form )
302330 if isinstance (result , Expression ):
303331 expr = result
304332
@@ -307,13 +335,14 @@ def format_expr(expr):
307335 and not isinstance (expr , (Atom , BoxElementMixin ))
308336 and head not in (SymbolGraphics , SymbolGraphics3D )
309337 ):
310- # print("Not inside graphics or numberform, and not is atom")
311- new_elements = [
312- element_formatters .get (type (element ), do_format_element )(
313- element , evaluation , form
338+ new_elements = tuple (
339+ (
340+ element_formatters .get (type (element ), do_format_element )(
341+ element , evaluation , form
342+ )
343+ for element in expr .elements
314344 )
315- for element in expr .elements
316- ]
345+ )
317346 expr_head = expr .head
318347 do_format = element_formatters .get (type (expr_head ), do_format_element )
319348 head = do_format (expr_head , evaluation , form )
@@ -367,7 +396,7 @@ def do_format_complex(
367396 form ,
368397 )
369398
370- parts : typing . List [Any ] = []
399+ parts : List [Any ] = []
371400 if element .is_machine_precision () or not element .real .is_zero :
372401 parts .append (element .real )
373402 if element .imag .sameQ (Integer (1 )):
@@ -418,27 +447,12 @@ def parenthesize(
418447 If when_equal is True, parentheses will be added if the two
419448 precedence values are equal.
420449 """
421- while element .has_form ("HoldForm" , 1 ):
422- element = element .elements [0 ]
423-
424- if element .has_form (("Infix" , "Prefix" , "Postfix" ), 3 , None ):
425- element_prec = element .elements [2 ].value
426- elif element .has_form ("PrecedenceForm" , 2 ):
427- element_prec = element .elements [1 ].value
428- # If "element" is a negative number, we need to parenthesize the number. (Fixes #332)
429- elif isinstance (element , (Integer , Real )) and element .value < 0 :
430- # Force parenthesis by adjusting the surrounding context's precedence value,
431- # We can't change the precedence for the number since it, doesn't
432- # have a precedence value.
433- element_prec = precedence
434- else :
435- element_prec = builtins_precedence .get (element .get_head ())
436- if precedence is not None and element_prec is not None :
437- if precedence > element_prec or (precedence == element_prec and when_equal ):
438- return Expression (
439- SymbolRowBox ,
440- ListExpression (StringLParen , element_boxes , StringRParen ),
441- )
450+ cmp = compare_precedence (element , precedence )
451+ if cmp is not None and (cmp == - 1 or cmp == 0 and when_equal ):
452+ return Expression (
453+ SymbolRowBox ,
454+ ListExpression (String ("(" ), element_boxes , String (")" )),
455+ )
442456 return element_boxes
443457
444458
0 commit comments