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

Split mathics.core.assignment into mathics.core.assignment and mathics.eval.assignment #1264

Merged
merged 16 commits into from
Jan 7, 2025
Merged
148 changes: 72 additions & 76 deletions mathics/builtin/assignments/assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,8 @@
"""
Forms of Assignment
"""
from typing import Optional


from mathics.core.assignment import (
ASSIGNMENT_FUNCTION_MAP,
AssignmentException,
assign_store_rules_by_tag,
normalize_lhs,
)
from mathics.core.atoms import String
from mathics.core.attributes import (
A_HOLD_ALL,
Expand All @@ -18,45 +12,16 @@
A_SEQUENCE_HOLD,
)
from mathics.core.builtin import Builtin, InfixOperator
from mathics.core.element import BaseElement
from mathics.core.evaluation import Evaluation
from mathics.core.symbols import SymbolNull
from mathics.core.systemsymbols import SymbolFailed
from mathics.eval.assignment import _SetOperator
from mathics.eval.pymathics import PyMathicsLoadException, eval_LoadModule


class _SetOperator:
"""

This is the base class for assignment Builtin operators.

Special cases are determined by the head of the expression. Then
they are processed by specific routines, which are poke from
the ``ASSIGNMENT_FUNCTION_MAP`` dict.
"""

# FIXME:
# Assignment is determined by the LHS.
# Are there a larger patterns or natural groupings that we are missing?
# For example, it might be that it
# we can key off of some attributes or other properties of the
# LHS of a builtin, instead of listing all of the builtins in that class
# (which may miss some).
# Below, we key on a string, but Symbol is more correct.

def assign(self, lhs, rhs, evaluation, tags=None, upset=False):
lhs, lookup_name = normalize_lhs(lhs, evaluation)
try:
# Using a builtin name, find which assignment procedure to perform,
# and then call that function.
assignment_func = ASSIGNMENT_FUNCTION_MAP.get(lookup_name, None)
if assignment_func:
return assignment_func(self, lhs, rhs, evaluation, tags, upset)

return assign_store_rules_by_tag(self, lhs, rhs, evaluation, tags, upset)
except AssignmentException:
return False


# Placing this here is a bit weird, but it is not clear where else is better suited for this right now.
# Placing this here is a bit weird, but it is not clear where else is better
# suited for this right now.
class LoadModule(Builtin):
"""
## <url>:mathics native for pymathics:</url>
Expand Down Expand Up @@ -88,8 +53,8 @@ def eval(self, module, evaluation):
except PyMathicsLoadException:
evaluation.message(self.name, "notmathicslib", module)
return SymbolFailed
except Exception as e:
evaluation.message(self.get_name(), "loaderror", String(str(e)))
except ImportError as exception:
evaluation.message(self.get_name(), "loaderror", String(str(exception)))
return SymbolFailed
return module

Expand All @@ -105,7 +70,8 @@ class Set(InfixOperator, _SetOperator):
<dd>evaluates $value$ and assigns it to $expr$.

<dt>{$s1$, $s2$, $s3$} = {$v1$, $v2$, $v3$}
<dd>sets multiple symbols ($s1$, $s2$, ...) to the corresponding values ($v1$, $v2$, ...).
<dd>sets multiple symbols ($s1$, $s2$, ...) to the corresponding \
values ($v1$, $v2$, ...).
</dl>

'Set' can be used to give a symbol a value:
Expand Down Expand Up @@ -189,7 +155,9 @@ class SetDelayed(Set):
<dd>assigns $value$ to $expr$, without evaluating $value$.
</dl>

'SetDelayed' is like 'Set', except it has attribute 'HoldAll', thus it does not evaluate the right-hand side immediately, but evaluates it when needed.
'SetDelayed' is like 'Set', except it has attribute 'HoldAll', thus it \
does not evaluate the right-hand side immediately, but evaluates \
it when needed.

>> Attributes[SetDelayed]
= {HoldAll, Protected, SequenceHold}
Expand Down Expand Up @@ -253,8 +221,8 @@ def eval(self, lhs, rhs, evaluation):

if self.assign(lhs, rhs, evaluation):
return SymbolNull
else:
return SymbolFailed

return SymbolFailed


class TagSet(Builtin, _SetOperator):
Expand All @@ -277,7 +245,8 @@ class TagSet(Builtin, _SetOperator):
>> UpValues[square]
= {HoldPattern[area[square[s_]]] :> s ^ 2}

The symbol $f$ must appear as the ultimate head of $lhs$ or as the head of an element in $lhs$:
The symbol $f$ must appear as the ultimate head of $lhs$ or as the head \
of an element in $lhs$:
>> x /: f[g[x]] = 3;
: Tag x not found or too deep for an assigned rule.
>> g /: f[g[x]] = 3;
Expand All @@ -290,18 +259,27 @@ class TagSet(Builtin, _SetOperator):
messages = {
"tagnfd": "Tag `1` not found or too deep for an assigned rule.",
}
summary_text = "assign a value to an expression, associating the corresponding assignment with the a symbol"
summary_text = (
"assign a value to an expression, associating the "
"corresponding assignment with the a symbol"
)

def eval(self, f, lhs, rhs, evaluation):
"f_ /: lhs_ = rhs_"
def eval(
self,
tag: BaseElement,
lhs: BaseElement,
rhs: BaseElement,
evaluation: Evaluation,
) -> Optional[BaseElement]:
"tag_ /: lhs_ = rhs_"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, f_ is correct here since this is how the documentation describes the parameter. I will be putting in a PR for this and other changes.


name = f.get_name()
if not name:
evaluation.message(self.get_name(), "sym", f, 1)
return
tag_name = tag.get_name()
if not tag_name:
evaluation.message(self.get_name(), "sym", tag, 1)
return None

rhs = rhs.evaluate(evaluation)
self.assign(lhs, rhs, evaluation, tags=[name])
self.assign(lhs, rhs, evaluation, tags=[tag_name])
return rhs


Expand All @@ -319,20 +297,29 @@ class TagSetDelayed(TagSet):
"""

