Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bark when a rule in Builtin.rules cannot be loaded #1006

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions mathics/builtin/box/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]


Expand Down
6 changes: 0 additions & 6 deletions mathics/builtin/datentime.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 3 additions & 1 deletion mathics/builtin/files_io/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -1384,6 +1384,8 @@ class Find(Read):
= ...
"""

rules = {}

options = {
"AnchoredSearch": "False",
"IgnoreCase": "False",
Expand Down
128 changes: 113 additions & 15 deletions mathics/core/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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()
Expand Down
59 changes: 59 additions & 0 deletions test/core/test_definitions.py
Original file line number Diff line number Diff line change
@@ -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