diff --git a/mathics/builtin/atomic/strings.py b/mathics/builtin/atomic/strings.py index 22291b7ec..3fe7a5582 100644 --- a/mathics/builtin/atomic/strings.py +++ b/mathics/builtin/atomic/strings.py @@ -616,9 +616,6 @@ class _StringFind(Builtin, ABC): messages = { "srep": "`1` is not a valid string replacement rule.", - "innf": ( - "Non-negative integer or Infinity expected at " "position `1` in `2`." - ), } def _find(py_stri, py_rules, py_n, flags): diff --git a/mathics/builtin/list/eol.py b/mathics/builtin/list/eol.py index bce7422ab..1eef7990f 100644 --- a/mathics/builtin/list/eol.py +++ b/mathics/builtin/list/eol.py @@ -8,10 +8,9 @@ """ from itertools import chain -from typing import Optional from mathics.builtin.box.layout import RowBox -from mathics.core.atoms import Integer, Integer0, Integer1, String +from mathics.core.atoms import Integer, Integer0, Integer1, Integer3, Integer4, String from mathics.core.attributes import ( A_HOLD_FIRST, A_HOLD_REST, @@ -30,7 +29,7 @@ PartError, PartRangeError, ) -from mathics.core.expression import Expression +from mathics.core.expression import Expression, ExpressionInfinity from mathics.core.list import ListExpression from mathics.core.rules import Rule from mathics.core.symbols import Atom, Symbol, SymbolNull, SymbolTrue @@ -42,6 +41,7 @@ SymbolKey, SymbolMakeBoxes, SymbolMissing, + SymbolSelect, SymbolSequence, SymbolSet, ) @@ -437,7 +437,6 @@ class DeleteCases(Builtin): messages = { "level": "Level specification `1` is not of the form n, {n}, or {m, n}.", - "innf": "Non-negative integer or Infinity expected at position 4 in `1`", } summary_text = "delete all occurrences of a pattern" @@ -455,14 +454,15 @@ def eval_ls_n(self, items, pattern, levelspec, n, evaluation): levelspec = python_levelspec(levelspec) - if n is SymbolInfinity: + if n is SymbolInfinity or ExpressionInfinity == n: n = -1 - elif n.get_head_name() == "System`Integer": - n = n.get_int_value() + elif isinstance(n, Integer): + n = n.value if n < 0: evaluation.message( "DeleteCases", "innf", + Integer4, Expression(SymbolDeleteCases, items, pattern, levelspec, n), ) else: @@ -1533,10 +1533,26 @@ class Select(Builtin): def eval(self, items, expr, evaluation: Evaluation): "Select[items_, expr_]" - return self.eval_with_n(items, expr, None, evaluation) + return self.eval_with_n(items, expr, SymbolInfinity, evaluation) - def eval_with_n(self, items, expr, n: Optional[Integer], evaluation: Evaluation): - "Select[items_, expr_, n_Integer]" + def eval_with_n(self, items, expr, n, evaluation: Evaluation): + "Select[items_, expr_, n_]" + + count_is_valid = True + if n is SymbolInfinity or ExpressionInfinity == n: + count = None + elif isinstance(n, Integer): + count = n.value + if count < 0: + count_is_valid = False + else: + count_is_valid = False + + if not count_is_valid: + evaluation.message( + "Select", "innf", Integer3, Expression(SymbolSelect, items, expr, n) + ) + return if isinstance(items, Atom): evaluation.message("Select", "normal") @@ -1546,7 +1562,6 @@ def cond(element): test = Expression(expr, element) return test.evaluate(evaluation) is SymbolTrue - count = n.value if n is not None else None return items.filter(items.head, cond, evaluation, count=count) @@ -1633,6 +1648,7 @@ class UpTo(Builtin): """ + # TODO: is there as way we can use general's innf? messages = { "innf": "Expected non-negative integer or infinity at position 1 in ``.", "argx": "UpTo expects 1 argument, `1` arguments were given.", diff --git a/mathics/builtin/messages.py b/mathics/builtin/messages.py index 31dc48d38..e926ab001 100644 --- a/mathics/builtin/messages.py +++ b/mathics/builtin/messages.py @@ -198,7 +198,7 @@ class General(Builtin): "Single or list of non-negative integers expected at " "position `1`." ), "indet": "Indeterminate expression `1` encountered.", - "innf": "Non-negative integer or Infinity expected at position `1`.", + "innf": "Non-negative integer or Infinity expected at position `1` in `2`", "int": "Integer expected.", "intp": "Positive integer expected.", "intnn": "Non-negative integer expected.", diff --git a/mathics/builtin/patterns/rules.py b/mathics/builtin/patterns/rules.py index 2055f305a..b95aab8ef 100644 --- a/mathics/builtin/patterns/rules.py +++ b/mathics/builtin/patterns/rules.py @@ -69,16 +69,21 @@ from typing import Optional as OptionalType -from mathics.core.atoms import Integer, Integer0, Integer2, Number +from mathics.core.atoms import Integer, Integer0, Integer2, Integer3, Number from mathics.core.attributes import A_HOLD_REST, A_PROTECTED, A_SEQUENCE_HOLD from mathics.core.builtin import AtomBuiltin, Builtin, InfixOperator, PatternError from mathics.core.element import BaseElement from mathics.core.evaluation import Evaluation from mathics.core.exceptions import InvalidLevelspecError -from mathics.core.expression import Expression +from mathics.core.expression import Expression, ExpressionInfinity from mathics.core.list import ListExpression from mathics.core.symbols import SymbolTrue -from mathics.core.systemsymbols import SymbolInfinity, SymbolRule, SymbolRuleDelayed +from mathics.core.systemsymbols import ( + SymbolInfinity, + SymbolReplaceList, + SymbolRule, + SymbolRuleDelayed, +) from mathics.eval.rules import ( Dispatch, create_rules, @@ -341,9 +346,6 @@ class ReplaceList(Builtin): Like in 'ReplaceAll', $rules$ can be a nested list: >> ReplaceList[{a, b, c}, {{{___, x__, ___} -> {x}}, {{a, b, c} -> t}}, 2] = {{{a}, {a, b}}, {t}} - >> ReplaceList[expr, {}, -1] - : Non-negative integer or Infinity expected at position 3. - = ReplaceList[expr, {}, -1] Possible matches for a sum: >> ReplaceList[a + b + c, x_ + y_ -> {x, y}] @@ -369,12 +371,17 @@ def eval( # default argument, when it is passed explictly, e.g. # ReplaceList[expr, {}, Infinity], then Infinity # comes in as DirectedInfinity[1]. - if maxidx == SymbolInfinity: + if maxidx == SymbolInfinity or ExpressionInfinity == maxidx: max_count = None else: max_count = maxidx.get_int_value() if max_count is None or max_count < 0: - evaluation.message("ReplaceList", "innf", 3) + evaluation.message( + "ReplaceList", + "innf", + Integer3, + Expression(SymbolReplaceList, expr, rules, maxidx), + ) return None try: rules, ret = create_rules( diff --git a/mathics/builtin/scipy_utils/optimizers.py b/mathics/builtin/scipy_utils/optimizers.py index a704b9596..4d705255e 100644 --- a/mathics/builtin/scipy_utils/optimizers.py +++ b/mathics/builtin/scipy_utils/optimizers.py @@ -6,8 +6,8 @@ from mathics.core.convert.function import expression_to_callable_and_args from mathics.core.element import BaseElement from mathics.core.evaluation import Evaluation -from mathics.core.expression import Expression -from mathics.core.systemsymbols import SymbolAutomatic, SymbolFailed, SymbolInfinity +from mathics.core.expression import Expression, ExpressionInfinity +from mathics.core.systemsymbols import SymbolAutomatic, SymbolFailed from mathics.eval.nevaluator import eval_N if not check_requires_list(["scipy", "numpy"]): @@ -30,7 +30,7 @@ def get_tolerance_and_maxit(opts: dict, scale: float, evaluation: Evaluation): acc_goal = eval_N(acc_goal, evaluation) if acc_goal is SymbolAutomatic: acc_goal = Real(12.0) - elif acc_goal is SymbolInfinity: + elif ExpressionInfinity == acc_goal: acc_goal = None elif not isinstance(acc_goal, Number): acc_goal = None @@ -40,7 +40,7 @@ def get_tolerance_and_maxit(opts: dict, scale: float, evaluation: Evaluation): prec_goal = eval_N(prec_goal, evaluation) if prec_goal is SymbolAutomatic: prec_goal = Real(12.0) - elif prec_goal is SymbolInfinity: + elif ExpressionInfinity == prec_goal: prec_goal = None elif not isinstance(prec_goal, Number): prec_goal = None diff --git a/mathics/builtin/string/operations.py b/mathics/builtin/string/operations.py index 99aeff2ca..ac294c3fd 100644 --- a/mathics/builtin/string/operations.py +++ b/mathics/builtin/string/operations.py @@ -13,7 +13,7 @@ mathics_split, to_regex, ) -from mathics.core.atoms import Integer, Integer1, String +from mathics.core.atoms import Integer, Integer1, Integer3, String from mathics.core.attributes import ( A_FLAT, A_LISTABLE, @@ -437,7 +437,7 @@ def eval_n(self, string, patt, n, evaluation: Evaluation, options: dict): else: py_n = n.get_int_value() if py_n is None or py_n < 0: - evaluation.message("StringPosition", "innf", expr, Integer(3)) + evaluation.message("StringPosition", "innf", expr, Integer3) return # check options diff --git a/mathics/core/atoms.py b/mathics/core/atoms.py index ad98756bc..2ee59657b 100644 --- a/mathics/core/atoms.py +++ b/mathics/core/atoms.py @@ -329,6 +329,7 @@ def is_zero(self) -> bool: Integer1 = Integer(1) Integer2 = Integer(2) Integer3 = Integer(3) +Integer4 = Integer(4) Integer310 = Integer(310) Integer10 = Integer(10) IntegerM1 = Integer(-1) diff --git a/mathics/core/expression.py b/mathics/core/expression.py index 2910491f5..c1560db95 100644 --- a/mathics/core/expression.py +++ b/mathics/core/expression.py @@ -19,7 +19,7 @@ import sympy -from mathics.core.atoms import Integer, String +from mathics.core.atoms import Integer, Integer1, String from mathics.core.attributes import ( A_FLAT, A_HOLD_ALL, @@ -2068,3 +2068,6 @@ def convert_expression_elements( def string_list(head, elements, evaluation): return atom_list_constructor(evaluation, head, "String")(elements) + + +ExpressionInfinity = Expression(SymbolDirectedInfinity, Integer1) diff --git a/mathics/core/systemsymbols.py b/mathics/core/systemsymbols.py index 892763bd0..1798a9bb8 100644 --- a/mathics/core/systemsymbols.py +++ b/mathics/core/systemsymbols.py @@ -216,6 +216,7 @@ SymbolRealSign = Symbol("System`RealSign") SymbolRepeated = Symbol("System`Repeated") SymbolRepeatedNull = Symbol("System`RepeatedNull") +SymbolReplaceList = Symbol("System`ReplaceList") SymbolReturn = Symbol("System`Return") SymbolReverse = Symbol("System`Reverse") SymbolRight = Symbol("System`Right") @@ -225,6 +226,7 @@ SymbolRule = Symbol("System`Rule") SymbolRuleDelayed = Symbol("System`RuleDelayed") SymbolSameQ = Symbol("System`SameQ") +SymbolSelect = Symbol("System`Select") SymbolSequence = Symbol("System`Sequence") SymbolSeries = Symbol("System`Series") SymbolSeriesData = Symbol("System`SeriesData") diff --git a/mathics/eval/numbers/calculus/optimizers.py b/mathics/eval/numbers/calculus/optimizers.py index 4798f1362..01178af5e 100644 --- a/mathics/eval/numbers/calculus/optimizers.py +++ b/mathics/eval/numbers/calculus/optimizers.py @@ -19,7 +19,7 @@ ) from mathics.core.convert.python import from_python from mathics.core.evaluation import Evaluation -from mathics.core.expression import Expression +from mathics.core.expression import Expression, ExpressionInfinity from mathics.core.symbols import BaseElement, SymbolPlus, SymbolTimes, SymbolTrue from mathics.core.systemsymbols import ( SymbolAutomatic, @@ -431,7 +431,7 @@ def to_real_or_none(value) -> Optional[Real]: value = eval_N(value, evaluation) if value is SymbolAutomatic: value = Real(12.0) - elif value is SymbolInfinity: + elif value is SymbolInfinity or ExpressionInfinity == value: value = None elif not isinstance(value, Number): value = None @@ -442,7 +442,7 @@ def to_integer_or_none(value) -> Optional[Integer]: value = eval_N(value, evaluation) if value is SymbolAutomatic: value = Integer(100) - elif value is SymbolInfinity: + elif value is SymbolInfinity or ExpressionInfinity == value: value = None elif not isinstance(value, Number): value = None diff --git a/test/builtin/list/test_eol.py b/test/builtin/list/test_eol.py index 869bc3bcc..8ae460746 100644 --- a/test/builtin/list/test_eol.py +++ b/test/builtin/list/test_eol.py @@ -226,6 +226,7 @@ ## Formatting ("a ;; b ;; c", None, "a ;; b ;; c", None), ("a ;; b", None, "a ;; b", None), + # TODO: Rework this test ("{a ;; b ;; c ;; d}", None, "{a ;; b ;; c, 1 ;; d}", ";; association"), ( "Select[a, True]", @@ -233,7 +234,6 @@ "Select[a, True]", None, ), - # TODO: Rework this test ("Take[Range[10], {8, 2, -1}]", None, "{8, 7, 6, 5, 4, 3, 2}", None), ("Take[Range[10], {-3, -7, -2}]", None, "{8, 6, 4}", None), (