diff --git a/mathics/builtin/list/associations.py b/mathics/builtin/list/associations.py index ad5750cc2..c2c5fd45b 100644 --- a/mathics/builtin/list/associations.py +++ b/mathics/builtin/list/associations.py @@ -26,7 +26,7 @@ from mathics.eval.lists import list_boxes -class Association(Builtin): +class Association_(Builtin): """ :WMA link: @@ -57,10 +57,11 @@ class Association(Builtin): = {1, 3} """ - error_idx = 0 - attributes = A_HOLD_ALL_COMPLETE | A_PROTECTED + error_idx = 0 + + name = "Association" summary_text = "make an association between keys and values" def eval_makeboxes(self, rules, f, evaluation: Evaluation): diff --git a/mathics/builtin/patterns/rules.py b/mathics/builtin/patterns/rules.py index 2327946cc..5555c00e2 100644 --- a/mathics/builtin/patterns/rules.py +++ b/mathics/builtin/patterns/rules.py @@ -140,7 +140,7 @@ def eval_list( return result def eval( - self, rules: Expression, evaluation: Evaluation + self, rules: BaseElement, evaluation: Evaluation ) -> OptionalType[BaseElement]: """Dispatch[rules_]""" if not isinstance(rules, Expression): @@ -489,6 +489,8 @@ def eval_list( return result +# rocky: I don't know why, but this class seems to need a lot of +# things that handled differently from normal practice. class Rule_(InfixOperator): """ @@ -510,20 +512,39 @@ class Rule_(InfixOperator): """ attributes = A_SEQUENCE_HOLD | A_PROTECTED + + # For some mysterious reason, the below does not work and + # we have to do argument checking as code in eval(). + # eval_error = Builtin.generic_argument_error + # expected_args = 2 + grouping = "Right" name = "Rule" needs_verbatim = True - # FIXME: if we remove this we have problems. - # We should be able to get this from JSON. + # FIXME: We should be able to get the operator from the JSON operator tables. + # However, if we remove the operator class variable assignment, is we have problems. + # Run test/format/test_format.py::test_makeboxes_text for details. operator = "->" + summary_text = "a replacement rule" - def eval_rule(self, elems, evaluation): + def eval(self, elems, evaluation: Evaluation): """Rule[elems___]""" + num_parms = len(elems.get_sequence()) if num_parms != 2: evaluation.message("Rule", "argrx", "Rule", Integer(num_parms), Integer2) + + # "Rule" is a somewhat generic term in WMA and thus Mathics3. + # Rule semantics and its implementation change depending on the context that the Rule appears in. + # Inside a Set, RuleDelayed, or ReplaceAll expression, the left-hand side of a Rule is a pattern. + # But inside an Association or an Option, it is a key-value pair, with no pattern matching applied + # to the left-hand side. + # As a result, at this point we don't know which context Rule[] might appear, so we have to return + # "None" which keeps the Expression the same. Returning "elems" which you might think would be the + # same thing, is not correct, since the Head symbol "Rule" can get replaced with "Sequence" symbol + # coming into this code. return None diff --git a/mathics/core/pattern.py b/mathics/core/pattern.py index 9d729e5f4..69260057f 100644 --- a/mathics/core/pattern.py +++ b/mathics/core/pattern.py @@ -203,6 +203,7 @@ def create( 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: diff --git a/mathics/core/rules.py b/mathics/core/rules.py index fc623bb9a..a52c9df8a 100644 --- a/mathics/core/rules.py +++ b/mathics/core/rules.py @@ -9,7 +9,10 @@ arguments, and then apply a Python "evaluation" function to a Mathics3 Expression. These kinds of rules are handled by objects in the `FunctionApplyRule` class. -This module contains the classes for these two types of rules. +Finally, Rules are used as KeyValue pairs in an Association or an Options list. +Here, no pattern matching is applied to the left-hand side. + +This module contains the classes for the first two non-KeyValue types of rules. In a `FunctionApplyRule` rule, the match status of a rule depends on the evaluation return. @@ -236,13 +239,24 @@ def element_order(self) -> tuple: # True used to be self.system. Can we remove True? return tuple((True, self.pattern.element_order)) + # The below routines might be needed in the future, if BaseRule becomes + # more BaseElement-like. Right now that's not the situation, probably + # due to the nature of the generality of Rule which causes + # special casing lots of places in the Expression code. + + # def get_head_name(self, short=False) -> str: + # return "Rule" if short else "System`Rule" + + # def get_lookup_name(self) -> str: + # return "System`Rule" + def get_replace_value(self) -> BaseElement: raise ValueError @property - def lhs(self) -> BasePattern: + def lhs(self) -> BasePattern | Expression: """ - Lefthand side of a rule. Also known as its "pattern". + Left-hand side of a rule. Also known, in this context, as its "pattern". """ return self.pattern @@ -351,7 +365,7 @@ def get_replace_value(self) -> BaseElement: """return the replace value""" return self.replace - # This will probably be needed when we use with builtin Rule_ + # # This might be needed in the future. # @property # def is_literal(self): # """