Skip to content

Commit

Permalink
Revise MapAt[] part 1 (#1238)
Browse files Browse the repository at this point in the history
Miscellaneous lint for expanding and correcting `MapAt[]` 

* add eval_MapAt (move out of builtin class), and start to address error messages more correctly
* walk_parts -> eval_Part which is what it is. And move to `mathics.eval.list.eol` since that is where it belongs
* DRY use of "psl" message
* Drop "_" at the beginning of routines which weren't really internal.
  • Loading branch information
rocky authored Dec 21, 2024
1 parent 3706cad commit b052251
Show file tree
Hide file tree
Showing 15 changed files with 425 additions and 395 deletions.
51 changes: 7 additions & 44 deletions mathics/builtin/functional/apply_fns_to_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,16 @@

from typing import Iterable

from mathics.builtin.list.constructing import List
from mathics.core.atoms import Integer, Integer3
from mathics.core.builtin import Builtin, InfixOperator
from mathics.core.convert.expression import to_mathics_list
from mathics.core.evaluation import Evaluation
from mathics.core.exceptions import (
InvalidLevelspecError,
MessageException,
PartRangeError,
)
from mathics.core.exceptions import InvalidLevelspecError, MessageException
from mathics.core.expression import Expression
from mathics.core.list import ListExpression
from mathics.core.symbols import Atom, Symbol, SymbolNull, SymbolTrue
from mathics.core.systemsymbols import SymbolMapThread, SymbolRule
from mathics.core.symbols import Atom, SymbolNull, SymbolTrue
from mathics.core.systemsymbols import SymbolMapThread
from mathics.eval.functional.apply_fns_to_lists import eval_MapAt
from mathics.eval.parts import python_levelspec, walk_levels

# This tells documentation how to sort this module
Expand Down Expand Up @@ -97,7 +93,7 @@ def callback(level):
return Expression(f, *level.elements)

heads = self.get_option(options, "Heads", evaluation) is SymbolTrue
result, depth = walk_levels(expr, start, stop, heads=heads, callback=callback)
result, _ = walk_levels(expr, start, stop, heads=heads, callback=callback)

return result

Expand Down Expand Up @@ -152,7 +148,7 @@ def callback(level):
return Expression(f, level)

heads = self.get_option(options, "Heads", evaluation) is SymbolTrue
result, depth = walk_levels(expr, start, stop, heads=heads, callback=callback)
result, _ = walk_levels(expr, start, stop, heads=heads, callback=callback)

return result

Expand Down Expand Up @@ -202,40 +198,7 @@ class MapAt(Builtin):

def eval(self, f, expr, args, evaluation: Evaluation):
"MapAt[f_, expr_, args_]"

m = len(expr.elements)

def map_at_one(i):
if 1 <= i <= m:
j = i - 1
elif -m <= i <= -1:
j = m + i
else:
raise PartRangeError
replace_element = new_elements[j]
if hasattr(replace_element, "head") and replace_element.head is Symbol(
"System`Rule"
):
new_elements[j] = Expression(
SymbolRule,
replace_element.elements[0],
Expression(f, replace_element.elements[1]),
)
else:
new_elements[j] = Expression(f, replace_element)
return new_elements

a = args.to_python()
if isinstance(a, int):
new_elements = list(expr.elements)
new_elements = map_at_one(a)
return List(*new_elements)
elif isinstance(a, list):
new_elements = list(expr.elements)
for item in a:
if len(item) == 1 and isinstance(item[0], int):
new_elements = map_at_one(item[0])
return List(*new_elements)
return eval_MapAt(f, expr, args, evaluation)


class MapIndexed(Builtin):
Expand Down
23 changes: 12 additions & 11 deletions mathics/builtin/list/eol.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@
from mathics.core.symbols import Atom, Symbol, SymbolNull, SymbolTrue
from mathics.core.systemsymbols import (
SymbolAppend,
SymbolAppendTo,
SymbolByteArray,
SymbolDrop,
SymbolFailed,
SymbolInfinity,
SymbolKey,
Expand All @@ -44,26 +46,26 @@
SymbolSelect,
SymbolSequence,
SymbolSet,
SymbolTake,
)
from mathics.eval.list.eol import (
drop_span_selector,
eval_Part,
parts,
take_span_selector,
)
from mathics.eval.lists import delete_one, delete_rec, list_boxes
from mathics.eval.parts import (
_drop_span_selector,
_take_span_selector,
deletecases_with_levelspec,
parts,
python_levelspec,
set_part,
walk_levels,
walk_parts,
)
from mathics.eval.patterns import Matcher

SymbolAppendTo = Symbol("System`AppendTo")
SymbolDeleteCases = Symbol("System`DeleteCases")
SymbolDrop = Symbol("System`Drop")
SymbolPrepend = Symbol("System`Prepend")
SymbolPrependTo = Symbol("System`PrependTo")
SymbolTake = Symbol("System`Take")


class Append(Builtin):
Expand Down Expand Up @@ -340,7 +342,6 @@ class Delete(Builtin):
# Delete *can* take more than 2 arguments.
"argr": "Delete called with 1 argument; 2 arguments are expected.",
"argt": "Delete called with `1` arguments; 2 arguments are expected.",
"psl": "Position specification `1` in `2` is not a machine-sized integer or a list of machine-sized integers.",
"pkspec": "The expression `1` cannot be used as a part specification. Use `2` instead.",
}
summary_text = "delete elements from a list at given positions"
Expand Down Expand Up @@ -586,7 +587,7 @@ def eval(self, items, seqs, evaluation: Evaluation):

