diff --git a/mathics/builtin/__init__.py b/mathics/builtin/__init__.py
index f7e9b0a61..79e6e1897 100755
--- a/mathics/builtin/__init__.py
+++ b/mathics/builtin/__init__.py
@@ -20,6 +20,8 @@
PatternObject,
)
+from mathics.core.pattern import pattern_objects
+
from mathics.settings import ENABLE_FILES_MODULE
from mathics.version import __version__ # noqa used in loading to check consistency.
@@ -267,7 +269,6 @@ def sanity_check(cls, module):
mathics_to_python = {} # here we have: name -> string
sympy_to_mathics = {}
-pattern_objects = {}
builtins_precedence = {}
new_builtins = _builtins
diff --git a/mathics/builtin/assignments/clear.py b/mathics/builtin/assignments/clear.py
index 471eb5692..e46dd3ab3 100644
--- a/mathics/builtin/assignments/clear.py
+++ b/mathics/builtin/assignments/clear.py
@@ -22,14 +22,20 @@
Atom,
Symbol,
SymbolNull,
- system_symbols,
+ symbol_set,
)
from mathics.core.systemsymbols import (
SymbolContext,
SymbolContextPath,
+ SymbolDownValues,
SymbolFailed,
+ SymbolMessages,
+ SymbolNValues,
SymbolOptions,
+ SymbolOwnValues,
+ SymbolSubValues,
+ SymbolUpValues,
)
from mathics.core.atoms import String
@@ -320,12 +326,12 @@ def apply(self, expr, evaluation):
return SymbolNull
-SYSTEM_SYMBOL_VALUES = system_symbols(
- "OwnValues",
- "DownValues",
- "SubValues",
- "UpValues",
- "NValues",
- "Options",
- "Messages",
+SYSTEM_SYMBOL_VALUES = symbol_set(
+ SymbolDownValues,
+ SymbolMessages,
+ SymbolNValues,
+ SymbolOptions,
+ SymbolOwnValues,
+ SymbolSubValues,
+ SymbolUpValues,
)
diff --git a/mathics/builtin/assignments/internals.py b/mathics/builtin/assignments/internals.py
index 9cd6e25fb..64588d72e 100644
--- a/mathics/builtin/assignments/internals.py
+++ b/mathics/builtin/assignments/internals.py
@@ -720,12 +720,7 @@ def process_rhs_conditions(lhs, rhs, condition, evaluation):
def process_tags_and_upset_dont_allow_custom(tags, upset, self, lhs, focus, evaluation):
- # TODO: the following provides a hacky fix for 1259. I know @rocky loves
- # this kind of things, but otherwise we need to work on rebuild the pattern
- # matching mechanism...
- flag_ioi, evaluation.ignore_oneidentity = evaluation.ignore_oneidentity, True
focus = focus.evaluate_elements(evaluation)
- evaluation.ignore_oneidentity = flag_ioi
name = lhs.get_head_name()
if tags is None and not upset:
name = focus.get_lookup_name()
@@ -745,14 +740,9 @@ def process_tags_and_upset_dont_allow_custom(tags, upset, self, lhs, focus, eval
def process_tags_and_upset_allow_custom(tags, upset, self, lhs, evaluation):
- # TODO: the following provides a hacky fix for 1259. I know @rocky loves
- # this kind of things, but otherwise we need to work on rebuild the pattern
- # matching mechanism...
name = lhs.get_head_name()
focus = lhs
- flag_ioi, evaluation.ignore_oneidentity = evaluation.ignore_oneidentity, True
focus = focus.evaluate_elements(evaluation)
- evaluation.ignore_oneidentity = flag_ioi
if tags is None and not upset:
name = focus.get_lookup_name()
if not name:
diff --git a/mathics/builtin/attributes.py b/mathics/builtin/attributes.py
index eea6b3006..05a72523a 100644
--- a/mathics/builtin/attributes.py
+++ b/mathics/builtin/attributes.py
@@ -460,15 +460,24 @@ class OneIdentity(Predefined):
- 'OneIdentity'
-
- is an attribute specifying that '$f$[$x$]' should be treated \
- as equivalent to $x$ in pattern matching.
+
- is an attribute assigned to a symbol, say $f$, indicating that '$f$[$x$]', $f$[$f$[$x$]], etc. are all \
+ equivalent to $x$ in pattern matching.
- 'OneIdentity' affects pattern matching:
+ >> a /. f[x_:0, u_] -> {u}
+ = a
+
+ Here is how 'OneIdentity' changes the pattern matched above :
+
>> SetAttributes[f, OneIdentity]
- >> a /. f[args___] -> {args}
+ >> a /. f[x_:0, u_] -> {u}
= {a}
- It does not affect evaluation:
+
+ However, without a default argument, the pattern does not match:
+ >> a /. f[u_] -> {u}
+ = a
+
+ 'OneIdentity' does not change evaluation:
>> f[a]
= f[a]
"""
diff --git a/mathics/builtin/files_io/files.py b/mathics/builtin/files_io/files.py
index eba2bd3a1..e801649bb 100644
--- a/mathics/builtin/files_io/files.py
+++ b/mathics/builtin/files_io/files.py
@@ -296,7 +296,7 @@ class FilePrint(Builtin):
}
def apply(self, path, evaluation, options):
- "FilePrint[path_ OptionsPattern[FilePrint]]"
+ "FilePrint[path_, OptionsPattern[FilePrint]]"
pypath = path.to_python()
if not (
isinstance(pypath, str)
diff --git a/mathics/builtin/graphics.py b/mathics/builtin/graphics.py
index 9651cefb7..de1e3f3a2 100644
--- a/mathics/builtin/graphics.py
+++ b/mathics/builtin/graphics.py
@@ -47,13 +47,16 @@
from mathics.core.list import ListExpression
from mathics.core.symbols import (
Symbol,
- system_symbols,
+ symbol_set,
system_symbols_dict,
SymbolList,
SymbolNull,
)
from mathics.core.systemsymbols import (
+ SymbolEdgeForm,
+ SymbolFaceForm,
SymbolMakeBoxes,
+ SymbolRule,
)
from mathics.core.formatter import lookup_method
@@ -61,9 +64,6 @@
from mathics.core.attributes import A_PROTECTED, A_READ_PROTECTED
-SymbolEdgeForm = Symbol("System`EdgeForm")
-SymbolFaceForm = Symbol("System`FaceForm")
-
GRAPHICS_OPTIONS = {
"AspectRatio": "Automatic",
"Axes": "False",
@@ -1427,26 +1427,26 @@ class Tiny(Builtin):
element_heads = frozenset(
- system_symbols(
- "Arrow",
- "BezierCurve",
- "Circle",
- "Cone",
- "Cuboid",
- "Cylinder",
- "Disk",
- "FilledCurve",
- "Inset",
- "Line",
- "Point",
- "Polygon",
- "Rectangle",
- "RegularPolygon",
- "Sphere",
- "Style",
- "Text",
- "Tube",
- "UniformPolyhedron",
+ symbol_set(
+ Symbol("System`Arrow"),
+ Symbol("System`BezierCurve"),
+ Symbol("System`Circle"),
+ Symbol("System`Cone"),
+ Symbol("System`Cuboid"),
+ Symbol("System`Cylinder"),
+ Symbol("System`Disk"),
+ Symbol("System`FilledCurve"),
+ Symbol("System`Inset"),
+ Symbol("System`Line"),
+ Symbol("System`Point"),
+ Symbol("System`Polygon"),
+ Symbol("System`Rectangle"),
+ Symbol("System`RegularPolygon"),
+ Symbol("System`Sphere"),
+ Symbol("System`Style"),
+ Symbol("System`Text"),
+ Symbol("System`Tube"),
+ Symbol("System`UniformPolyhedron"),
)
)
@@ -1477,7 +1477,7 @@ class Tiny(Builtin):
style_heads = frozenset(styles.keys())
style_and_form_heads = frozenset(
- style_heads.union(system_symbols("System`EdgeForm", "System`FaceForm"))
+ style_heads.union(symbol_set(SymbolEdgeForm, SymbolFaceForm))
)
GLOBALS.update(
@@ -1497,8 +1497,8 @@ class Tiny(Builtin):
GLOBALS.update(styles)
GRAPHICS_SYMBOLS = {
- Symbol("System`List"),
- Symbol("System`Rule"),
+ SymbolList,
+ SymbolRule,
Symbol("System`VertexColors"),
*element_heads,
*[Symbol(element.name + "Box") for element in element_heads],
diff --git a/mathics/builtin/options.py b/mathics/builtin/options.py
index e9d0371e5..9df8bee11 100644
--- a/mathics/builtin/options.py
+++ b/mathics/builtin/options.py
@@ -79,11 +79,11 @@ def eval(self, f, i, evaluation):
i = [index.get_int_value() for index in i]
for index in i:
if index is None or index < 1:
- evaluation.message(SymbolDefault, "intp")
+ evaluation.message(SymbolDefault.name, "intp")
return
name = f.get_name()
if not name:
- evaluation.message(SymbolDefault, "sym", f, 1)
+ evaluation.message(SymbolDefault.name, "sym", f, 1)
return
result = get_default_value(name, evaluation, *i)
return result
diff --git a/mathics/core/atoms.py b/mathics/core/atoms.py
index 9134b16d9..57e8661c5 100644
--- a/mathics/core/atoms.py
+++ b/mathics/core/atoms.py
@@ -24,9 +24,9 @@
Symbol,
SymbolNull,
SymbolTrue,
- system_symbols,
+ symbol_set,
)
-from mathics.core.systemsymbols import SymbolInfinity
+from mathics.core.systemsymbols import SymbolInfinity, SymbolInputForm, SymbolFullForm
# Imperical number that seems to work.
# We have to be able to match mpmath values with sympy values
@@ -35,7 +35,7 @@
SymbolI = Symbol("I")
SymbolString = Symbol("String")
-SYSTEM_SYMBOLS_INPUT_OR_FULL_FORM = system_symbols("InputForm", "FullForm")
+SYSTEM_SYMBOLS_INPUT_OR_FULL_FORM = symbol_set(SymbolInputForm, SymbolFullForm)
class Number(Atom, ImmutableValueMixin, NumericOperators):
diff --git a/mathics/core/evaluation.py b/mathics/core/evaluation.py
index 3c1dfa174..3ac098309 100644
--- a/mathics/core/evaluation.py
+++ b/mathics/core/evaluation.py
@@ -262,9 +262,6 @@ def __init__(
# status of last evaluate
self.exc_result = self.SymbolNull
self.last_eval = None
- # Necesary to handle OneIdentity on
- # lhs in assignment
- self.ignore_oneidentity = False
# Used in ``mathics.builtin.numbers.constants.get_constant`` and
# ``mathics.builtin.numeric.N``.
self._preferred_n_method = []
diff --git a/mathics/core/evaluators.py b/mathics/core/evaluators.py
index 1485e121e..8f0dc1762 100644
--- a/mathics/core/evaluators.py
+++ b/mathics/core/evaluators.py
@@ -21,10 +21,11 @@
)
from mathics.core.convert.sympy import from_sympy
from mathics.core.definitions import PyMathicsLoadException
+from mathics.core.element import BaseElement
from mathics.core.evaluation import Evaluation
from mathics.core.expression import Expression
from mathics.core.number import PrecisionValueError, get_precision
-from mathics.core.symbols import Atom, BaseElement
+from mathics.core.symbols import Atom
from mathics.core.systemsymbols import SymbolMachinePrecision, SymbolN
diff --git a/mathics/core/expression.py b/mathics/core/expression.py
index 04025e0ef..5cb02211f 100644
--- a/mathics/core/expression.py
+++ b/mathics/core/expression.py
@@ -37,11 +37,14 @@
Monomial,
NumericOperators,
Symbol,
+ SymbolAbs,
+ SymbolDivide,
SymbolList,
SymbolN,
+ SymbolPlus,
SymbolTimes,
SymbolTrue,
- system_symbols,
+ symbol_set,
)
from mathics.core.systemsymbols import (
SymbolAborted,
@@ -50,9 +53,14 @@
SymbolCondition,
SymbolDirectedInfinity,
SymbolFunction,
+ SymbolMinus,
SymbolPattern,
+ SymbolPower,
SymbolSequence,
+ SymbolSin,
SymbolSlot,
+ SymbolSqrt,
+ SymbolSubtract,
SymbolUnevaluated,
)
@@ -70,16 +78,16 @@
SymbolVerbatim = Symbol("Verbatim")
-symbols_arithmetic_operations = system_symbols(
- "Sqrt",
- "Times",
- "Plus",
- "Subtract",
- "Minus",
- "Power",
- "Abs",
- "Divide",
- "Sin",
+symbols_arithmetic_operations = symbol_set(
+ SymbolAbs,
+ SymbolDivide,
+ SymbolMinus,
+ SymbolPlus,
+ SymbolPower,
+ SymbolSin,
+ SymbolSqrt,
+ SymbolSubtract,
+ SymbolTimes,
)
diff --git a/mathics/core/list.py b/mathics/core/list.py
index 4dd21d6d1..bd5818681 100644
--- a/mathics/core/list.py
+++ b/mathics/core/list.py
@@ -41,7 +41,7 @@ def __init__(
# call_frame = inspect.getouterframes(curframe, 2)
# print("caller name:", call_frame[1][3])
- # from mathics.core.symbols import BaseElement
+ # from mathics.core.element import BaseElement
# for element in elements:
# if not isinstance(element, BaseElement):
# from trepan.api import debug; debug()
diff --git a/mathics/core/pattern.py b/mathics/core/pattern.py
index 39dc1511e..b0a398d58 100644
--- a/mathics/core/pattern.py
+++ b/mathics/core/pattern.py
@@ -2,52 +2,58 @@
# cython: profile=False
# -*- coding: utf-8 -*-
-
-from mathics.core.element import ensure_context
-from mathics.core.expression import Expression
-from mathics.core.symbols import Atom, Symbol, system_symbols
-from mathics.core.systemsymbols import SymbolSequence
+from typing import Optional
+
+from mathics.core.atoms import Integer
+from mathics.core.element import BaseElement, ensure_context
+from mathics.core.evaluation import Evaluation
+from mathics.core.expression import Expression, SymbolDefault
+from mathics.core.symbols import Atom, Symbol, symbol_set
+from mathics.core.systemsymbols import (
+ SymbolAlternatives,
+ SymbolBlank,
+ SymbolBlankNullSequence,
+ SymbolBlankSequence,
+ SymbolCondition,
+ SymbolOptional,
+ SymbolOptionsPattern,
+ SymbolPattern,
+ SymbolPatternTest,
+ SymbolRepeated,
+ SymbolRepeatedNull,
+ SymbolSequence,
+)
from mathics.core.util import subsets, subranges, permutations
from itertools import chain
from mathics.core.attributes import A_FLAT, A_ONE_IDENTITY, A_ORDERLESS
-# from mathics.core.pattern_nocython import (
-# StopGenerator #, Pattern #, ExpressionPattern)
-# from mathics.core import pattern_nocython
-
-
-SYSTEM_SYMBOLS_PATTERNS = system_symbols(
- "Pattern",
- "PatternTest",
- "Condition",
- "Optional",
- "Blank",
- "BlankSequence",
- "BlankNullSequence",
- "Alternatives",
- "OptionsPattern",
- "Repeated",
- "RepeatedNull",
+# FIXME: create definitions in systemsymbols for missing items below.
+SYSTEM_SYMBOLS_PATTERNS = symbol_set(
+ SymbolAlternatives,
+ SymbolBlank,
+ SymbolBlankNullSequence,
+ SymbolBlankSequence,
+ SymbolCondition,
+ SymbolOptional,
+ SymbolOptionsPattern,
+ SymbolPattern,
+ SymbolPatternTest,
+ SymbolRepeated,
+ SymbolRepeatedNull,
)
-
-def Pattern_create(expr):
- from mathics.builtin import pattern_objects
-
- # from mathics.core.pattern import AtomPattern, ExpressionPattern
-
- name = expr.get_head_name()
- pattern_object = pattern_objects.get(name)
- if pattern_object is not None:
- return pattern_object(expr)
- if isinstance(expr, Atom):
- return AtomPattern(expr)
- else:
- return ExpressionPattern(expr)
+pattern_objects = {}
class StopGenerator(Exception):
+ """
+ StopGenerator is the exception raised when
+ an expression matches a pattern.
+ The exception holds the attribute `value`
+ that is used as a return value in `match`.
+ """
+
def __init__(self, value=None):
self.value = value
@@ -70,7 +76,22 @@ class Pattern:
When the pattern matches, the symbol is bound to the parameter ``x``.
"""
- create = staticmethod(Pattern_create)
+ @staticmethod
+ def create(expr: BaseElement) -> "Pattern":
+ """
+ If ``expr`` is listed in ``pattern_object`` return the pattern found there.
+ Otherwise, if ``expr`` is an ``Atom``, create and return ``AtomPattern`` for ``expr``.
+ Otherwise, create and return and ``ExpressionPattern`` for ``expr``.
+ """
+
+ name = expr.get_head_name()
+ pattern_object = pattern_objects.get(name)
+ if pattern_object is not None:
+ return pattern_object(expr)
+ if isinstance(expr, Atom):
+ return AtomPattern(expr)
+ else:
+ return ExpressionPattern(expr)
def match(
self,
@@ -82,22 +103,29 @@ def match(
element_index=None,
element_count=None,
fully=True,
- wrap_oneid=True,
):
+ """
+ Check if the expression matches the pattern (self).
+ If it does, calls `yield_func`.
+ vars collects subexpressions associated to subpatterns.
+ head ?
+ element_index ?
+ element_count ?
+ fully is used in match_elements, for the case of Orderless patterns.
+ """
raise NotImplementedError
- """def match(self, expression, vars, evaluation,
- head=None, element_index=None, element_count=None,
- fully=True, wrap_oneid=True):
- #raise NotImplementedError
- result = []
- def yield_func(vars, rest):
- result.append(vars, rest)
- self._match(yield_func, expression, vars, evaluation, head,
- element_index, element_count, fully, wrap_oneid)
- return result"""
+ def does_match(
+ self,
+ expression: BaseElement,
+ evaluation: Evaluation,
+ vars=Optional[dict],
+ fully: bool = True,
+ ) -> bool:
- def does_match(self, expression, evaluation, vars=None, fully=True):
+ """
+ returns True if `expression` matches self.
+ """
if vars is None:
vars = {}
@@ -187,7 +215,6 @@ def match_symbol(
element_index=None,
element_count=None,
fully=True,
- wrap_oneid=True,
):
if expression is self.atom:
yield_func(vars, None)
@@ -207,7 +234,6 @@ def match(
element_index=None,
element_count=None,
fully=True,
- wrap_oneid=True,
):
if isinstance(expression, Atom) and expression.sameQ(self.atom):
# yield vars, None
@@ -244,7 +270,6 @@ def match(
element_index=None,
element_count=None,
fully=True,
- wrap_oneid=True,
):
evaluation.check_stopped()
attributes = self.head.get_attributes(evaluation.definitions)
@@ -311,8 +336,7 @@ def yield_choice(pre_vars):
# for new_vars, rest in self.match_element( # nopep8
# self.elements[0], self.elements[1:], ([], expression.elements),
# pre_vars, expression, attributes, evaluation, first=True,
- # fully=fully, element_count=len(self.elements),
- # wrap_oneid=expression.get_head_name() != 'System`MakeBoxes'):
+ # fully=fully, element_count=len(self.elements)):
# def yield_element(new_vars, rest):
# yield_func(new_vars, rest)
self.match_element(
@@ -327,7 +351,6 @@ def yield_choice(pre_vars):
first=True,
fully=fully,
element_count=len(self.elements),
- wrap_oneid=expression.get_head_name() != "System`MakeBoxes",
)
# for head_vars, _ in self.head.match(expression.get_head(), vars,
@@ -351,51 +374,88 @@ def yield_head(head_vars, _):
self.head.match(yield_head, expression.get_head(), vars, evaluation)
except StopGenerator_ExpressionPattern_match:
return
- if (
- wrap_oneid
- and not evaluation.ignore_oneidentity
- and A_ONE_IDENTITY & attributes
- and not self.head.expr.sameQ(expression.get_head()) # nopep8
- and not self.head.expr.sameQ(expression)
- ):
- # and not OneIdentity &
- # (expression.get_attributes(evaluation.definitions) |
- # expression.get_head().get_attributes(evaluation.definitions)):
- new_expression = Expression(self.head.expr, expression)
- for element in self.elements:
- element.match_count = element.get_match_count()
- element.candidates = [expression]
- # element.get_match_candidates(
- # new_expression.elements, new_expression, attributes,
- # evaluation, vars)
- if len(element.candidates) < element.match_count[0]:
- return
- # for new_vars, rest in self.match_element(
- # self.elements[0], self.elements[1:],
- # ([], [expression]), vars, new_expression, attributes,
- # evaluation, first=True, fully=fully,
- # element_count=len(self.elements), wrap_oneid=True):
- # def yield_element(new_vars, rest):
- # yield_func(new_vars, rest)
- self.match_element(
+
+ if A_ONE_IDENTITY & attributes:
+ # This is all about the pattern. We do this
+ # each time because at some point we should need
+ # to check the default values each time...
+
+ # This tries to reduce the pattern to a non empty
+ # set of default values, and a single pattern.
+ default_indx = 0
+ optionals = {}
+ new_pattern = None
+ pattern_head = self.head.expr
+ for pat_elem in self.elements:
+ default_indx += 1
+ if isinstance(pat_elem, AtomPattern):
+ if new_pattern is not None:
+ return
+ new_pattern = pat_elem
+ # TODO: check into account the second argument,
+ # and if there is a default value...
+ elif pat_elem.get_head_name() == "System`Optional":
+ if len(pat_elem.elements) == 2:
+ pat, value = pat_elem.elements
+ if pat.get_head_name() == "System`Pattern":
+ key = pat.elements[0].atom.name
+ else:
+ # if the first element of the Optional
+ # is not a `Pattern`, then we need to
+ # store an empty element.
+ key = ""
+ optionals[key] = value
+ elif len(pat_elem.elements) == 1:
+ pat = pat_elem.elements[0]
+ if pat.get_head_name() == "System`Pattern":
+ key = pat.elements[0].atom.name
+ else:
+ key = ""
+ # Now, determine the default value
+ defaultvalue_expr = Expression(
+ SymbolDefault, pattern_head, Integer(default_indx)
+ )
+ value = defaultvalue_expr.evaluate(evaluation)
+ if value.sameQ(defaultvalue_expr):
+ return
+ optionals[key] = value
+ else:
+ return
+ else:
+ if new_pattern is not None:
+ return
+ new_pattern = pat_elem
+
+ # If there is not optional values in the pattern, then
+ # it can not match any expression as a OneIdentity pattern:
+ if len(optionals) == 0:
+ return
+
+ # Remove the empty key and load the default values in vars
+ if "" in optionals:
+ del optionals[""]
+ vars.update(optionals)
+ # Try to match the non-optional element with the expression
+ new_pattern.match(
yield_func,
- self.elements[0],
- self.elements[1:],
- ([], [expression]),
+ expression,
vars,
- new_expression,
- attributes,
evaluation,
- first=True,
+ head=head,
+ element_index=element_index,
+ element_count=element_count,
fully=fully,
- element_count=len(self.elements),
- wrap_oneid=True,
)
- def get_pre_choices(self, yield_func, expression, attributes, vars):
+ def get_pre_choices(self, yield_choice, expression, attributes, vars):
+ """
+ If not Orderless, call yield_choice with vars as the parameter.
+ """
if A_ORDERLESS & attributes:
self.sort()
patterns = self.filter_elements("Pattern")
+ # a dict with entries having patterns with the same name
+ # which are not in vars.
groups = {}
prev_pattern = prev_name = None
for pattern in patterns:
@@ -484,9 +544,9 @@ def yield_next(next):
# for setting in per_name(groups.items(), vars):
# def yield_name(setting):
# yield_func(setting)
- per_name(yield_func, list(groups.items()), vars)
+ per_name(yield_choice, list(groups.items()), vars)
else:
- yield_func(vars)
+ yield_choice(vars)
def __init__(self, expr):
self.head = Pattern.create(expr.head)
@@ -545,7 +605,6 @@ def match_element(
first=False,
fully=True,
depth=1,
- wrap_oneid=True,
):
if rest_expression is None:
@@ -626,6 +685,9 @@ def match_element(
*set_lengths
)
else:
+ # a generator that yields partitions of
+ # candidates as [before | block | after ]
+
sets = subranges(
candidates,
flexible_start=first and not fully,
@@ -633,7 +695,6 @@ def match_element(
less_first=less_first,
*set_lengths
)
-
if rest_elements:
next_element = rest_elements[0]
next_rest_elements = rest_elements[1:]
@@ -680,7 +741,6 @@ def match_yield(new_vars, _):
depth=next_depth,
element_index=next_index,
element_count=element_count,
- wrap_oneid=wrap_oneid,
)
else:
if not fully or (not items_rest[0] and not items_rest[1]):
@@ -696,7 +756,6 @@ def yield_wrapping(item):
head=expression.head,
element_index=element_index,
element_count=element_count,
- wrap_oneid=wrap_oneid,
)
self.get_wrappings(
diff --git a/mathics/core/symbols.py b/mathics/core/symbols.py
index 9ea745368..beb75ab7e 100644
--- a/mathics/core/symbols.py
+++ b/mathics/core/symbols.py
@@ -3,8 +3,7 @@
import sympy
import time
-import typing
-from typing import Any, Optional
+from typing import Any, FrozenSet, List, Optional, Tuple
from mathics.core.element import (
BaseElement,
@@ -251,7 +250,7 @@ def evaluate_elements(self, evaluation) -> "Atom":
def get_atom_name(self) -> str:
return self.__class__.__name__
- def get_atoms(self, include_heads=True) -> typing.List["Atom"]:
+ def get_atoms(self, include_heads=True) -> List["Atom"]:
return [self]
# We seem to need this because the caller doesn't distinguish something with elements
@@ -667,15 +666,14 @@ def is_uncertain_final_definitions(self, definitions) -> bool:
return False
-# system_symbols('A', 'B', ...) -> [Symbol('System`A'), Symbol('System`B'), ...]
-def system_symbols(*symbols) -> typing.FrozenSet[Symbol]:
+def symbol_set(*symbols: Tuple[Symbol]) -> FrozenSet[Symbol]:
"""
- Return a frozenset of symbols from a list of names (strings).
+ Return a frozenset of symbols from a Symbol arguments.
We will use this in testing membership, so an immutable object is fine.
In 2021, we benchmarked frozenset versus list, tuple, and set and frozenset was the fastest.
"""
- return frozenset(Symbol(s) for s in symbols)
+ return frozenset(symbols)
# Symbols used in this module.
diff --git a/mathics/core/systemsymbols.py b/mathics/core/systemsymbols.py
index b9d9c47fb..15088a264 100644
--- a/mathics/core/systemsymbols.py
+++ b/mathics/core/systemsymbols.py
@@ -35,6 +35,8 @@
SymbolAttributes = Symbol("System`Attributes")
SymbolAutomatic = Symbol("System`Automatic")
SymbolBlank = Symbol("System`Blank")
+SymbolBlankNullSequence = Symbol("System`BlankNullSequence")
+SymbolBlankSequence = Symbol("System`BlankSequence")
SymbolBlend = Symbol("System`Blend")
SymbolBreak = Symbol("System`Break")
SymbolByteArray = Symbol("System`ByteArray")
@@ -60,6 +62,7 @@
SymbolDirectedInfinity = Symbol("System`DirectedInfinity")
SymbolDispatch = Symbol("System`Dispatch")
SymbolDot = Symbol("System`Dot")
+SymbolDownValues = Symbol("System`DownValues")
SymbolE = Symbol("System`E")
SymbolEdgeForm = Symbol("System`EdgeForm")
SymbolEqual = Symbol("System`Equal")
@@ -67,6 +70,7 @@
SymbolExpandAll = Symbol("System`ExpandAll")
SymbolExport = Symbol("System`Export")
SymbolExportString = Symbol("System`ExportString")
+SymbolFaceForm = Symbol("System`FaceForm")
SymbolFactorial = Symbol("System`Factorial")
SymbolFailed = Symbol("System`$Failed")
SymbolFloor = Symbol("System`Floor")
@@ -112,11 +116,12 @@
SymbolMean = Symbol("System`Mean")
SymbolMemberQ = Symbol("System`MemberQ")
SymbolMessageName = Symbol("System`MessageName")
-SymbolMinus = Symbol("System`Minus")
+SymbolMessages = Symbol("System`Messages")
SymbolMinus = Symbol("System`Minus")
SymbolMissing = Symbol("System`Missing")
SymbolN = Symbol("System`N")
SymbolNIntegrate = Symbol("System`NIntegrate")
+SymbolNValues = Symbol("System`NValues")
SymbolNeeds = Symbol("System`Needs")
SymbolNone = Symbol("System`None")
SymbolNorm = Symbol("System`Norm")
@@ -126,13 +131,17 @@
SymbolNumericQ = Symbol("System`NumericQ")
SymbolO = Symbol("System`O")
SymbolOptionValue = Symbol("System`OptionValue")
+SymbolOptional = Symbol("System`Optional")
SymbolOptions = Symbol("System`Options")
+SymbolOptionsPattern = Symbol("System`OptionsPattern")
SymbolOr = Symbol("System`Or")
SymbolOut = Symbol("System`Out")
SymbolOutputForm = Symbol("System`OutputForm")
SymbolOverflow = Symbol("System`Overflow")
+SymbolOwnValues = Symbol("System`OwnValues")
SymbolPackages = Symbol("System`$Packages")
SymbolPattern = Symbol("System`Pattern")
+SymbolPatternTest = Symbol("System`PatternTest")
SymbolPower = Symbol("System`Power")
SymbolPi = Symbol("System`Pi")
SymbolPiecewise = Symbol("System`Piecewise")
@@ -146,6 +155,8 @@
SymbolRe = Symbol("System`Re")
SymbolReal = Symbol("System`Real")
SymbolRealDigits = Symbol("System`RealDigits")
+SymbolRepeated = Symbol("System`Repeated")
+SymbolRepeatedNull = Symbol("System`RepeatedNull")
SymbolReturn = Symbol("System`Return")
SymbolRound = Symbol("System`Round")
SymbolRow = Symbol("System`Row")
@@ -167,6 +178,7 @@
SymbolStringForm = Symbol("System`StringForm")
SymbolStringQ = Symbol("System`StringQ")
SymbolStyle = Symbol("System`Style")
+SymbolSubValues = Symbol("System`SubValues")
SymbolSubsetQ = Symbol("System`SubsetQ")
SymbolSubtract = Symbol("System`Subtract")
SymbolSubscriptBox = Symbol("System`SubscriptBox")
@@ -181,4 +193,5 @@
SymbolUndefined = Symbol("System`Undefined")
SymbolUnequal = Symbol("System`Unequal")
SymbolUnevaluated = Symbol("System`Unevaluated")
+SymbolUpValues = Symbol("System`UpValues")
SymbolXor = Symbol("System`Xor")
diff --git a/mathics/core/util.py b/mathics/core/util.py
index 0dc06064e..b1bfef55e 100644
--- a/mathics/core/util.py
+++ b/mathics/core/util.py
@@ -64,6 +64,12 @@ def decide(chosen, not_chosen, rest, count):
def subranges(
items, min_count, max, flexible_start=False, included=None, less_first=False
):
+ """
+ generator that yields possible divisions of items as
+ ([items_inside],([previos_items],[remaining_items]))
+ with items_inside of variable lengths.
+ If flexible_start, then [previos_items] also has a variable size.
+ """
# TODO: take into account included
if max is None:
diff --git a/test/test_rules_patterns.py b/test/builtin/atomic/test_symbols.py
similarity index 51%
rename from test/test_rules_patterns.py
rename to test/builtin/atomic/test_symbols.py
index b1c7ac476..bba780e0c 100644
--- a/test/test_rules_patterns.py
+++ b/test/builtin/atomic/test_symbols.py
@@ -1,5 +1,9 @@
# -*- coding: utf-8 -*-
-from .helper import check_evaluation
+"""
+Unit tests from mathics.builtin.atomic.symbols.
+"""
+
+from test.helper import check_evaluation
def test_downvalues():
@@ -21,25 +25,3 @@ def test_downvalues():
),
):
check_evaluation(str_expr, str_expected, message)
-
-
-def test_blank():
- for str_expr, str_expected, message in (
- (
- "g[i] /. _[i] :> a",
- "a",
- "Issue #203",
- ),
- ):
- check_evaluation(str_expr, str_expected, message)
-
-
-def test_complex_rule():
- for str_expr, str_expected, message in (
- (
- "a == d b + d c /. a_ x_ + a_ y_ -> a (x + y)",
- "a == (b + c) d",
- "Issue #212",
- ),
- ):
- check_evaluation(str_expr, str_expected, message)
diff --git a/test/builtin/test_attributes.py b/test/builtin/test_attributes.py
new file mode 100644
index 000000000..175deeaeb
--- /dev/null
+++ b/test/builtin/test_attributes.py
@@ -0,0 +1,206 @@
+# -*- coding: utf-8 -*-
+"""
+Unit tests from mathics.builtin.attributes.
+"""
+
+import os
+import pytest
+
+from test.helper import check_evaluation
+
+DEBUGRULESPAT = int(os.environ.get("DEBUGRULESPAT", "0")) == 1
+
+if DEBUGRULESPAT:
+ skip_or_fail = pytest.mark.xfail
+else:
+ skip_or_fail = pytest.mark.skip
+
+
+@pytest.mark.parametrize(
+ ("str_expr", "str_expected", "msg"),
+ [
+ (None, None, None),
+ # F has the attribute, but G doesn't.
+ ("SetAttributes[F, OneIdentity]", "Null", None),
+ ("SetAttributes[r, Flat]", "Null", None),
+ ("SetAttributes[s, Flat]", "Null", None),
+ ("SetAttributes[s, OneIdentity]", "Null", None),
+ ("MatchQ[x, F[y_]]", "False", "With OneIdentity"),
+ ("MatchQ[x, G[y_]]", "False", "Without OneIdentity"),
+ ("MatchQ[x, F[x_:0,y_]]", "True", "With OneIdentity, and Default"),
+ ("MatchQ[x, G[x_:0,y_]]", "False", "Without OneIdentity, and Default"),
+ ("MatchQ[F[x], F[x_:0,y_]]", "True", "With OneIdentity, and Default"),
+ ("MatchQ[G[x], G[x_:0,y_]]", "True", "Without OneIdentity, and Default"),
+ ("MatchQ[F[F[F[x]]], F[x_:0,y_]]", "True", "With OneIdentity, nested"),
+ ("MatchQ[G[G[G[x]]], G[x_:0,y_]]", "True", "Without OneIdentity, nested"),
+ ("MatchQ[F[3, F[F[x]]], F[x_:0,y_]]", "True", "With OneIdentity, nested"),
+ ("MatchQ[G[3, G[G[x]]], G[x_:0,y_]]", "True", "Without OneIdentity, nested"),
+ (
+ "MatchQ[x, F[x1_:0, F[x2_:0,y_]]]",
+ "True",
+ "With OneIdentity, pattern nested",
+ ),
+ (
+ "MatchQ[x, G[x1_:0, G[x2_:0,y_]]]",
+ "False",
+ "With OneIdentity, pattern nested",
+ ),
+ (
+ "MatchQ[x, F[x1___:0, F[x2_:0,y_]]]",
+ "True",
+ "With OneIdentity, pattern nested",
+ ),
+ (
+ "MatchQ[x, G[x1___:0, G[x2_:0,y_]]]",
+ "False",
+ "With OneIdentity, pattern nested",
+ ),
+ ("MatchQ[x, F[F[x2_:0,y_],x1_:0]]", "True", "With OneIdentity, pattern nested"),
+ (
+ "MatchQ[x, G[G[x2_:0,y_],x1_:0]]",
+ "False",
+ "With OneIdentity, pattern nested",
+ ),
+ ("MatchQ[x, F[x_.,y_]]", "False", "With OneIdentity, and Optional, no default"),
+ (
+ "MatchQ[x, G[x_.,y_]]",
+ "False",
+ "Without OneIdentity, and Optional, no default",
+ ),
+ ("Default[F, 1]=1.", "1.", None),
+ ("Default[G, 1]=2.", "2.", None),
+ ("MatchQ[x, F[x_.,y_]]", "True", "With OneIdentity, and Optional, default"),
+ ("MatchQ[x, G[x_.,y_]]", "False", "Without OneIdentity, and Optional, default"),
+ ("MatchQ[F[F[H[y]]],F[x_:0,u_H]]", "False", None),
+ ("MatchQ[G[G[H[y]]],G[x_:0,u_H]]", "False", None),
+ ("MatchQ[F[p, F[p, H[y]]],F[x_:0,u_H]]", "False", None),
+ ("MatchQ[G[p, G[p, H[y]]],G[x_:0,u_H]]", "False", None),
+ (None, None, None),
+ ],
+)
+def test_one_identity(str_expr, str_expected, msg):
+ check_evaluation(
+ str_expr,
+ str_expected,
+ to_string_expr=True,
+ to_string_expected=True,
+ hold_expected=True,
+ failure_message=msg,
+ )
+
+
+@pytest.mark.parametrize(
+ ("str_expr", "str_expected", "msg"),
+ [
+ (None, None, None),
+ # F has the attribute, but G doesn't.
+ ("SetAttributes[F, OneIdentity]", "Null", None),
+ ("SetAttributes[r, Flat]", "Null", None),
+ ("SetAttributes[s, Flat]", "Null", None),
+ ("SetAttributes[s, OneIdentity]", "Null", None),
+ # Replace also takes into account the OneIdentity attribute,
+ # and also modifies the interpretation of the Flat attribute.
+ (
+ "F[a,b,b,c]/.F[x_,x_]->Fp[x]",
+ "F[a, b, b, c]",
+ "https://reference.wolfram.com/language/tutorial/Patterns.html",
+ ),
+ # When fixed, Add the difference between the next two as a
+ # doctest example.
+ (
+ "r[a,b,b,c]/.r[x_,x_]->rp[x]",
+ "r[a, rp[r[b]], c]",
+ "https://reference.wolfram.com/language/tutorial/Patterns.html",
+ ),
+ (
+ "s[a,b,b,c]/.s[x_,x_]->sp[x]",
+ "s[a, rp[b], c]",
+ "https://reference.wolfram.com/language/tutorial/Patterns.html",
+ ),
+ (None, None, None),
+ ],
+)
+@skip_or_fail
+def test_one_identity_stil_failing(str_expr, str_expected, msg):
+ check_evaluation(
+ str_expr,
+ str_expected,
+ to_string_expr=True,
+ to_string_expected=True,
+ hold_expected=True,
+ failure_message=msg,
+ )
+
+
+@pytest.mark.parametrize(
+ ("str_expr", "str_expected", "msg"),
+ [
+ (None, None, None),
+ # F has the attribute, but G doesn't.
+ ("SetAttributes[F, OneIdentity]", "Null", None),
+ ("SetAttributes[r, Flat]", "Null", None),
+ ("SetAttributes[s, Flat]", "Null", None),
+ ("SetAttributes[s, OneIdentity]", "Null", None),
+ ("MatchQ[x, F[y_]]", "False", "With OneIdentity"),
+ ("MatchQ[x, G[y_]]", "False", "Without OneIdentity"),
+ ("MatchQ[x, F[x_:0,y_]]", "True", "With OneIdentity, and Default"),
+ ("MatchQ[x, G[x_:0,y_]]", "False", "Without OneIdentity, and Default"),
+ ("MatchQ[F[x], F[x_:0,y_]]", "True", "With OneIdentity, and Default"),
+ ("MatchQ[G[x], G[x_:0,y_]]", "True", "Without OneIdentity, and Default"),
+ ("MatchQ[F[F[F[x]]], F[x_:0,y_]]", "True", "With OneIdentity, nested"),
+ ("MatchQ[G[G[G[x]]], G[x_:0,y_]]", "True", "Without OneIdentity, nested"),
+ ("MatchQ[F[3, F[F[x]]], F[x_:0,y_]]", "True", "With OneIdentity, nested"),
+ ("MatchQ[G[3, G[G[x]]], G[x_:0,y_]]", "True", "Without OneIdentity, nested"),
+ (
+ "MatchQ[x, F[x1_:0, F[x2_:0,y_]]]",
+ "True",
+ "With OneIdentity, pattern nested",
+ ),
+ (
+ "MatchQ[x, G[x1_:0, G[x2_:0,y_]]]",
+ "False",
+ "With OneIdentity, pattern nested",
+ ),
+ (
+ "MatchQ[x, F[x1___:0, F[x2_:0,y_]]]",
+ "True",
+ "With OneIdentity, pattern nested",
+ ),
+ (
+ "MatchQ[x, G[x1___:0, G[x2_:0,y_]]]",
+ "False",
+ "With OneIdentity, pattern nested",
+ ),
+ ("MatchQ[x, F[F[x2_:0,y_],x1_:0]]", "True", "With OneIdentity, pattern nested"),
+ (
+ "MatchQ[x, G[G[x2_:0,y_],x1_:0]]",
+ "False",
+ "With OneIdentity, pattern nested",
+ ),
+ ("MatchQ[x, F[x_.,y_]]", "False", "With OneIdentity, and Optional, no default"),
+ (
+ "MatchQ[x, G[x_.,y_]]",
+ "False",
+ "Without OneIdentity, and Optional, no default",
+ ),
+ ("Default[F, 1]=1.", "1.", None),
+ ("Default[G, 1]=2.", "2.", None),
+ ("MatchQ[x, F[x_.,y_]]", "True", "With OneIdentity, and Optional, default"),
+ ("MatchQ[x, G[x_.,y_]]", "False", "Without OneIdentity, and Optional, default"),
+ ("MatchQ[F[F[H[y]]],F[x_:0,u_H]]", "False", None),
+ ("MatchQ[G[G[H[y]]],G[x_:0,u_H]]", "False", None),
+ ("MatchQ[F[p, F[p, H[y]]],F[x_:0,u_H]]", "False", None),
+ ("MatchQ[G[p, G[p, H[y]]],G[x_:0,u_H]]", "False", None),
+ (None, None, None),
+ ],
+)
+@skip_or_fail
+def test_one_identity_stil_failing(str_expr, str_expected, msg):
+ check_evaluation(
+ str_expr,
+ str_expected,
+ to_string_expr=True,
+ to_string_expected=True,
+ hold_expected=True,
+ failure_message=msg,
+ )
diff --git a/test/builtin/test_patterns.py b/test/builtin/test_patterns.py
new file mode 100644
index 000000000..9fb3d8a0f
--- /dev/null
+++ b/test/builtin/test_patterns.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+"""
+Unit tests from mathics.builtin.patterns.
+"""
+
+from test.helper import check_evaluation
+
+
+def test_blank():
+ for str_expr, str_expected, message in (
+ (
+ "g[i] /. _[i] :> a",
+ "a",
+ "Issue #203",
+ ),
+ ):
+ check_evaluation(str_expr, str_expected, message)
+
+
+def test_replace_all():
+ for str_expr, str_expected, message in (
+ (
+ "a == d b + d c /. a_ x_ + a_ y_ -> a (x + y)",
+ "a == (b + c) d",
+ "Issue #212",
+ ),
+ ):
+ check_evaluation(str_expr, str_expected, message)
diff --git a/test/test_context.py b/test/test_context.py
index aec68fc7d..480121de0 100644
--- a/test/test_context.py
+++ b/test/test_context.py
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
from .helper import check_evaluation, reset_session
+import pytest
+
from mathics_scanner.errors import IncompleteSyntaxError
@@ -60,37 +62,37 @@ def test_context1():
check_evaluation("bar[]", "42", to_string_expr=False, to_string_expected=False)
-def test_context2():
- nomessage = tuple([])
- for expr, expected, lst_messages, msg in [
+@pytest.mark.parametrize(
+ ("expr", "expected", "lst_messages", "msg"),
+ [
(
"""globalvarY = 37;""",
None,
- nomessage,
+ None,
"set the value of a global symbol",
),
(
"""globalvarZ = 37;""",
None,
- nomessage,
+ None,
"set the value of a global symbol",
),
(
"""BeginPackage["apackage`"];""",
None,
- nomessage,
+ None,
"Start a context. Add it to the context path",
),
(
"""Minus::usage=" usage string setted in the package for Minus";""",
None,
- nomessage,
+ None,
"set the usage string for a protected symbol ->no error",
),
(
"""Minus::mymessage=" custom message string for Minus";""",
None,
- nomessage,
+ None,
"set a custom message for a protected symbol ->no error",
),
(
@@ -106,44 +108,44 @@ def test_context2():
(
"""X::usage = "package variable";""",
None,
- nomessage,
+ None,
"set the usage string for a package variable",
),
(
"""globalvarZ::usage = "a global variable";""",
None,
- nomessage,
+ None,
"set the usage string for a global symbol",
),
(
"""globalvarZ = 57;""",
None,
- nomessage,
+ None,
"reset the value of a global symbol",
),
- ("""B = 6;""", None, nomessage, "Set a symbol value in the package context"),
+ ("""B = 6;""", None, None, "Set a symbol value in the package context"),
(
"""Begin["`implementation`"];""",
None,
- nomessage,
+ None,
"Start a context. Do not add it to the context path",
),
(
"""{Context[A], Context[B], Context[X], Context[globalvarY], Context[globalvarZ]}""",
"""{"apackage`implementation`", "apackage`", "apackage`", "apackage`implementation`", "apackage`"}""",
- nomessage,
+ None,
None, # "context of the variables"
),
(
"""globalvarY::usage = "a global variable";""",
None,
- nomessage,
+ None,
"set the usage string for a global symbol",
),
(
"""globalvarY = 97;""",
None,
- nomessage,
+ None,
"reset the value of a global symbol",
),
(
@@ -159,115 +161,119 @@ def test_context2():
(
"""Plus::usage=" usage string setted in the package for Plus";""",
None,
- nomessage,
+ None,
"set the usage string for a protected symbol ->no error",
),
(
"""Plus::mymessage=" custom message string for Plus";""",
None,
- nomessage,
+ None,
"set a custom message for a protected symbol ->no error",
),
- ("""A = 7;""", None, nomessage, "Set a symbol value in the context"),
- ("""X = 9;""", None, nomessage, "set the value of the package variable"),
- ("""End[];""", None, nomessage, "go back to the previous context"),
+ ("""A = 7;""", None, None, "Set a symbol value in the context"),
+ ("""X = 9;""", None, None, "set the value of the package variable"),
+ ("""End[];""", None, None, "go back to the previous context"),
(
"""{Context[A], Context[B], Context[X], Context[globalvarY], Context[globalvarZ]}""",
"""{"apackage`", "apackage`", "apackage`", "apackage`", "apackage`"}""",
- nomessage,
+ None,
None, # "context of the variables in the package"
),
(
"""EndPackage[];""",
None,
- nomessage,
+ None,
"go back to the previous context. Keep the context in the contextpath",
),
(
"""{Context[A], Context[B], Context[X], Context[globalvarY], Context[globalvarZ]}""",
"""{"apackage`", "apackage`", "apackage`", "apackage`", "apackage`"}""",
- nomessage,
+ None,
None, # "context of the variables at global level"
),
- ("""A""", "A", nomessage, "A is not in any context of the context path. "),
- ("""B""", "6", nomessage, "B is in a context of the context path"),
- ("""Global`globalvarY""", "37", nomessage, ""),
+ ("""A""", "A", None, "A is not in any context of the context path. "),
+ ("""B""", "6", None, "B is in a context of the context path"),
+ ("""Global`globalvarY""", "37", None, ""),
(
"""Global`globalvarY::usage""",
"Global`globalvarY::usage",
- nomessage,
+ None,
"In WMA, the value would be set in the package",
),
- ("""Global`globalvarZ""", "37", nomessage, "the value set inside the package"),
+ ("""Global`globalvarZ""", "37", None, "the value set inside the package"),
(
"""Global`globalvarZ::usage""",
"Global`globalvarZ::usage",
- nomessage,
+ None,
"not affected by the package",
),
- ("""globalvarY""", "apackage`globalvarY", nomessage, ""),
+ ("""globalvarY""", "apackage`globalvarY", None, ""),
(
"""globalvarY::usage""",
"apackage`globalvarY::usage",
- nomessage,
+ None,
"In WMA, the value would be set in the package",
),
- ("""globalvarZ""", "57", nomessage, "the value set inside the package"),
+ ("""globalvarZ""", "57", None, "the value set inside the package"),
(
"""globalvarZ::usage""",
'"a global variable"',
- nomessage,
+ None,
"not affected by the package",
),
- ("""X""", "9", nomessage, "X is in a context of the context path"),
+ ("""X""", "9", None, "X is in a context of the context path"),
(
"""X::usage""",
'"package variable"',
- nomessage,
+ None,
"X is in a context of the context path",
),
(
"""apackage`implementation`A""",
"7",
- nomessage,
+ None,
"get A using its fully qualified name",
),
- ("""apackage`B""", "6", nomessage, "get B using its fully qualified name"),
+ ("""apackage`B""", "6", None, "get B using its fully qualified name"),
(
"""Plus::usage""",
' " usage string setted in the package for Plus" ',
- nomessage,
+ None,
"custom usage for Plus",
),
(
"""Minus::usage""",
'" usage string setted in the package for Minus"',
- nomessage,
+ None,
"custom usage for Minus",
),
(
"""Plus::mymessage""",
'" custom message string for Plus"',
- nomessage,
+ None,
"custom message for Plus",
),
(
"""Minus::mymessage""",
'" custom message string for Minus"',
- nomessage,
+ None,
"custom message for Minus",
),
- ]:
+ (None, None, None, None),
+ ],
+)
+def test_context2(expr, expected, lst_messages, msg):
+ if expr is not None and expected is None:
+ expected = "System`Null"
- if expected is None:
- expected = "System`Null"
- check_evaluation(
- expr,
- expected,
- failure_message=msg,
- to_string_expr=False,
- to_string_expected=False,
- expected_messages=lst_messages,
- hold_expected=True,
- )
- reset_session()
+ if lst_messages is None:
+ lst_messages = tuple([])
+ check_evaluation(
+ expr,
+ expected,
+ failure_message=msg,
+ to_string_expr=False,
+ to_string_expected=False,
+ expected_messages=lst_messages,
+ hold_expected=True,
+ )