attributes = A_HOLD_ALL | A_PROTECTED | A_SEQUENCE_HOLD
summary_text = "assign a delayed value to an expression, associating the corresponding assignment with the a symbol"

def eval(self, f, lhs, rhs, evaluation):
"f_ /: lhs_ := rhs_"

name = f.get_name()
if not name:
evaluation.message(self.get_name(), "sym", f, 1)
return
summary_text = (
"assign a delayed value to an expression, associating "
"the corresponding assignment with the a symbol"
)

if self.assign(lhs, rhs, evaluation, tags=[name]):
def eval(
self,
tag: BaseElement,
lhs: BaseElement,
rhs: BaseElement,
evaluation: Evaluation,
) -> Optional[BaseElement]:
"tag_ /: lhs_ := rhs_"

tag_name = tag.get_name()
if not tag_name:
evaluation.message(self.get_name(), "sym", tag, 1)
return None

if self.assign(lhs, rhs, evaluation, tags=[tag_name]):
return SymbolNull
else:
return SymbolFailed

return SymbolFailed


class UpSet(InfixOperator, _SetOperator):
Expand All @@ -342,7 +329,8 @@ class UpSet(InfixOperator, _SetOperator):

<dl>
<dt>$f$[$x$] ^= $expression$
<dd>evaluates $expression$ and assigns it to the value of $f$[$x$], associating the value with $x$.
<dd>evaluates $expression$ and assigns it to the value of $f$[$x$], \
associating the value with $x$.
</dl>

'UpSet' creates an upvalue:
Expand All @@ -365,10 +353,12 @@ class UpSet(InfixOperator, _SetOperator):
grouping = "Right"

summary_text = (
"set value and associate the assignment with symbols that occur at level one"
"set value and associate the assignment with " "symbols that occur at level one"
)

def eval(self, lhs, rhs, evaluation):
def eval(
self, lhs: BaseElement, rhs: BaseElement, evaluation: Evaluation
) -> Optional[BaseElement]:
"lhs_ ^= rhs_"

self.assign(lhs, rhs, evaluation, upset=True)
Expand All @@ -384,7 +374,8 @@ class UpSetDelayed(UpSet):
<dt>'UpSetDelayed[$expression$, $value$]'

<dt>'$expression$ ^:= $value$'
<dd>assigns $expression$ to the value of $f$[$x$] (without evaluating $expression$), associating the value with $x$.
<dd>assigns $expression$ to the value of $f$[$x$] \
(without evaluating $expression$), associating the value with $x$.
</dl>

>> a[b] ^:= x
Expand All @@ -396,12 +387,17 @@ class UpSetDelayed(UpSet):
"""

attributes = A_HOLD_ALL | A_PROTECTED | A_SEQUENCE_HOLD
summary_text = "set a delayed value and associate the assignment with symbols that occur at level one"
summary_text = (
"set a delayed value and associate the assignment "
"with symbols that occur at level one"
)

def eval(self, lhs, rhs, evaluation):
def eval(
self, lhs: BaseElement, rhs: BaseElement, evaluation: Evaluation
) -> Optional[BaseElement]:
"lhs_ ^:= rhs_"

if self.assign(lhs, rhs, evaluation, upset=True):
return SymbolNull
else:
return SymbolFailed

return SymbolFailed
Loading
Loading