try:
return parts(
items, [_drop_span_selector(seq) for seq in seq_tuple], evaluation
items, [drop_span_selector(seq) for seq in seq_tuple], evaluation
)
except MessageException as e:
e.message(evaluation)
Expand Down Expand Up @@ -1183,7 +1184,7 @@ def eval(self, list, i, evaluation):
return

# Otherwise...
result = walk_parts([list], indices, evaluation)
result = eval_Part([list], indices, evaluation)
if result:
return result

Expand Down Expand Up @@ -1688,7 +1689,7 @@ def eval(self, items, seqs, evaluation):
return

try:
return parts(items, [_take_span_selector(seq) for seq in seqs], evaluation)
return parts(items, [take_span_selector(seq) for seq in seqs], evaluation)
except MessageException as e:
e.message(evaluation)

Expand Down
1 change: 1 addition & 0 deletions mathics/builtin/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ class General(Builtin):
"pspec": (
"Part specification `1` is neither an integer nor " "a list of integer."
),
"psl": "Position specification `1` in `2` is not a machine-sized integer or a list of machine-sized integers.",
"rvalue": "`1` is not a variable with a value, so its value cannot be changed.",
"seqs": "Sequence specification expected, but got `1`.",
"setp": "Part assignment to `1` could not be made",
Expand Down
4 changes: 2 additions & 2 deletions mathics/builtin/numbers/algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@
SymbolTable,
SymbolTanh,
)
from mathics.eval.list.eol import eval_Part
from mathics.eval.numbers.algebra.simplify import eval_Simplify
from mathics.eval.numbers.numbers import cancel, sympy_factor
from mathics.eval.parts import walk_parts
from mathics.eval.patterns import match


Expand Down Expand Up @@ -868,7 +868,7 @@ def eval_list(self, polys, varlist, evaluation: Evaluation, options: dict):
if dim1 == 1 and order == 0:
arrays[0] = coeff
else:
walk_parts([curr_array], arrayidx, evaluation, coeff)
eval_Part([curr_array], arrayidx, evaluation, coeff)
arrays[order] = curr_array
return ListExpression(*arrays)

Expand Down
4 changes: 2 additions & 2 deletions mathics/builtin/sparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
SymbolSparseArray,
SymbolTable,
)
from mathics.eval.parts import walk_parts
from mathics.eval.list.eol import eval_Part


class SparseArray(Builtin):
Expand Down Expand Up @@ -135,7 +135,7 @@ def eval_normal(self, dims, default, data, evaluation: Evaluation):
for item in data.elements:
pos, val = item.elements
if pos.has_form("List", None):
walk_parts([table], pos.elements, evaluation, val)
eval_Part([table], pos.elements, evaluation, val)
return table

def find_dimensions(self, rules, evaluation: Evaluation):
Expand Down
3 changes: 1 addition & 2 deletions mathics/builtin/string/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
SymbolStringRiffle,
SymbolStringSplit,
)
from mathics.eval.list.eol import convert_seq, python_seq
from mathics.eval.makeboxes import format_element
from mathics.eval.parts import convert_seq, python_seq
from mathics.eval.strings import eval_StringFind


Expand Down Expand Up @@ -200,7 +200,6 @@ class StringInsert(Builtin):
messages = {
"string": "String expected at position `1` in `2`.",
"ins": "Cannot insert at position `1` in `2`.",
"psl": "Position specification `1` in `2` is not a machine-sized integer or a list of machine-sized integers.",
}

summary_text = "insert a string in a given position"
Expand Down
4 changes: 2 additions & 2 deletions mathics/core/assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
SymbolPattern,
SymbolRuleDelayed,
)
from mathics.eval.parts import walk_parts
from mathics.eval.list.eol import eval_Part


