63
63
)
64
64
from mathics .core .systemsymbols import (
65
65
SymbolAborted ,
66
- SymbolAlternatives ,
67
- SymbolBlank ,
68
- SymbolBlankNullSequence ,
69
- SymbolBlankSequence ,
70
- SymbolCondition ,
71
66
SymbolDirectedInfinity ,
72
67
SymbolFunction ,
73
68
SymbolMinus ,
74
- SymbolOptional ,
75
- SymbolOptionsPattern ,
76
69
SymbolOverflow ,
77
- SymbolPattern ,
78
- SymbolPatternTest ,
79
70
SymbolPower ,
80
71
SymbolSequence ,
81
72
SymbolSin ,
82
73
SymbolSlot ,
83
74
SymbolSqrt ,
84
75
SymbolSubtract ,
76
+ SymbolUndefined ,
85
77
SymbolUnevaluated ,
86
- SymbolVerbatim ,
87
78
)
88
79
from mathics .eval .tracing import trace_evaluate
89
80
@@ -743,7 +734,7 @@ def flatten_with_respect_to_head(
743
734
break
744
735
if do_flatten :
745
736
new_elements : List [BaseElement ] = []
746
- for element in self ._elements :
737
+ for i , element in enumerate ( self ._elements ) :
747
738
if (
748
739
isinstance (element , Expression )
749
740
and element .get_head ().sameQ (head )
@@ -753,8 +744,9 @@ def flatten_with_respect_to_head(
753
744
head , pattern_only , callback , level = sub_level
754
745
)
755
746
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 )
758
750
else :
759
751
new_elements .append (element )
760
752
return to_expression_with_specialization (self ._head , * new_elements )
@@ -1119,25 +1111,51 @@ def rewrite_apply_eval_step(self, evaluation) -> Tuple[BaseElement, bool]:
1119
1111
assert self .elements_properties is not None
1120
1112
1121
1113
recompute_properties = False
1114
+ unevaluated_pairs : Dict [int , BaseElement ] = {}
1122
1115
1123
1116
# @timeit
1124
1117
def eval_elements ():
1125
1118
# @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
+ """
1127
1124
nonlocal recompute_properties
1128
1125
recompute_properties = False
1129
1126
for index in indices :
1130
1127
element = elements [index ]
1131
- if not ( element .is_literal or element . has_form ( "Unevaluated" , 1 )) :
1128
+ if not element .is_literal :
1132
1129
if isinstance (element , EvalMixin ):
1133
1130
new_value = element .evaluate (evaluation )
1134
1131
# We need id() because != by itself is too permissive
1135
1132
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
+
1137
1155
elements [index ] = new_value
1138
1156
1139
1157
# @timeit
1140
- def rest_range (indices ):
1158
+ def rest_range (indices : range ):
1141
1159
nonlocal recompute_properties
1142
1160
if not A_HOLD_ALL_COMPLETE & attributes :
1143
1161
if self ._does_not_contain_symbol ("System`Evaluate" ):
@@ -1205,54 +1223,26 @@ def rest_range(indices):
1205
1223
new ._build_elements_properties ()
1206
1224
elements = new ._elements
1207
1225
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 )
1252
1240
1253
1241
if A_FLAT & attributes :
1254
1242
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
+ )
1256
1246
if new .elements_properties is None :
1257
1247
new ._build_elements_properties ()
1258
1248
@@ -1373,18 +1363,14 @@ def rules():
1373
1363
# Step 7: If we are here, is because we didn't find any rule that
1374
1364
# matches the expression.
1375
1365
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 )
1388
1374
1389
1375
# Step 8: Update the cache. Return the new compound Expression and
1390
1376
# indicate that no further evaluation is needed.
@@ -1486,17 +1472,17 @@ def to_mpmath(self):
1486
1472
1487
1473
def to_python (self , * args , ** kwargs ) -> Any :
1488
1474
"""
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.
1500
1486
"""
1501
1487
from mathics .core .builtin import mathics_to_python
1502
1488
0 commit comments