From f51844cbe53a8ab5d21cb3d5b39effe823b64c9a Mon Sep 17 00:00:00 2001 From: Stanislav Date: Fri, 16 Oct 2026 01:23:40 +0400 Subject: [PATCH] Fix TypeError sorting Orderless patterns that mix atoms and literal expressions Since #1987 a literal ExpressionPattern sorts by (MAGIC_ATOM_SORT_KEY, element_order), while atoms use (MAGIC_ATOM_SORT_KEY, 0, 0). Both share the magic number, so sorting the elements of an Orderless pattern such as 2 x[3] compares 0 with a tuple: MatchQ[2 x[3], 2 x[3]] raises TypeError. Co-Authored-By: Claude Opus 5.5 --- mathics/core/pattern/base.py | 9 ++++++++- test/builtin/test_patterns.py | 3 +++ test/builtin/test_testing_expressions.py | 4 ++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/mathics/core/pattern/base.py b/mathics/core/pattern/base.py index 16b6f96c41..1a86181134 100644 --- a/mathics/core/pattern/base.py +++ b/mathics/core/pattern/base.py @@ -480,7 +480,14 @@ def __init__( def _build_pattern_sort_key(self) -> tuple: if self.isliteral: - return (BASIC_LITERAL_EXPRESSION_PATTERN_SORT_KEY, self.expr.element_order) + # Keep the shape of BASIC_ATOM_PATTERN_SORT_KEY (magic, int, ...): + # atoms share the magic number, so the second slot must be an int + # here too, or sorting an Orderless pattern mixing both fails. + return ( + BASIC_LITERAL_EXPRESSION_PATTERN_SORT_KEY, + 1, + self.expr.element_order, + ) return ( BASIC_EXPRESSION_PATTERN_SORT_KEY, self.head.pattern_precedence, diff --git a/test/builtin/test_patterns.py b/test/builtin/test_patterns.py index 6f1ccbe288..e93a2dc4d4 100644 --- a/test/builtin/test_patterns.py +++ b/test/builtin/test_patterns.py @@ -168,6 +168,9 @@ def test_dispatch(str_expr, msgs, str_expected, fail_msg): ("f[10]", None, "100", None), ("f[10, Power -> 3]", None, "1000", None), ("Clear[f]", None, None, None), + # Orderless literal pattern mixing an atom and an expression + ("f[2 x[3]] = 1; f[2 x[3]]", None, "1", None), + ("Clear[f]", None, None, None), ], ) def test_pattern(str_expr, msgs, str_expected, fail_msg): diff --git a/test/builtin/test_testing_expressions.py b/test/builtin/test_testing_expressions.py index 4dd16437f6..a2cfc03085 100644 --- a/test/builtin/test_testing_expressions.py +++ b/test/builtin/test_testing_expressions.py @@ -176,6 +176,10 @@ def test_numerical_properties(str_expr, msgs, str_expected, fail_msg): ("MatchQ[1, a_.*b_.]", None, "True", None), ("MatchQ[x, a_.*b_.]", None, "True", None), ("MatchQ[2*x, a_.*b_.]", None, "True", None), + # Orderless literal patterns mixing an atom and an expression + ("MatchQ[2 x[3], 2 x[3]]", None, "True", None), + ("MatchQ[x[3] + 2, 2 + x[3]]", None, "True", None), + ("MatchQ[2 x[3], 3 x[3]]", None, "False", None), ], ) def test_matchq(str_expr, msgs, str_expected, fail_msg):