class AssignmentException(Exception):
Expand Down Expand Up @@ -689,7 +689,7 @@ def eval_assign_part(self, lhs, rhs, evaluation, tags, upset):
evaluation.message(self.get_name(), "noval", symbol)
return False
indices = lhs.elements[1:]
return walk_parts([rule.replace], indices, evaluation, rhs)
return eval_Part([rule.replace], indices, evaluation, rhs)


def eval_assign_random_state(self, lhs, rhs, evaluation, tags, upset):
Expand Down
2 changes: 1 addition & 1 deletion mathics/core/subexpression.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ def to_expression(self):

def replace(self, new):
"""
Assigns `new` to the subexpression, according to the logic of `mathics.core.walk_parts`
Assigns `new` to the subexpression, according to the logic of `mathics.eval.list.eol.eval_Part`
"""
if (new.has_form("List", None) or new.get_head_name() == "System`List") and len(
new.elements
Expand Down
4 changes: 4 additions & 0 deletions mathics/core/systemsymbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
SymbolAlternatives = Symbol("System`Alternatives")
SymbolAnd = Symbol("System`And")
SymbolAppend = Symbol("System`Append")
SymbolAppendTo = Symbol("System`AppendTo")
SymbolApply = Symbol("System`Apply")
SymbolArcCos = Symbol("System`ArcCos")
SymbolArcSin = Symbol("System`ArcSin")
Expand Down Expand Up @@ -80,6 +81,7 @@
SymbolDispatch = Symbol("System`Dispatch")
SymbolDot = Symbol("System`Dot")
SymbolDownValues = Symbol("System`DownValues")
SymbolDrop = Symbol("System`Drop")
SymbolE = Symbol("System`E")
SymbolEdgeForm = Symbol("System`EdgeForm")
SymbolEndOfFile = Symbol("System`EndOfFile")
Expand Down Expand Up @@ -146,6 +148,7 @@
SymbolMachinePrecision = Symbol("System`MachinePrecision")
SymbolMakeBoxes = Symbol("System`MakeBoxes")
SymbolMap = Symbol("System`Map")
SymbolMapAt = Symbol("System`MapAt")
SymbolMapThread = Symbol("System`MapThread")
SymbolMatchQ = Symbol("System`MatchQ")
SymbolMatrixQ = Symbol("System`MatrixQ")
Expand Down Expand Up @@ -266,6 +269,7 @@
SymbolSubsuperscriptBox = Symbol("System`SubsuperscriptBox")
SymbolSuperscriptBox = Symbol("System`SuperscriptBox")
SymbolTable = Symbol("System`Table")
SymbolTake = Symbol("System`Take")
SymbolTan = Symbol("System`Tan")
SymbolTanh = Symbol("System`Tanh")
SymbolTeXForm = Symbol("System`TeXForm")
Expand Down
Empty file.
57 changes: 57 additions & 0 deletions mathics/eval/functional/apply_fns_to_lists.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
Evaluation routines for mathics.builtin.functional.appy_fns_to_lists
"""
from mathics.core.atoms import Integer
from mathics.core.evaluation import Evaluation
from mathics.core.exceptions import PartRangeError
from mathics.core.expression import Expression
from mathics.core.list import ListExpression
from mathics.core.symbols import Symbol
from mathics.core.systemsymbols import SymbolMapAt, SymbolRule


def eval_MapAt(f, expr, args, evaluation: Evaluation):
m = len(expr.elements)
new_elements = list(expr.elements)

def map_at_replace_one(i: int):
if 1 <= i <= m:
j = i - 1
elif -m <= i <= -1:
j = m + i
else:
evaluation.message("MapAt", "partw", ListExpression(Integer(i)), expr)
raise PartRangeError
replace_element = new_elements[j]
if hasattr(replace_element, "head") and replace_element.head is Symbol(
"System`Rule"
):
new_elements[j] = Expression(
SymbolRule,
replace_element.elements[0],
Expression(f, replace_element.elements[1]),
)
else:
new_elements[j] = Expression(f, replace_element)

try:
if isinstance(args, Integer):
map_at_replace_one(args.value)
return ListExpression(*new_elements)
elif isinstance(args, Expression):
for item in args.elements:
# Get value for arg in expr.elemnts
# Replace value
if (
isinstance(item, Expression)
and len(item.elements) == 1
and isinstance(item.elements[0], Integer)
):
map_at_replace_one(item.elements[0].value)
return ListExpression(*new_elements)
else:
evaluation.message(
"MapAt", "psl", args, Expression(SymbolMapAt, f, expr, args)
)
except PartRangeError:
return
Empty file added mathics/eval/list/__init__.py
Empty file.
Loading

0 comments on commit b052251

Please sign in to comment.