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 fe00e74e7..1f9926f7a 100644 --- a/mathics/builtin/files_io/files.py +++ b/mathics/builtin/files_io/files.py @@ -1031,7 +1031,7 @@ def eval(self, channel, types, evaluation: Evaluation, options: dict): return from_python(result) def eval_nostream(self, arg1, arg2, evaluation): - "Read[arg1_, arg2_]" + "%(name)s[arg1_, arg2_]" evaluation.message("General", "stream", arg1) return @@ -1384,6 +1384,8 @@ class Find(Read): = ... """ + rules = {} + options = { "AnchoredSearch": "False", "IgnoreCase": "False", diff --git a/mathics/core/definitions.py b/mathics/core/definitions.py index 0308f5312..e4e77ac74 100644 --- a/mathics/core/definitions.py +++ b/mathics/core/definitions.py @@ -725,25 +725,122 @@ 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): + """ + 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. + """ + if pat.get_head_name() == "System`Condition": + if len(pat.elements) > 1: + return strip_pattern_name_and_condition(pat.elements[0]) + if pat.get_head_name() == "System`Pattern": + if len(pat.elements) == 2: + return strip_pattern_name_and_condition(pat.elements[1]) + return pat + + def check_is_subvalue(pattern_sv, name_sv): + """Determines if ``pattern`` is a subvalue of ``name``""" + if name_sv == pattern_sv.get_lookup_name(): + return True + + # Try again after strip Pattern and Condition wrappers: + head = strip_pattern_name_and_condition(pattern_sv.get_head()) + head_name = head.get_lookup_name() + if name_sv == head_name: + return True + # The head is of the form ``_SymbolName|__SymbolName|___SymbolName`` + # If name matches with SymbolName, then is a subvalue: + 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 == name_sv: + 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) + + # Check if ``pattern`` is a subvalue: + + # The head is not a symbol. pattern is a subvalue? + if check_is_subvalue(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 +915,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