diff --git a/mathics/builtin/box/layout.py b/mathics/builtin/box/layout.py index b5bc7e9d9..9908fcc74 100644 --- a/mathics/builtin/box/layout.py +++ b/mathics/builtin/box/layout.py @@ -203,11 +203,11 @@ class InterpretationBox(BoxExpression): summary_text = "box associated to an input expression" def eval_to_expression(boxexpr, form, evaluation): - """ToExpression[boxexpr_IntepretationBox, form___]""" + """ToExpression[boxexpr_InterpretationBox, form___]""" return boxexpr.elements[1] def eval_display(boxexpr, evaluation): - """DisplayForm[boxexpr_IntepretationBox]""" + """DisplayForm[boxexpr_InterpretationBox]""" return boxexpr.elements[0] diff --git a/mathics/builtin/datentime.py b/mathics/builtin/datentime.py index 577e78a95..d0a2aebb7 100644 --- a/mathics/builtin/datentime.py +++ b/mathics/builtin/datentime.py @@ -1201,12 +1201,6 @@ class TimeZone(Predefined): summary_text = "gets the default time zone" - def eval(self, lhs, rhs, evaluation): - "lhs_ = rhs_" - - self.assign(lhs, rhs, evaluation) - return rhs - def evaluate(self, evaluation) -> Real: return self.value diff --git a/mathics/builtin/files_io/files.py b/mathics/builtin/files_io/files.py index 3248ba826..9efb0e43a 100644 --- a/mathics/builtin/files_io/files.py +++ b/mathics/builtin/files_io/files.py @@ -1354,6 +1354,8 @@ class Find(Read): = ... """ + rules = {} + options = { "AnchoredSearch": "False", "IgnoreCase": "False", diff --git a/mathics/core/definitions.py b/mathics/core/definitions.py index 0308f5312..1d449e063 100644 --- a/mathics/core/definitions.py +++ b/mathics/core/definitions.py @@ -17,6 +17,7 @@ from mathics.core.element import fully_qualified_symbol_name from mathics.core.expression import Expression from mathics.core.load_builtin import definition_contribute, mathics3_builtins_modules +from mathics.core.pattern import BasePattern, ExpressionPattern from mathics.core.symbols import Atom, Symbol, strip_context from mathics.core.systemsymbols import SymbolGet from mathics.core.util import canonic_filename @@ -725,25 +726,137 @@ def get_history_length(self): def get_tag_position(pattern, name) -> Optional[str]: + """ + Determine the position of a pattern in + the definition of the symbol ``name`` + """ + blanks = ( + "System`Blank", + "System`BlankSequence", + "System`BlankNullSequence", + ) + + def strip_pattern_name_and_condition(pat: BasePattern) -> ExpressionPattern: + """ + In ``Pattern[name_, pattern_]`` and + ``Condition[pattern_, cond_]`` + the tag is determined by pat. + This function strips it to ensure that + ``pat`` does not have that form. + """ + + # Is "pat" as ExpressionPattern or an AtomPattern? + # Note: the below test could also be on ExpressionPattern or + # AtomPattern, but using hasattr is more flexible if more + # kinds of patterns are added. + if not hasattr(pat, "head"): + return pat + + # We have to use get_head_name() below because + # pat can either SymbolCondition or . + # In the latter case, comparing to SymbolCondition is not sufficient. + if pat.get_head_name() == "System`Condition": + if len(pat.elements) > 1: + return strip_pattern_name_and_condition(pat.elements[0]) + # The same kind of get_head_name() check is needed here as well and + # is not the same as testing against SymbolPattern. + if pat.get_head_name() == "System`Pattern": + if len(pat.elements) == 2: + return strip_pattern_name_and_condition(pat.elements[1]) + return pat + + def is_pattern_a_kind_of(pattern: ExpressionPattern, pattern_name: str) -> bool: + """ + Returns `True` if `pattern` or any of its alternates is a + pattern with name `pattern_name` and `False` otherwise.""" + + if pattern_name == pattern.get_lookup_name(): + return True + + # Try again after stripping Pattern and Condition wrappers: + head = strip_pattern_name_and_condition(pattern.get_head()) + head_name = head.get_lookup_name() + if pattern_name == head_name: + return True + + # The head is of the form ``_SymbolName|__SymbolName|___SymbolName`` + # If name matches with SymbolName, then it is a kind of: + if head_name in blanks: + if isinstance(head, Symbol): + return False + sub_elements = head.elements + if len(sub_elements) == 1: + head_name = head.elements[0].get_name() + if head_name == pattern_name: + return True + return False + + # If pattern is a Symbol, and coincides with + # name, it is an ownvalue: + if pattern.get_name() == name: return "own" - elif isinstance(pattern, Atom): + # If pattern is an ``Atom``, does not have + # a position + if isinstance(pattern, Atom): return None - else: - head_name = pattern.get_head_name() - if head_name == name: - return "down" - elif head_name == "System`N" and len(pattern.elements) == 2: + + # The pattern is an Expression. + head_name = pattern.get_head_name() + # If the name is the head name, is a downvalue: + if head_name == name: + return "down" + + # Handle special cases + if head_name == "System`N": + if len(pattern.elements) == 2: return "n" - elif head_name == "System`Condition" and len(pattern.elements) > 0: - return get_tag_position(pattern.elements[0], name) - elif pattern.get_lookup_name() == name: - return "sub" - else: - for element in pattern.elements: - if element.get_lookup_name() == name: + + # The pattern has the form `_SymbolName | __SymbolName | ___SymbolName` + # Then it only can be a downvalue + if head_name in blanks: + elements = pattern.elements + if len(elements) == 1: + head_name = elements[0].get_name() + return "down" if head_name == name else None + + # TODO: Consider process format_values + + if head_name != "": + # Check + strip_pattern = strip_pattern_name_and_condition(pattern) + if strip_pattern is not pattern: + return get_tag_position(strip_pattern, name) + + # The head is not a symbol. Is pattern is "name" kind of pattern? + if is_pattern_a_kind_of(pattern, name): + return "sub" + + # If we are here, pattern is not an Ownvalue, DownValue, SubValue or NValue + # Let's check the elements for UpValues + for element in pattern.elements: + lookup_name = element.get_lookup_name() + if lookup_name == name: + return "up" + + # Strip Pattern and Condition wrappers and check again + if lookup_name in ( + "System`Condition", + "System`Pattern", + ): + element = strip_pattern_name_and_condition(element) + lookup_name = element.get_lookup_name() + if lookup_name == name: + return "up" + # Check if one of the elements is not a "Blank" + + if element.get_head_name() in blanks: + sub_elements = element.elements + if len(sub_elements) == 1: + if sub_elements[0].get_name() == name: return "up" - return None + # ``pattern`` does not have a tag position in the Definition + return None def insert_rule(values, rule) -> None: @@ -818,7 +931,8 @@ def __init__( self.defaultvalues = defaultvalues self.builtin = builtin for rule in rules: - self.add_rule(rule) + if not self.add_rule(rule): + print(f"{rule.pattern.expr} could not be associated to {self.name}") def get_values_list(self, pos): assert pos.isalpha() diff --git a/test/core/test_definitions.py b/test/core/test_definitions.py new file mode 100644 index 000000000..43eb160b0 --- /dev/null +++ b/test/core/test_definitions.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +""" +Tests functions in mathics.core.definition +""" + +import pytest + +from mathics.core.definitions import get_tag_position +from mathics.core.parser import parse_builtin_rule + + +@pytest.mark.parametrize( + ("pattern_str", "tag", "position"), + [ + # None + ("A", "B", None), + ("A_", "B", None), + ("A[c_]", "B", None), + ("A[3]", "B", None), + ("A[B][3]", "B", None), + ("A[s[x_]][y]", "s", None), + # Ownvalues + ("A", "A", "own"), + ("A/;A>0", "A", "own"), + ("s:(A/;A>0)", "A", "own"), + ("(s:A)/;A>0", "A", "own"), + ("s:A/;A>0", "A", "own"), + # Downvalues + ("_A", "A", "down"), + ("A[]", "A", "down"), + ("_A", "A", "down"), + ("A[p_, q]", "A", "down"), + ("s:A[p_, q]", "A", "down"), + ("A[p_, q]/;q>0", "A", "down"), + ("(s:A[p_, q])/;q>0", "A", "down"), + # NValues + ("N[A[x_], _]", "A", "n"), + ("N[A[x_], _]/; x>0", "A", "n"), + # Subvalues + ("_A[]", "A", "sub"), + ("A[x][t]", "A", "sub"), + ("(s:A[x])[t]", "A", "sub"), + ("(x_A/;u>0)[p]", "A", "sub"), + # Upvalues + ("S[x_, A]", "A", "up"), + ("S[x_, _A]", "A", "up"), + ("S[x_, s_A/;s>0]", "A", "up"), + ("S[x_, q:A]", "A", "up"), + ("S[x_, q:(A[t_]/;t>0)]", "A", "up"), + ("A[x_][s[y]]", "s", "up"), + ("DisplayForm[boxexpr_InterpretationBox]", "InterpretationBox", "up"), + ("ToExpression[boxexpr_InterpretationBox, form___]", "InterpretationBox", "up"), + # Just one argument, must be an upvalue + ("N[A[s_]]", "A", "up"), + ], +) +def test_get_tag_position(pattern_str, tag, position): + pattern = parse_builtin_rule(pattern_str) + assert get_tag_position(pattern, f"System`{tag}") == position