diff --git a/mathics/builtin/list/constructing.py b/mathics/builtin/list/constructing.py index bb6212e5f..c9b84c6c3 100644 --- a/mathics/builtin/list/constructing.py +++ b/mathics/builtin/list/constructing.py @@ -566,12 +566,16 @@ class Table(IterationFunction): summary_text = "make a table of values of an expression" def get_result(self, elements, is_uniform=False) -> ListExpression: - return ListExpression( - *elements, - elements_properties=ElementsProperties( - elements_fully_evaluated=True, is_uniform=is_uniform - ), - ) + # The elements have just been evaluated, but that does not make them + # "fully evaluated": that would keep the list from ever being evaluated + # again, so that in + # l = Table[x, {1}]; x = 1; l + # l would stay {x}. Only a list of literal elements, like a list of + # numbers, is fully evaluated, and that is settled here, at creation. + result = ListExpression(*elements) + if result.is_literal: + result._build_elements_properties() + return result class Tuples(Builtin): diff --git a/mathics/core/expression.py b/mathics/core/expression.py index cd7b54e7d..d9417dcb9 100644 --- a/mathics/core/expression.py +++ b/mathics/core/expression.py @@ -436,9 +436,13 @@ def _build_elements_properties(self): element._build_elements_properties() # and now possibly adjust self.elements_properties.elements_fully_evaluted + # A literal element (e.g. {}, which Map over an empty list + # gives with pessimistic properties) is fully evaluated + # whatever its own properties say. if self.elements_properties.elements_fully_evaluated: self.elements_properties.elements_fully_evaluated = ( - element.elements_properties.elements_fully_evaluated + element.is_literal + or element.elements_properties.elements_fully_evaluated ) if element.is_literal: @@ -2149,7 +2153,8 @@ def convert_expression_elements( if elements_properties.elements_fully_evaluated: elements_properties.elements_fully_evaluated = ( - converted_elt.elements_properties.elements_fully_evaluated + converted_elt.is_literal + or converted_elt.elements_properties.elements_fully_evaluated ) if elements_properties.is_ordered and last_converted_elt is not None: diff --git a/test/builtin/list/test_constructing.py b/test/builtin/list/test_constructing.py index 22159b969..9201fe8cd 100644 --- a/test/builtin/list/test_constructing.py +++ b/test/builtin/list/test_constructing.py @@ -2,6 +2,7 @@ """ Unit tests for mathics.builtins.list.constructing """ + from test.helper import check_evaluation import pytest @@ -72,3 +73,59 @@ def test_range(str_expr, str_expected, failure_message): ) def test_table(str_expr, str_expected, failure_message): check_evaluation(str_expr, str_expected, failure_message) + + +@pytest.mark.parametrize( + ("str_expr", "str_expected", "failure_message"), + [ + (None, None, None), + ( + "ClearAll[l, x]; l = Table[x, {1}]; x = 1; l", + "{1}", + "A table sees a definition made after it.", + ), + ( + "ClearAll[l, f, y]; l = Table[f[y], {1}]; f[y_] := 7; l", + "{7}", + "A table sees a rule defined after it.", + ), + ( + "ClearAll[t, x]; t = Table[i + x, {i, 2}]; x = 1; t", + "{2, 3}", + None, + ), + ("{f /@ {}}", "{{}}", "A list of literals with pessimistic properties."), + ("Table[f /@ {}, {2}]", "{{}, {}}", None), + ("ClearAll[l, t, f, x, y]", "Null", None), + ], +) +def test_table_is_evaluated_again(str_expr, str_expected, failure_message): + check_evaluation(str_expr, str_expected, failure_message) + + +def test_table_fully_evaluated_at_creation(): + """A table of literal elements, like a list of numbers, is known to be + fully evaluated as soon as Table builds it; a table with other elements + is not.""" + from mathics.builtin.list.constructing import Table + from mathics.core.atoms import Integer1, Integer2 + from mathics.core.expression import Expression + from mathics.core.list import ListExpression + from mathics.core.symbols import Symbol + from mathics.core.systemsymbols import SymbolTimes + + numbers = Table.get_result(None, [Integer1, Integer2]) + assert numbers.elements_properties is not None + assert numbers.elements_properties.elements_fully_evaluated + + pairs = Table.get_result( + None, [ListExpression(Integer1, Integer2), ListExpression(Integer2, Integer1)] + ) + assert pairs.elements_properties.elements_fully_evaluated + + x = Symbol("Global`x") + symbolic = Table.get_result(None, [x, Expression(SymbolTimes, Integer2, x)]) + assert not ( + symbolic.elements_properties + and symbolic.elements_properties.elements_fully_evaluated + )