6363)
6464from mathics .core .systemsymbols import (
6565 SymbolAborted ,
66- SymbolAlternatives ,
67- SymbolBlank ,
68- SymbolBlankNullSequence ,
69- SymbolBlankSequence ,
70- SymbolCondition ,
7166 SymbolDirectedInfinity ,
7267 SymbolFunction ,
7368 SymbolMinus ,
74- SymbolOptional ,
75- SymbolOptionsPattern ,
7669 SymbolOverflow ,
77- SymbolPattern ,
78- SymbolPatternTest ,
7970 SymbolPower ,
8071 SymbolSequence ,
8172 SymbolSin ,
8273 SymbolSlot ,
8374 SymbolSqrt ,
8475 SymbolSubtract ,
76+ SymbolUndefined ,
8577 SymbolUnevaluated ,
86- SymbolVerbatim ,
8778)
8879from mathics .eval .tracing import trace_evaluate
8980
@@ -743,7 +734,7 @@ def flatten_with_respect_to_head(
743734 break
744735 if do_flatten :
745736 new_elements : List [BaseElement ] = []
746- for element in self ._elements :
737+ for i , element in enumerate ( self ._elements ) :
747738 if (
748739 isinstance (element , Expression )
749740 and element .get_head ().sameQ (head )
@@ -753,8 +744,9 @@ def flatten_with_respect_to_head(
753744 head , pattern_only , callback , level = sub_level
754745 )
755746 if callback is not None :
756- callback (new_element ._elements , element )
757- new_elements .extend (new_element ._elements )
747+ new_elements += callback (new_element ._elements , i )
748+ else :
749+ new_elements .extend (new_element ._elements )
758750 else :
759751 new_elements .append (element )
760752 return to_expression_with_specialization (self ._head , * new_elements )
@@ -1119,25 +1111,51 @@ def rewrite_apply_eval_step(self, evaluation) -> Tuple[BaseElement, bool]:
11191111 assert self .elements_properties is not None
11201112
11211113 recompute_properties = False
1114+ unevaluated_pairs : Dict [int , BaseElement ] = {}
11221115
11231116 # @timeit
11241117 def eval_elements ():
11251118 # @timeit
1126- def eval_range (indices ):
1119+ def eval_range (indices : range ):
1120+ """
1121+ This is called to evaluate arguments of a function when the function
1122+ doesn't have some sort of Hold property for parameters named by "indices".
1123+ """
11271124 nonlocal recompute_properties
11281125 recompute_properties = False
11291126 for index in indices :
11301127 element = elements [index ]
1131- if not ( element .is_literal or element . has_form ( "Unevaluated" , 1 )) :
1128+ if not element .is_literal :
11321129 if isinstance (element , EvalMixin ):
11331130 new_value = element .evaluate (evaluation )
11341131 # We need id() because != by itself is too permissive
11351132 if id (element ) != id (new_value ):
1136- recompute_properties = True
1133+ if (
1134+ hasattr (new_value , "head" )
1135+ and new_value .head is SymbolUnevaluated
1136+ ):
1137+ # Strip off Unevaluated[], but keep property of the expression inside
1138+ # Unevaluated[] to be "fully evaluated". (Or, rather, not
1139+ # needing further evaluation.)
1140+ # We also have to save the old value in case there is no function
1141+ # that gets applied.
1142+ new_value_first = new_value .elements [0 ]
1143+
1144+ # I don't understand why, but if we have Unevaluated[Sequnce[...]], that should
1145+ # not be changed.
1146+ if not (
1147+ hasattr (new_value_first , "head" )
1148+ and new_value_first .head is SymbolSequence
1149+ ):
1150+ new_value = new_value_first
1151+ unevaluated_pairs [index ] = element
1152+ else :
1153+ recompute_properties = True
1154+
11371155 elements [index ] = new_value
11381156
11391157 # @timeit
1140- def rest_range (indices ):
1158+ def rest_range (indices : range ):
11411159 nonlocal recompute_properties
11421160 if not A_HOLD_ALL_COMPLETE & attributes :
11431161 if self ._does_not_contain_symbol ("System`Evaluate" ):
@@ -1205,54 +1223,26 @@ def rest_range(indices):
12051223 new ._build_elements_properties ()
12061224 elements = new ._elements
12071225
1208- # comment @mmatera: I think this is wrong now, because alters
1209- # singletons... (see PR #58) The idea is to mark which elements was
1210- # marked as "Unevaluated" Also, this consumes time for long lists, and
1211- # is useful just for a very unfrequent expressions, involving
1212- # `Unevaluated` elements. Notice also that this behaviour is broken
1213- # when the argument of "Unevaluated" is a symbol (see comment and tests
1214- # in test/test_unevaluate.py)
1215-
1216- for element in elements :
1217- element .unevaluated = False
1218-
1219- # If HoldAllComplete Attribute (flag ``A_HOLD_ALL_COMPLETE``) is not set,
1220- # and the expression has elements of the form `Unevaluated[element]`
1221- # change them to `element` and set a flag `unevaluated=True`
1222- # If the evaluation fails, use this flag to restore back the initial form
1223- # Unevaluated[element]
1224-
1225- # comment @mmatera:
1226- # what we need here is some way to track which elements are marked as
1227- # Unevaluated, that propagates by flatten, and at the end,
1228- # to recover a list of positions that (eventually)
1229- # must be marked again as Unevaluated.
1230-
1231- if not A_HOLD_ALL_COMPLETE & attributes :
1232- dirty_elements = None
1233-
1234- for index , element in enumerate (elements ):
1235- if element .has_form ("Unevaluated" , 1 ):
1236- if dirty_elements is None :
1237- dirty_elements = list (elements )
1238- dirty_elements [index ] = element .get_element (0 )
1239- dirty_elements [index ].unevaluated = True
1240-
1241- if dirty_elements :
1242- new = Expression (head , * dirty_elements )
1243- elements = dirty_elements
1244- new ._build_elements_properties ()
1245-
1246- # If the Attribute ``Flat`` (flag ``A_FLAT``) is set, calls
1247- # flatten with a callback that set elements as unevaluated
1248- # too.
1249- def flatten_callback (new_elements , old ):
1250- for element in new_elements :
1251- element .unevaluated = old .unevaluated
1226+ def flatten_callback_for_Unevaluated (new_elements : tuple , i : int ) -> list :
1227+ """If the Attribute ``Flat`` (flag ``A_FLAT``) is set, this
1228+ function is called to reinstate any Unevaluated[] stripping that
1229+ was performed earlier in parameter evalaution."""
1230+ if i in unevaluated_pairs .keys ():
1231+ new_unevaluated_elements = []
1232+ for element in new_elements :
1233+ new_unevaluated_elements .append (
1234+ Expression (SymbolUnevaluated , element )
1235+ )
1236+ del unevaluated_pairs [i ]
1237+ return new_unevaluated_elements
1238+ else :
1239+ return list (new_elements )
12521240
12531241 if A_FLAT & attributes :
12541242 assert isinstance (new ._head , Symbol )
1255- new = new .flatten_with_respect_to_head (new ._head , callback = flatten_callback )
1243+ new = new .flatten_with_respect_to_head (
1244+ new ._head , callback = flatten_callback_for_Unevaluated
1245+ )
12561246 if new .elements_properties is None :
12571247 new ._build_elements_properties ()
12581248
@@ -1373,18 +1363,14 @@ def rules():
13731363 # Step 7: If we are here, is because we didn't find any rule that
13741364 # matches the expression.
13751365
1376- dirty_elements = None
1377-
1378- # Expression did not change, re-apply Unevaluated
1379- for index , element in enumerate (new ._elements ):
1380- if element .unevaluated :
1381- if dirty_elements is None :
1382- dirty_elements = list (new ._elements )
1383- dirty_elements [index ] = Expression (SymbolUnevaluated , element )
1384-
1385- if dirty_elements :
1386- new = Expression (head )
1387- new .elements = dirty_elements
1366+ # If any arguments were "Unevaluated[]" that we stripped off,
1367+ # put them back here. WMA specifieds that Unevaluated[] function should remain
1368+ # in the result when no function is applied, but they do get stripped of when there
1369+ if unevaluated_pairs :
1370+ new_elements = list (elements )
1371+ for index , unevaluated_element in unevaluated_pairs .items ():
1372+ new_elements [index ] = unevaluated_element
1373+ new .elements = tuple (new_elements )
13881374
13891375 # Step 8: Update the cache. Return the new compound Expression and
13901376 # indicate that no further evaluation is needed.
@@ -1486,17 +1472,17 @@ def to_mpmath(self):
14861472
14871473 def to_python (self , * args , ** kwargs ) -> Any :
14881474 """
1489- Convert the Expression to a Python object:
1490- List[...] -> Python list
1491- DirectedInfinity[1] -> inf
1492- DirectedInfinity[-1] -> -inf
1493- True/False -> True/False
1494- Null -> None
1495- Symbol -> '...'
1496- String -> '"..."'
1497- Function -> python function
1498- numbers -> Python number
1499- If kwarg n_evaluation is given, apply N first to the expression.
1475+ Convert the Expression to a Python object:
1476+ v List[...] -> Python list
1477+ DirectedInfinity[1] -> inf
1478+ DirectedInfinity[-1] -> -inf
1479+ True/False -> True/False
1480+ Null -> None
1481+ Symbol -> '...'
1482+ String -> '"..."'
1483+ Function -> python function
1484+ numbers -> Python number
1485+ If kwarg n_evaluation is given, apply N first to the expression.
15001486 """
15011487 from mathics .core .builtin import mathics_to_python
15021488
0 commit comments