Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions mathics/core/pattern/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,9 @@ def match_yield(new_vars, _):
new_parms["vars_dict"] = new_vars
new_parms["element_index"] = parms["element_index"] + 1
new_parms["yield_func"] = element_yield
# Only the first element may leave elements unmatched before it (a
# partial match of a Flat expression); the next one starts right after.
new_parms["first"] = False
Comment thread
mmatera marked this conversation as resolved.
Resolved
del new_parms["element"]
pattern.match_element(
element=parms["next_element"], pattern_context=new_parms
Expand Down
6 changes: 5 additions & 1 deletion mathics/core/pattern/ordered.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,13 @@ def yield_choice(pre_vars):
#
# without "leading_blanks", Range[5000] will be tested against {a__, b_} in a
# call to get_match_candidates_count(), which is slow.
#
# With Flat, a pattern element whose match count is (1, 1) can still take
# several elements (x_ in f[x_, z] matches f[x, y] in f[x, y, z]), so the
# elements can not be paired one by one there either.

unmatched_elements = expression.elements
leading_blanks = not A_ORDERLESS & attributes
leading_blanks = not (A_ORDERLESS | A_FLAT) & attributes

for element in self.elements:
match_count = element.get_match_count()
Expand Down
43 changes: 43 additions & 0 deletions test/builtin/test_patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,49 @@ def test_dispatch(str_expr, msgs, str_expected, fail_msg):
)


@pytest.mark.parametrize(
("str_expr", "str_expected", "fail_msg"),
[
("ClearAll[f, g, o]; SetAttributes[f, Flat];", "Null", None),
(
"MatchQ[f[x, y, z], f[a_, z]]",
"True",
"A blank before a literal can take several elements",
),
("Replace[f[x, y, z], f[a_, z] :> {a}]", "{f[x, y]}", None),
(
"Replace[f[x, y, z], f[a_, z[_]] :> {a}]",
"Replace[f[x, y, z], f[a_, z[_]] :> {a}]",
None,
),
("Replace[f[x, y, z[1]], f[a_, z[_]] :> {a}]", "{f[x, y]}", None),
("f[x, y, z] /. f[a__, z] :> {a}", "{x, y}", "No element is lost"),
("f[x, y, z] /. f[a___, y, b___] :> {{a}, {b}}", "{{x}, {z}}", None),
("f[a, b, c, d, e] /. f[b, c, d] -> x", "f[a, x, e]", "WMA docs for Flat"),
("f[a, b, c, d, e] /. f[b, c, d] -> f[x, y]", "f[a, x, y, e]", None),
("f[a, b, c, d, e] /. f[b] -> x", "f[a, x, c, d, e]", None),
("f[a, b, c, d, e] /. f[d, b] -> x", "f[a, b, c, d, e]", "Order matters"),
("f[w, x, y, z] /. f[x, z] -> q", "f[w, x, y, z]", "Not contiguous"),
(
"SetAttributes[o, {Flat, OneIdentity}]; o[a_, 0] := 0; o[x, y, 0]",
"0",
None,
),
("SetAttributes[g, Flat]; g[a_, 0] := h[a]; g[x, y, 0]", "h[g[x, y]]", None),
(
"ClearAll[g]; SetAttributes[g, Flat]; g[l__, SameTest -> s_] := {{l}, s};"
" g[{1, -1}, {2, 3}, SameTest -> t]",
"{{{1, -1}, {2, 3}}, t}",
"Issue #1381",
),
("ClearAll[f, g, o];", "Null", None),
],
)
def test_flat_patterns(str_expr, str_expected, fail_msg):
"""Pattern matching against expressions whose head is Flat"""
check_evaluation(str_expr, str_expected, failure_message=fail_msg)


@pytest.mark.parametrize(
("str_expr", "msgs", "str_expected", "fail_msg"),
[
Expand Down
Loading