diff --git a/mathics/builtin/atomic/symbols.py b/mathics/builtin/atomic/symbols.py
index 628bc0af0..c576905a2 100644
--- a/mathics/builtin/atomic/symbols.py
+++ b/mathics/builtin/atomic/symbols.py
@@ -3,7 +3,7 @@
Symbol Handling
Symbolic data. Every symbol has a unique name, exists in a certain context \
-or namespace, and can have a variety of type of values and attributes.
+or namespace, and can have a variety of types of values and attributes.
"""
import re
from typing import Callable, Optional
@@ -28,7 +28,7 @@
from mathics.core.evaluation import Evaluation
from mathics.core.expression import Expression
from mathics.core.list import ListExpression
-from mathics.core.rules import Rule
+from mathics.core.rules import RewriteRule
from mathics.core.symbols import (
Symbol,
SymbolFalse,
@@ -69,7 +69,7 @@ def rhs_format(expr):
return expr
def format_rule(
- rule: Rule,
+ rule: RewriteRule,
up: bool = False,
lhs: Callable = lambda k: k,
rhs: Callable = lambda r: r,
@@ -78,7 +78,7 @@ def format_rule(
Add a line showing `rule`
"""
evaluation.check_stopped()
- if isinstance(rule, Rule):
+ if isinstance(rule, RewriteRule):
lhs_pat = Expression(SymbolInputForm, lhs(rule.pattern.expr))
repl_expr = rhs(
rule.replace.replace_vars(
@@ -363,7 +363,7 @@ class DownValues(Builtin):
= {HoldPattern[f[x_]] ⧴ x ^ 2}
Mathics3 will sort the rules you assign to a symbol according to \
- their specificity. If it cannot decide which rule is more special, \
+ their specificity. If it cannot decide which rule is more specific, \
the newer one will get higher precedence.
>> f[x_Integer] := 2
>> f[x_Real] := 3
@@ -420,7 +420,7 @@ class FormatValues(Builtin):
>> FormatValues[F]
= {HoldPattern[Subscript[x_, F]] ⧴ Subscript[x, F]}
- The replacment pattern on the right in the delayed rule is formatted according to the top-level form. To see the rule input, we can use 'InputForm':
+ The replacement pattern on the right in the delayed rule is formatted according to the top-level form. To see the rule input, we can use 'InputForm':
>> FormatValues[F] //InputForm
= {HoldPattern[Format[F[x_], OutputForm]] ⧴ Subscript[x, F]}
"""
diff --git a/mathics/builtin/box/graphics.py b/mathics/builtin/box/graphics.py
index 1a5b4b431..2372ae5d0 100644
--- a/mathics/builtin/box/graphics.py
+++ b/mathics/builtin/box/graphics.py
@@ -4,7 +4,7 @@
"""
from abc import ABC
from math import atan2, cos, degrees, pi, sin
-from typing import Any, Dict, Final, List, Optional, Tuple
+from typing import Any, Final, Optional
from mathics.builtin.box.expression import BoxExpression
from mathics.builtin.colors.color_directives import (
@@ -31,6 +31,7 @@
from mathics.core.expression import Expression
from mathics.core.formatter import lookup_method
from mathics.core.list import ListExpression
+from mathics.core.rules import is_rule
from mathics.core.symbols import Symbol
from mathics.core.systemsymbols import SymbolInsetBox, SymbolTraditionalForm
from mathics.format.box import format_element
@@ -56,7 +57,7 @@ def init(self, graphics, item=None, style={}, opacity=1.0):
# GraphicsElementBox Builtin class that should not get added as a definition,
# and therefore not added to to external documentation.
-DOES_NOT_ADD_BUILTIN_DEFINITION: Final[List[BoxExpression]] = [GraphicsElementBox]
+DOES_NOT_ADD_BUILTIN_DEFINITION: Final[list[BoxExpression]] = [GraphicsElementBox]
class _Polyline(GraphicsElementBox):
@@ -471,9 +472,9 @@ class GraphicsBox(BoxExpression):
summary_text = "symbol used in boxing 'Graphics'"
def init(self, *items, **kwargs):
- self._elements: Optional[Tuple[BaseElement], ...] = None
+ self._elements: Optional[tuple[BaseElement], ...] = None
self.content = items[0]
- self.box_options: Dict[str, Any] = kwargs
+ self.box_options: dict[str, Any] = kwargs
self.background_color = None
self.tooltip_text: Optional[str] = None
self.evaluation = kwargs.pop("_evaluation", None)
@@ -763,7 +764,7 @@ def init(self, graphics, style, item=None):
self.do_init(graphics, points)
self.vertex_colors = None
for element in item.elements[1:]:
- if not element.has_form("Rule", 2):
+ if not is_rule(element):
raise BoxExpressionError
name = element.elements[0].get_name()
self.process_option(name, element.elements[1])
diff --git a/mathics/builtin/functional/apply_fns_to_lists.py b/mathics/builtin/functional/apply_fns_to_lists.py
index 7d663f8be..87eefff36 100644
--- a/mathics/builtin/functional/apply_fns_to_lists.py
+++ b/mathics/builtin/functional/apply_fns_to_lists.py
@@ -18,6 +18,7 @@
from mathics.core.exceptions import InvalidLevelspecError, MessageException
from mathics.core.expression import Expression
from mathics.core.list import ListExpression
+from mathics.core.rules import is_rule
from mathics.core.symbols import Atom, Symbol, SymbolNull, SymbolTrue
from mathics.core.systemsymbols import SymbolMapThread
from mathics.eval.functional.apply_fns_to_lists import eval_MapAt
@@ -191,7 +192,7 @@ def callback(level):
#
# Fixing this would require a different implementation of this eval_ method.
#
- if is_association and level.has_form(("Rule", "RuleDelayed"), 2):
+ if is_association and is_rule(level):
return Expression(
level.get_head(),
level.elements[0],
diff --git a/mathics/builtin/list/associations.py b/mathics/builtin/list/associations.py
index b31adf502..ad5750cc2 100644
--- a/mathics/builtin/list/associations.py
+++ b/mathics/builtin/list/associations.py
@@ -15,6 +15,7 @@
from mathics.core.convert.expression import to_mathics_list
from mathics.core.evaluation import Evaluation
from mathics.core.expression import Expression
+from mathics.core.rules import is_rule
from mathics.core.symbols import Symbol, SymbolTrue
from mathics.core.systemsymbols import SymbolAssociation, SymbolMakeBoxes, SymbolMissing
from mathics.eval.list.associations import (
@@ -68,7 +69,7 @@ def eval_makeboxes(self, rules, f, evaluation: Evaluation):
def validate(exprs):
for expr in exprs:
- if expr.has_form(("Rule", "RuleDelayed"), 2):
+ if is_rule(expr):
pass
elif expr.has_form(("List", "Association"), None):
if not validate(expr.elements):
@@ -96,7 +97,7 @@ def eval(self, rules, evaluation: Evaluation):
def make_flatten(exprs, rules_dictionary: dict = {}):
for expr in exprs:
- if expr.has_form(("Rule", "RuleDelayed"), 2):
+ if is_rule(expr):
elements = expr.elements
key = elements[0].evaluate(evaluation)
value = elements[1].evaluate(evaluation)
@@ -117,7 +118,7 @@ def eval_key(self, rules, key, evaluation: Evaluation):
def find_key(exprs, rules_dictionary: dict = {}):
for expr in exprs:
- if expr.has_form(("Rule", "RuleDelayed"), 2):
+ if is_rule(expr):
if expr.elements[0] == key:
rules_dictionary[key] = expr.elements[1]
elif expr.has_form(("List", "Association"), None):
@@ -158,7 +159,7 @@ class AssociationQ(Test):
def test(self, expr) -> bool:
def validate(elements):
for element in elements:
- if element.has_form(("Rule", "RuleDelayed"), 2):
+ if is_rule(element):
pass
elif element.has_form(("List", "Association"), None):
if not validate(element.elements):
@@ -233,7 +234,7 @@ def eval(self, rules, evaluation: Evaluation):
"Keys[rules_]"
def get_keys(expr):
- if expr.has_form(("Rule", "RuleDelayed"), 2):
+ if is_rule(expr):
return expr.elements[0]
elif expr.has_form("List", None) or (
expr.has_form("Association", None)
@@ -258,7 +259,7 @@ def eval_with_head(self, rules, head, evaluation: Evaluation):
"Keys[rules_, head_]"
def get_keys_with_head(expr, h):
- if expr.has_form(("Rule", "RuleDelayed"), 2):
+ if is_rule(expr):
key = expr.elements[0]
return Expression(h, key)
elif expr.has_form("List", None) or (
@@ -419,7 +420,7 @@ def eval(self, rules, evaluation: Evaluation):
"Values[rules_]"
def get_values(expr):
- if expr.has_form(("Rule", "RuleDelayed"), 2):
+ if is_rule(expr):
return expr.elements[1]
elif expr.has_form("List", None) or (
expr.has_form("Association", None)
@@ -445,7 +446,7 @@ def eval_with_head(self, rules, head, evaluation: Evaluation):
"Values[rules_, head_]"
def get_values_with_head(expr, h):
- if expr.has_form(("Rule", "RuleDelayed"), 2):
+ if is_rule(expr):
value = expr.elements[1]
return Expression(h, value)
elif expr.has_form("List", None) or (
diff --git a/mathics/builtin/list/eol.py b/mathics/builtin/list/eol.py
index 7bb938538..e84fd488a 100644
--- a/mathics/builtin/list/eol.py
+++ b/mathics/builtin/list/eol.py
@@ -43,7 +43,7 @@
from mathics.core.expression import Expression
from mathics.core.expression_predefined import MATHICS3_INFINITY
from mathics.core.list import ListExpression
-from mathics.core.rules import Rule
+from mathics.core.rules import RewriteRule, is_rule
from mathics.core.symbols import Atom, Symbol, SymbolNull, SymbolTrue
from mathics.core.systemsymbols import (
SymbolAppend,
@@ -216,7 +216,7 @@ def eval(self, items, pattern, levelspec, evaluation, options):
if isinstance(items, Atom):
return ListExpression()
- if levelspec.has_form("Rule", 2):
+ if is_rule(levelspec):
if levelspec.elements[0].get_name() == "System`Heads":
heads = levelspec.elements[1] is SymbolTrue
levelspec = ListExpression(Integer1)
@@ -234,9 +234,9 @@ def eval(self, items, pattern, levelspec, evaluation, options):
results = []
- if pattern.has_form("Rule", 2) or pattern.has_form("RuleDelayed", 2):
+ if is_rule(pattern):
match = Matcher(pattern.elements[0], evaluation).match
- rule = Rule(pattern.elements[0], pattern.elements[1])
+ rule = RewriteRule(pattern.elements[0], pattern.elements[1])
def callback(level):
if match(level, evaluation):
@@ -1497,9 +1497,7 @@ def eval(self, expr, replacements, evaluation):
new_expr = expr.copy()
replacements = replacements.get_sequence()
for replacement in replacements:
- if not replacement.has_form("Rule", 2) and not replacement.has_form( # noqa
- "RuleDelayed", 2
- ):
+ if not is_rule(replacement):
evaluation.message("ReplacePart", "reps", ListExpression(*replacements))
return
position = replacement.elements[0]
diff --git a/mathics/builtin/patterns/rules.py b/mathics/builtin/patterns/rules.py
index 1fb49428d..2327946cc 100644
--- a/mathics/builtin/patterns/rules.py
+++ b/mathics/builtin/patterns/rules.py
@@ -492,7 +492,7 @@ def eval_list(
class Rule_(InfixOperator):
"""
- :WMA link:https://reference.wolfram.com/language/ref/Rule_.html
+ :WMA link:https://reference.wolfram.com/language/ref/Rule.html
- 'Rule'[$x$, $y$]
diff --git a/mathics/core/assignment.py b/mathics/core/assignment.py
index 0a94f0fe9..a75fcd28a 100644
--- a/mathics/core/assignment.py
+++ b/mathics/core/assignment.py
@@ -14,7 +14,7 @@
from mathics.core.evaluation import Evaluation
from mathics.core.expression import Expression
from mathics.core.list import ListExpression
-from mathics.core.rules import Rule
+from mathics.core.rules import RewriteRule
from mathics.core.symbols import Atom, Symbol, SymbolList
from mathics.core.systemsymbols import (
SymbolAnd,
@@ -29,9 +29,9 @@
)
-def build_rulopc(optval: BaseElement) -> Rule:
+def build_rulopc(optval: BaseElement) -> RewriteRule:
"""Build an option value rule for optval"""
- return Rule(
+ return RewriteRule(
Expression(
SymbolOptionValue,
Expression(SymbolPattern, Symbol("$cond$"), SymbolBlank),
@@ -157,7 +157,7 @@ def get_symbol_values(
return ListExpression(*elements)
for rule in definition.get_values_list(position):
- if isinstance(rule, Rule):
+ if isinstance(rule, RewriteRule):
pattern = rule.pattern
if pattern.has_form("HoldPattern", 1):
expr_pattern = pattern.expr
@@ -350,7 +350,7 @@ def unroll_patterns(
# like
# rhs = Expression(Symbol("System`Replace"), Rule(*rulerepl))
# TODO: check if this is the correct behavior.
- rhs, _ = rhs.do_apply_rules([Rule(*rulerepl)], evaluation)
+ rhs, _ = rhs.do_apply_rules([RewriteRule(*rulerepl)], evaluation)
name = lhs.get_head_name()
elif name == "System`HoldPattern":
lhs = lhs_elements[0]
diff --git a/mathics/core/builtin.py b/mathics/core/builtin.py
index 125cc9e5e..c570ae7fa 100644
--- a/mathics/core/builtin.py
+++ b/mathics/core/builtin.py
@@ -60,7 +60,7 @@
from mathics.core.parser.operators import OPERATOR_DATA
from mathics.core.parser.util import PyMathicsDefinitions, SystemDefinitions
from mathics.core.pattern import BasePattern, build_pattern_sort_key
-from mathics.core.rules import BaseRule, FunctionApplyRule, Rule
+from mathics.core.rules import BaseRule, FunctionApplyRule, RewriteRule
from mathics.core.symbols import (
Atom,
BaseElement,
@@ -300,7 +300,6 @@ def contribute(self, definitions: Definitions, is_pymodule=False):
function,
check_options,
attributes=pat_attr,
- system=True,
)
)
for pattern_str, replace_str in self.rules.items():
@@ -309,11 +308,10 @@ def contribute(self, definitions: Definitions, is_pymodule=False):
replace_str = replace_str % {"name": name}
pat_attr = attributes if pattern.get_head_name() == name else None
rules.append(
- Rule(
+ RewriteRule(
pattern,
parse_builtin_rule(replace_str),
attributes=pat_attr,
- system=not is_pymodule,
)
)
@@ -361,7 +359,7 @@ def contextify_form_name(f):
formatvalues[form] = []
formatvalues[form].append(
FunctionApplyRule(
- name, pattern, function, None, attributes=pat_attr, system=True
+ name, pattern, function, None, attributes=pat_attr
)
)
for pattern, replace in self.formats.items():
@@ -374,7 +372,7 @@ def contextify_form_name(f):
pattern = parse_builtin_rule(pattern)
replace = replace % {"name": name}
formatvalues[form].append(
- Rule(pattern, parse_builtin_rule(replace), system=True)
+ RewriteRule(pattern, parse_builtin_rule(replace))
)
formatvalues.setdefault("_MakeBoxes", []).extend(box_rules)
@@ -385,19 +383,17 @@ def contextify_form_name(f):
if hasattr(self, "summary_text"):
self.messages["usage"] = self.summary_text
messages = [
- Rule(
+ RewriteRule(
Expression(SymbolMessageName, Symbol(name), String(msg)),
String(value),
- system=True,
)
for msg, value in self.messages.items()
]
messages.append(
- Rule(
+ RewriteRule(
Expression(SymbolMessageName, Symbol(name), String("optx")),
String("`1` is not a supported option for `2`[]."),
- system=True,
)
)
@@ -410,7 +406,7 @@ def contextify_form_name(f):
elif isinstance(spec, int):
pattern = Expression(SymbolDefault, Symbol(name), Integer(spec))
if pattern is not None:
- defaults.append(Rule(pattern, value, system=True))
+ defaults.append(RewriteRule(pattern, value))
definition = Definition(
name=name,
@@ -1389,7 +1385,7 @@ def __init__(self, *args, **kwargs):
operator = ascii_operator_to_symbol.get(self.operator, self.__class__.__name__)
if self.default_formats:
- if name not in ("Rule", "RuleDelayed"):
+ if name not in ("RewriteRule", "Rule", "RuleDelayed"):
formats = {
op_pattern: "HoldForm[Infix[{%s}, %s, %d, %s]]"
% (replace_items, operator, self.precedence, self.grouping)
diff --git a/mathics/core/definitions.py b/mathics/core/definitions.py
index c58c60869..234ab7194 100644
--- a/mathics/core/definitions.py
+++ b/mathics/core/definitions.py
@@ -18,7 +18,7 @@
from mathics.core.attributes import A_NO_ATTRIBUTES
from mathics.core.convert.expression import to_mathics_list
from mathics.core.element import BaseElement, fully_qualified_symbol_name
-from mathics.core.rules import BaseRule, Rule
+from mathics.core.rules import BaseRule, RewriteRule
from mathics.core.symbols import Atom, Symbol, strip_context
from mathics.core.util import canonic_filename
from mathics.settings import ROOT_DIR
@@ -771,7 +771,7 @@ def get_ownvalue(self, name: str) -> BaseElement:
def set_ownvalue(self, name: str, value) -> None:
"""Set an ownvalue for name"""
name = self.lookup_name(name)
- self.add_rule(name, Rule(Symbol(name), value))
+ self.add_rule(name, RewriteRule(Symbol(name), value))
self.clear_cache(name)
def set_options(self, name: str, options) -> None:
diff --git a/mathics/core/evaluation.py b/mathics/core/evaluation.py
index 06e0e3be3..c3c09dae0 100644
--- a/mathics/core/evaluation.py
+++ b/mathics/core/evaluation.py
@@ -214,7 +214,7 @@ def evaluate(self, query, timeout=None, format=None):
"""
from mathics.core.convert.expression import to_expression
from mathics.core.expression import Expression
- from mathics.core.rules import Rule
+ from mathics.core.rules import RewriteRule
self.start_time = time.time()
self.iteration_count = 0
@@ -242,7 +242,7 @@ def check_io_hook(hook):
def evaluate():
if history_length > 0:
self.definitions.add_rule(
- "In", Rule(to_expression("In", line_no), query)
+ "In", RewriteRule(to_expression("In", line_no), query)
)
if check_io_hook("System`$Pre"):
self.last_eval = Expression(SymbolPre, query).evaluate(self)
@@ -260,7 +260,8 @@ def evaluate():
stored_result = self.get_stored_result(out_result, output_forms)
self.definitions.add_rule(
- "Out", Rule(Expression(SymbolOut, Integer(line_no)), stored_result)
+ "Out",
+ RewriteRule(Expression(SymbolOut, Integer(line_no)), stored_result),
)
if self.last_eval != self.SymbolNull:
if check_io_hook("System`$PrePrint"):
diff --git a/mathics/core/expression.py b/mathics/core/expression.py
index 11256aeb5..a4d409d1a 100644
--- a/mathics/core/expression.py
+++ b/mathics/core/expression.py
@@ -838,7 +838,9 @@ def get_option_values(
if allow_symbols and symbol_name:
option_values.update(evaluation.definitions.get_options(symbol_name))
else:
- if not option.has_form(("Rule", "RuleDelayed"), 2):
+ from mathics.core.rules import is_rule
+
+ if not is_rule(option):
if stop_on_error:
return None
else:
@@ -864,7 +866,7 @@ def get_rules_list(self) -> Optional[list]:
If the expression is of the form {pat1->expr1,... {pat_2,expr2},...}
return a (python) list of rules.
"""
- from mathics.core.rules import Rule
+ from mathics.core.rules import RewriteRule, is_rule
from mathics.core.symbols import SymbolList
list_expr = self.flatten_with_respect_to_head(SymbolList)
@@ -875,9 +877,9 @@ def get_rules_list(self) -> Optional[list]:
list.append(list_expr)
rules = []
for item in list:
- if not item.has_form(("Rule", "RuleDelayed"), 2):
+ if not is_rule(item):
return None
- rule = Rule(item.elements[0], item.elements[1])
+ rule = RewriteRule(item.elements[0], item.elements[1])
rules.append(rule)
return rules
@@ -1573,10 +1575,12 @@ def to_sympy(self, **kwargs):
return expression_to_sympy(self, **kwargs)
def process_style_box(self, options):
+ from mathics.core.rules import RewriteRule
+
if self.has_form("StyleBox", 1, None):
rules = self._elements[1:]
for rule in rules:
- if rule.has_form("Rule", 2):
+ if isinstance(rule, RewriteRule):
name = rule._elements[0].get_name()
value = rule._elements[1]
if name == "System`ShowStringCharacters":
diff --git a/mathics/core/rules.py b/mathics/core/rules.py
index 7439279b3..1cc0e0bd4 100644
--- a/mathics/core/rules.py
+++ b/mathics/core/rules.py
@@ -3,7 +3,7 @@
program.
Expressions which are transformed by rewrite rules (AKA transformation
-rules) are handed by the `Rule` class.
+rules) are handled by the `Rule` class.
There are also rules for how to match, assign function parameter
arguments, and then apply a Python "evaluation" function to a Mathics3 Expression.
@@ -15,7 +15,7 @@
For example, suppose that we try to apply rule `F[x_]->x^2` to the expression `F[2]`. The pattern part of the rule,`F[x_]` matches
the expression, `Blank[x]` (or `x_`) is replaced by `2`, giving the substitution expression `2^2`. Evaluation then stops
-looking for other rules to be applied over `F[2]`.
+looking for other rules to be applied to `F[2]`.
On the other hand, suppose that we define a `FunctionApplyRule` that associates `F[x_]` with the function:
@@ -29,7 +29,7 @@ def eval_f(self, x, evaluation) -> Optional[Expression]:
return Expression(SymbolPower, x, Integer2)
return None
-Then, if we apply the rule to `F[2]`, the function is evaluated returning `None`. Then, in the evaluation loop, we get the same
+Then, if we apply the rule to `F[2]`, the function is evaluated, returning `None`. Then, in the evaluation loop, we get the same
effect as if the pattern didn't match with the expression. The loop continues then with the next rule associated with `F`.
Why do things this way?
@@ -39,19 +39,18 @@ def eval_f(self, x, evaluation) -> Optional[Expression]:
F[x_/;(G[x]>0)]:=G[x]
with G[x] a computationally expensive function. To decide if G[x] is larger than 0, we need to evaluate it,
-and once we have evaluated it, just need to return its value.
+and once we have evaluated it, we just need to return its value.
Also, this allows us to handle several rules in the same function, without relying on our very slow pattern-matching routines.
-In particular, this is used for for some critical low-level tasks like building lists in iterators, processing arithmetic expressions,
+In particular, this is used for some critical low-level tasks like building lists in iterators, processing arithmetic expressions,
plotting functions, or evaluating derivatives and integrals.
"""
-
from abc import ABC
from inspect import signature
from itertools import chain
-from typing import Callable, Optional
+from typing import Any, Callable, Optional
from mathics.core.element import BaseElement
from mathics.core.evaluation import Evaluation
@@ -69,6 +68,24 @@ def function_arguments(f):
return _python_function_arguments(f)
+def is_rule(element: Any, include_delayed: bool = True) -> bool:
+ """
+ Return True if element is a RewriteRule, typically called a "Rule" in WMA.
+ In Mathics3, a hard-coded FunctionApplyRule can invoke a Python function.
+ This kind of rule is not detected here.
+
+ The parameter "included_delayed" indicates whether we allow Delayed Rules,
+ the default is True.
+ """
+ # FIXME: remove the test on has_form("Rule") when by fixing up
+ # class Rule_ in mathics.core.builtins.
+ return (
+ isinstance(element, RewriteRule) or element.has_form(("Rule", "RuleDelayed"), 2)
+ if include_delayed
+ else isinstance(element, RewriteRule)
+ )
+
+
class StopGenerator_BaseRule(StopGenerator):
"""
Signals that there are no more rules to check for pattern matching
@@ -80,7 +97,7 @@ class StopGenerator_BaseRule(StopGenerator):
class RuleApplicationFailed(Exception):
"""
Exception raised when a condition fails
- in the RHS, indicating that the match have failed.
+ in the RHS, indicating that the match has failed.
"""
pass
@@ -88,14 +105,14 @@ class RuleApplicationFailed(Exception):
class BaseRule(KeyComparable, ABC):
"""This is the base class from which the FunctionApplyRule and
- Rule classes are derived from.
+ RewriteRule classes are derived from.
Rules are part of the rewriting system of Mathics3. See
https://en.wikipedia.org/wiki/Rewriting
This class is not complete in of itself; subclasses must adapt or
fill in what is needed. In particular either ``apply_rule()`` or
- ``apply_function()`` need to be implemented.
+ ``apply_function()`` needs to be implemented.
Note: we want Rules to be serializable so that we can dump and
restore Rules in order to make startup time faster.
@@ -104,15 +121,12 @@ class BaseRule(KeyComparable, ABC):
def __init__(
self,
pattern: BaseElement,
- system: bool = False,
- evaluation: Optional[Evaluation] = None,
attributes: Optional[int] = None,
) -> None:
self.location: Optional[Callable] = None
self.pattern = BasePattern.create(
- pattern, attributes=attributes, evaluation=evaluation
+ pattern, attributes=attributes, evaluation=None
)
- self.system = system
def apply(
self,
@@ -210,9 +224,6 @@ def apply_function(
):
raise NotImplementedError
- def get_replace_value(self) -> BaseElement:
- raise ValueError
-
@property
def element_order(self) -> tuple:
"""
@@ -220,7 +231,18 @@ def element_order(self) -> tuple:
of an expression. The tuple is ultimately compared lexicographically.
"""
# FIXME: check if this makes sense:
- return tuple((self.system, self.pattern.element_order))
+ # True used to be self.system. Can we remove True?
+ return tuple((True, self.pattern.element_order))
+
+ def get_replace_value(self) -> BaseElement:
+ raise ValueError
+
+ @property
+ def lhs(self) -> BasePattern:
+ """
+ Lefthand side of a rule. Also known as its "pattern".
+ """
+ return self.pattern
@property
def pattern_precedence(self) -> tuple:
@@ -229,17 +251,27 @@ def pattern_precedence(self) -> tuple:
which pattern to select when several match.
"""
# FIXME: check if this makes sense:
- return tuple((self.system, self.pattern.pattern_precedence))
+ # True used to be self.system. Can we remove True?
+ return tuple((True, self.pattern.pattern_precedence))
+
+ @property
+ def rhs(self):
+ """
+ Right-hand side of a rule. Also known as its "replacement".
+ """
+ raise NotImplementedError
+
+
+class RewriteRule(BaseRule):
+ """A RewriteRule in WMA is called simply a "Rule".
+ Rules that are user-visible. In Mathics3, we also have rules to
+ perform function application, FunctionApplyRule.
-# FIXME: Given what is stated in the docstring below,
-# the class name would be better called RewriteRule, instead of the
-# more generic term Rule.
-class Rule(BaseRule):
- """There are two kinds of Rules. This kind of is a rewrite rule
- and transforms an Expression into another Expression based on the
- pattern and a replacement term and doesn't involve function
- application.
+ A RewriteRule transforms an Expression into another Expression
+ based on the pattern and a replacement term; "pattern" and "
+ replacement" are sometimes called the "lhs" and "rhs",
+ respectively.
In contrast to FunctionApplyRule[], rule application cannot force
a reevaluation of the expression when the rewrite/apply/eval step
@@ -247,30 +279,33 @@ class Rule(BaseRule):
Here is an example of a Rule::
- F[x_] -> x^2 (* The same thing as: Rule[x_, x^2] *)
+ F[x_] -> x^2 (* The same thing as: Rule[x_, x^2] *)
``F[x_]`` is a pattern and ``x^2`` is the replacement term. When
applied to the expression ``G[F[1.], F[a]]`` the result is
``G[1.^2, a^2]``
- Note: we want Rules to be serializable so that we can dump and
- restore Rules in order to make startup time faster.
+ Rewrite rules can also be mapping objects when the lhs is a
+ constant. These are individual key-value pairs that form in
+ Association-like data such as InformationData.
+ Note: we want Rules to be serializable so that we can dump and
+ restore Rules of classes of builtin function to be loaded via
+ lazy loading.
"""
def __init__(
self,
- pattern: BaseElement,
+ pattern: BaseElement, # Note: a constant value is also a "pattern".
replace: BaseElement,
- system=False,
- evaluation: Optional[Evaluation] = None,
attributes: Optional[int] = None,
) -> None:
- super(Rule, self).__init__(
- pattern, system=system, evaluation=evaluation, attributes=attributes
- )
+ super(RewriteRule, self).__init__(pattern, attributes=None)
self.replace = replace
+ def __repr__(self) -> str:
+ return " %s>" % (self.pattern, self.replace)
+
def apply_rule(
self, expression: BaseElement, vars: dict, options: dict, evaluation: Evaluation
):
@@ -311,8 +346,14 @@ def get_replace_value(self) -> BaseElement:
"""return the replace value"""
return self.replace
- def __repr__(self) -> str:
- return " %s>" % (self.pattern, self.replace)
+ # This will probably be needed when we use with builtin Rule_
+ # @property
+ # def is_literal(self):
+ # """
+ # Mathics3 FunctionApply Rules are literal do not need to be revaluated.
+ # """
+ # print("ReplaceRule WOOT")
+ # return self.pattern.is_literal and self.replace.is_literal
@property
def pattern_precedence(self) -> tuple:
@@ -325,13 +366,19 @@ def pattern_precedence(self) -> tuple:
sort_key_list = list(sort_key)
sort_key_list[0] = sort_key_list[0] & PATTERN_SORT_KEY_CONDITIONAL
sort_key = tuple(sort_key_list)
+
+ # True used to be self.system. Can we remove True?
return tuple(
(
- self.system,
+ True,
sort_key,
)
)
+ @property
+ def rhs(self):
+ return self.replace
+
class FunctionApplyRule(BaseRule):
"""
@@ -382,13 +429,9 @@ def __init__(
pattern: Expression,
function: Callable,
check_options: Optional[Callable],
- system: bool = False,
- evaluation: Optional[Evaluation] = None,
attributes: Optional[int] = None,
) -> None:
- super(FunctionApplyRule, self).__init__(
- pattern, system=system, attributes=attributes, evaluation=evaluation
- )
+ super(FunctionApplyRule, self).__init__(pattern, attributes=attributes)
self.name = name
self.location = self.function = function
self.check_options = check_options
@@ -431,3 +474,7 @@ def __getstate__(self):
def __setstate__(self, dict):
self.__dict__.update(dict) # update attributes
+
+ @property
+ def rhs(self):
+ return self.function
diff --git a/mathics/doc/online.py b/mathics/doc/online.py
index d59d1dae6..10097bdae 100644
--- a/mathics/doc/online.py
+++ b/mathics/doc/online.py
@@ -24,7 +24,7 @@ def online_doc_string(
# First look at user definitions:
for rulemsg in ruleusage:
if rulemsg.pattern.expr.get_elements()[1].__str__() == '"usage"':
- usagetext = rulemsg.get_replace_value().to_python(string_quotes=False)
+ usagetext = rulemsg.rhs.to_python(string_quotes=False)
if not is_long_form and usagetext:
return usagetext
diff --git a/mathics/eval/assignments/assignment.py b/mathics/eval/assignments/assignment.py
index 07cda6447..ef9804a8a 100644
--- a/mathics/eval/assignments/assignment.py
+++ b/mathics/eval/assignments/assignment.py
@@ -26,7 +26,7 @@
set_python_recursion_limit,
)
from mathics.core.expression import Expression
-from mathics.core.rules import Rule
+from mathics.core.rules import RewriteRule
from mathics.core.symbols import (
Atom,
Symbol,
@@ -401,7 +401,7 @@ def eval_assign_default(
tags = process_tags_and_upset_dont_allow_custom(
tags, upset, self, lhs, lhs_reference, evaluation
)
- rule = Rule(lhs, rhs)
+ rule = RewriteRule(lhs, rhs)
for tag in tags:
if rejected_because_protected(self, lhs, tag, evaluation):
continue
@@ -536,7 +536,7 @@ def eval_assign_format(
tags = process_tags_and_upset_dont_allow_custom(
tags, upset, self, lhs, lhs_reference, evaluation
)
- rule = Rule(lhs, rhs)
+ rule = RewriteRule(lhs, rhs)
for tag in tags:
if rejected_because_protected(self, lhs, tag, evaluation):
continue
@@ -692,7 +692,7 @@ def eval_assign_makeboxes(
target, form = lhs.elements
# Check second argument
- makeboxes_rule = Rule(lhs, rhs, system=False)
+ makeboxes_rule = RewriteRule(lhs, rhs)
tags = [] if tags is None else tags
if upset:
tags = tags + [target.get_lookup_name()]
@@ -850,7 +850,7 @@ def eval_assign_messagename(
tags = process_tags_and_upset_dont_allow_custom(
tags, upset, self, lhs, lhs_reference, evaluation
)
- rule = Rule(lhs, rhs)
+ rule = RewriteRule(lhs, rhs)
for tag in tags:
# Messages can be assigned even if the symbol is protected...
# if rejected_because_protected(self, lhs, tag, evaluation):
@@ -1059,7 +1059,7 @@ def eval_assign_n(
tags, upset, self, lhs, lhs_reference, evaluation
)
count = 0
- rule = Rule(lhs, rhs)
+ rule = RewriteRule(lhs, rhs)
for tag in tags:
if rejected_because_protected(self, lhs, tag, evaluation):
continue
@@ -1240,7 +1240,7 @@ def eval_assign_store_rules_by_tag(
# some combinatorica tests fail.
# Also, should not be at the beginning?
count = 0
- rule = Rule(lhs, rhs)
+ rule = RewriteRule(lhs, rhs)
position = "upvalues" if upset else None
for tag in tags:
if rejected_because_protected(self, lhs, tag, evaluation, False):
@@ -1284,7 +1284,7 @@ def eval_assign_to_symbol(
tag = lhs_reference.get_name()
if rejected_because_protected(self, lhs, tag, evaluation, ignore_protection):
return False
- evaluation.definitions.add_rule(tag, Rule(lhs, rhs), position="ownvalues")
+ evaluation.definitions.add_rule(tag, RewriteRule(lhs, rhs), position="ownvalues")
return True
diff --git a/mathics/eval/debug_tuning/dialog.py b/mathics/eval/debug_tuning/dialog.py
index a8d7fd84c..5f0305ea8 100644
--- a/mathics/eval/debug_tuning/dialog.py
+++ b/mathics/eval/debug_tuning/dialog.py
@@ -1,9 +1,9 @@
from mathics import settings
from mathics.core.atoms import Integer
from mathics.core.element import BaseElement
-from mathics.core.evaluation import Evaluation, Result
+from mathics.core.evaluation import Evaluation
from mathics.core.expression import Expression
-from mathics.core.rules import Rule
+from mathics.core.rules import RewriteRule
from mathics.core.systemsymbols import SymbolOut
from mathics.repl import interactive_eval_loop
@@ -29,7 +29,9 @@ def eval_Dialog(expr: BaseElement | None, evaluation: Evaluation):
definitions = evaluation.definitions
definitions.add_rule(
"Out",
- Rule(Expression(SymbolOut, Integer(saved_line_number)), result.last_eval),
+ RewriteRule(
+ Expression(SymbolOut, Integer(saved_line_number)), result.last_eval
+ ),
)
shell.print_result(result)
diff --git a/mathics/eval/functional/apply_fns_to_lists.py b/mathics/eval/functional/apply_fns_to_lists.py
index ce8015b90..b42ccdb23 100644
--- a/mathics/eval/functional/apply_fns_to_lists.py
+++ b/mathics/eval/functional/apply_fns_to_lists.py
@@ -10,6 +10,7 @@
from mathics.core.exceptions import PartRangeError
from mathics.core.expression import Expression
from mathics.core.list import ListExpression
+from mathics.core.rules import is_rule
from mathics.core.symbols import SymbolTrue
from mathics.core.systemsymbols import SymbolMapAt
from mathics.eval.testing_expressions import eval_ArrayQ
@@ -38,7 +39,7 @@ def map_at_replace_one(elements: Iterable, index: ListExpression, i: int) -> lis
raise PartRangeError
new_elements = list(elements)
replace_element = elements[j]
- if replace_element.has_form(("Rule", "RuleDelayed"), 2):
+ if is_rule(replace_element):
new_elements[j] = Expression(
replace_element.get_head(),
replace_element.elements[0],
diff --git a/mathics/eval/inference.py b/mathics/eval/inference.py
index b8cd61e42..1c249a2a2 100644
--- a/mathics/eval/inference.py
+++ b/mathics/eval/inference.py
@@ -7,7 +7,7 @@
from mathics.core.expression import Expression
from mathics.core.parser import parse_builtin_rule
from mathics.core.parser.util import SystemDefinitions
-from mathics.core.rules import Rule
+from mathics.core.rules import RewriteRule
from mathics.core.symbols import Atom, Symbol, SymbolFalse, SymbolTrue
from mathics.core.systemsymbols import SymbolAnd, SymbolEqual, SymbolNot, SymbolOr
@@ -98,14 +98,12 @@ def ensure_logical_algebraic_rules():
for pattern, replace in logical_algebraic_rules_spec.items():
pattern = parse_builtin_rule(pattern, SystemDefinitions())
logical_algebraic_rules.append(
- Rule(pattern, parse_builtin_rule(replace), system=True)
+ RewriteRule(pattern, parse_builtin_rule(replace))
)
remove_not_rules = []
for pattern, replace in remove_not_rules_spec.items():
pattern = parse_builtin_rule(pattern, SystemDefinitions())
- remove_not_rules.append(
- Rule(pattern, parse_builtin_rule(replace), system=True)
- )
+ remove_not_rules.append(RewriteRule(pattern, parse_builtin_rule(replace)))
return
@@ -308,43 +306,43 @@ def get_assumption_rules_dispatch(evaluation):
if value:
lhs, rhs = pat.elements
if lhs.is_numeric(evaluation):
- assumption_rules.append(Rule(rhs, lhs))
+ assumption_rules.append(RewriteRule(rhs, lhs))
else:
- assumption_rules.append(Rule(lhs, rhs))
+ assumption_rules.append(RewriteRule(lhs, rhs))
else:
- assumption_rules.append(Rule(pat, SymbolFalse))
+ assumption_rules.append(RewriteRule(pat, SymbolFalse))
symm_pat = Expression(pat._head, pat.elements[1], pat.elements[0])
- assumption_rules.append(Rule(symm_pat, SymbolFalse))
+ assumption_rules.append(RewriteRule(symm_pat, SymbolFalse))
elif pat.has_form("Equivalent", 2):
- assumption_rules.append(Rule(pat, symbol_value))
+ assumption_rules.append(RewriteRule(pat, symbol_value))
symm_pat = Expression(pat._head, pat.elements[1], pat.elements[0])
- assumption_rules.append(Rule(symm_pat, symbol_value))
+ assumption_rules.append(RewriteRule(symm_pat, symbol_value))
elif pat.has_form("Less", 2):
if value:
- assumption_rules.append(Rule(pat, SymbolTrue))
+ assumption_rules.append(RewriteRule(pat, SymbolTrue))
assumption_rules.append(
- Rule(
+ RewriteRule(
Expression(pat._head, pat.elements[1], pat.elements[0]),
SymbolFalse,
)
)
for head in (SymbolEqual, Symbol("Equivalent")):
assumption_rules.append(
- Rule(
+ RewriteRule(
Expression(head, pat.elements[0], pat.elements[1]),
SymbolFalse,
)
)
assumption_rules.append(
- Rule(
+ RewriteRule(
Expression(head, pat.elements[1], pat.elements[0]),
SymbolFalse,
)
)
else:
- assumption_rules.append(Rule(pat, SymbolFalse))
+ assumption_rules.append(RewriteRule(pat, SymbolFalse))
else:
- assumption_rules.append(Rule(pat, symbol_value))
+ assumption_rules.append(RewriteRule(pat, symbol_value))
# TODO: expand the pred and assumptions into an standard,
# atomized form, and then apply the rules...
if len(assumption_rules) == 0:
diff --git a/mathics/eval/list/associations.py b/mathics/eval/list/associations.py
index 81969c694..b369bf92c 100644
--- a/mathics/eval/list/associations.py
+++ b/mathics/eval/list/associations.py
@@ -1,6 +1,7 @@
from mathics.core.evaluation import Evaluation
from mathics.core.expression import Expression
from mathics.core.list import ListExpression
+from mathics.core.rules import is_rule
from mathics.core.systemsymbols import SymbolKeyAbsent, SymbolMissing
@@ -10,7 +11,7 @@ def eval_Lookup(assoc, key, default, evaluation: Evaluation):
if assoc.has_form("Association", None):
# Search through association elements (rules)
for element in assoc.elements:
- if element.has_form(("Rule", "RuleDelayed"), 2):
+ if is_rule(element):
if element.elements[0] == key:
return element.elements[1]
@@ -23,7 +24,7 @@ def eval_Lookup(assoc, key, default, evaluation: Evaluation):
elif isinstance(assoc, ListExpression):
# Search through list of rules
for element in assoc.elements:
- if element.has_form(("Rule", "RuleDelayed"), 2):
+ if is_rule(element):
if element.elements[0] == key:
return element.elements[1]
@@ -33,7 +34,7 @@ def eval_Lookup(assoc, key, default, evaluation: Evaluation):
else:
return Expression(SymbolMissing, SymbolKeyAbsent, key)
- elif assoc.has_form(("Rule", "RuleDelayed"), 2):
+ elif is_rule(assoc):
if assoc.elements[0] == key:
return assoc.elements[1]
return None
diff --git a/mathics/eval/rules.py b/mathics/eval/rules.py
index 7c5d24dc5..deac6070d 100644
--- a/mathics/eval/rules.py
+++ b/mathics/eval/rules.py
@@ -5,7 +5,7 @@
from mathics.core.evaluation import Evaluation
from mathics.core.expression import Expression
from mathics.core.list import ListExpression
-from mathics.core.rules import Rule
+from mathics.core.rules import RewriteRule, is_rule
from mathics.core.symbols import Atom, Symbol, SymbolList
from mathics.core.systemsymbols import SymbolDispatch, SymbolRule, SymbolRuleDelayed
from mathics.eval.parts import python_levelspec
@@ -18,7 +18,7 @@ def create_rules(
name: str,
evaluation: Evaluation,
extra_args: OptionalType[List] = None,
-) -> Union[Tuple[Union[List[Rule], BaseElement, None], bool], "Dispatch"]:
+) -> Union[Tuple[Union[List[RewriteRule], BaseElement, None], bool], "Dispatch"]:
"""
This function implements `Replace`, `ReplaceAll`, `ReplaceRepeated`
and `ReplaceList` eval methods.
@@ -91,7 +91,7 @@ def create_rules(
)
return None, True
- result.append(Rule(rule.elements[0], rule.elements[1]))
+ result.append(RewriteRule(rule.elements[0], rule.elements[1]))
return result, False
@@ -118,7 +118,7 @@ def eval_dispatch_atom(
rule = rule.evaluate(evaluation)
if rule.has_form("List", None):
flatten_list.extend(rule.elements)
- elif rule.has_form(("Rule", "RuleDelayed"), 2):
+ elif is_rule(rule):
flatten_list.append(rule)
elif isinstance(rule, Dispatch):
flatten_list.extend(rule.src.elements)
@@ -150,7 +150,7 @@ class Dispatch(Atom):
class_head_name = "System`Dispatch"
src: ListExpression
- rules: List[Rule]
+ rules: List[RewriteRule]
def __init__(
self, rule_tuple: Tuple[Expression, ...], evaluation: Evaluation
@@ -159,7 +159,7 @@ def __init__(
self.src = ListExpression(*rule_tuple)
try:
self.rules = [
- Rule(rule.elements[0], rule.elements[1]) for rule in rule_tuple
+ RewriteRule(rule.elements[0], rule.elements[1]) for rule in rule_tuple
]
except:
raise
diff --git a/mathics/format/box/numberform.py b/mathics/format/box/numberform.py
index 81a040bf3..26448870b 100644
--- a/mathics/format/box/numberform.py
+++ b/mathics/format/box/numberform.py
@@ -27,6 +27,7 @@
convert_base,
dps,
)
+from mathics.core.rules import is_rule
from mathics.core.symbols import Symbol, SymbolNull
from mathics.core.systemsymbols import (
SymbolFullForm,
@@ -373,7 +374,7 @@ def get_numberform_parameters(
options.update(default_options)
for elem in elements[::-1]:
pos = pos - 1
- if elem.has_form(("Rule", "RuleDelayed"), 2):
+ if is_rule(elem):
key, val = elem.elements
if isinstance(key, Symbol):
key_name = key.get_name()
diff --git a/mathics/format/form/outputform.py b/mathics/format/form/outputform.py
index b6697d080..a129d543f 100644
--- a/mathics/format/form/outputform.py
+++ b/mathics/format/form/outputform.py
@@ -34,6 +34,7 @@
from mathics.core.expression import BoxError, Expression
from mathics.core.list import ListExpression
from mathics.core.number import dps
+from mathics.core.rules import is_rule
from mathics.core.symbols import (
Atom,
Symbol,
@@ -162,7 +163,7 @@ def _association_outputform(expr: Expression, evaluation: Evaluation, **kwargs):
elements = expr.elements
parts = []
for element in elements:
- if not element.has_form(("Rule", "RuleDelayed"), 2):
+ if not is_rule(element):
raise _WrongFormattedExpression
parts.append(rule_to_outputform_text(element, evaluation, **kwargs))
return "<|" + ", ".join(parts) + "|>"
diff --git a/test/builtin/box/test_custom_boxexpression.py b/test/builtin/box/test_custom_boxexpression.py
index edeb4bdef..5a3d2fabf 100644
--- a/test/builtin/box/test_custom_boxexpression.py
+++ b/test/builtin/box/test_custom_boxexpression.py
@@ -55,7 +55,11 @@ def contribute(self, definitions, is_pymodule=True):
for pattern, function in self.get_functions("makeboxes_"):
mb_rule = FunctionApplyRule(
- name, pattern, function, None, attributes=None, system=True
+ name,
+ pattern,
+ function,
+ None,
+ attributes=None,
)
definitions.add_format("System`MakeBoxes", mb_rule, "_MakeBoxes")
@@ -85,9 +89,7 @@ def contribute(self, definitions, is_pymodule=True):
name = self.get_name()
for pattern, function in self.get_functions("makeboxes_"):
- mb_rule = FunctionApplyRule(
- name, pattern, function, None, attributes=None, system=True
- )
+ mb_rule = FunctionApplyRule(name, pattern, function, None, attributes=None)
definitions.add_format("System`MakeBoxes", mb_rule, "_MakeBoxes")
def init(self, *elems, **options):