Skip to content

Commit 9e286fd

Browse files
committed
MakeBoxes[Infix[...],form]-> Infix
1 parent 5e815b5 commit 9e286fd

File tree

7 files changed

+260
-185
lines changed

7 files changed

+260
-185
lines changed

mathics/builtin/colors/color_operations.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
try:
2626
import numpy
27+
import PIL.Image as PILImage
2728
import PIL.ImageOps
2829

2930
_enabled = True
@@ -377,7 +378,7 @@ def eval(self, image, n, prop, evaluation, options):
377378
im = (
378379
image.color_convert("RGB")
379380
.pil()
380-
.convert("P", palette=PIL.Image.ADAPTIVE, colors=256)
381+
.convert("P", palette=PILImage.ADAPTIVE, colors=256)
381382
)
382383
pixels = numpy.array(list(im.getdata()))
383384

mathics/builtin/drawing/image.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868

6969
import numpy
7070
import PIL
71+
import PIL.Image as PILImage
7172
import PIL.ImageEnhance
7273
import PIL.ImageFilter
7374
import PIL.ImageOps
@@ -158,7 +159,7 @@ class ImageImport(_ImageBuiltin):
158159

159160
def eval(self, path: String, evaluation: Evaluation):
160161
"""ImageImport[path_String]"""
161-
pillow = PIL.Image.open(path.value)
162+
pillow = PILImage.open(path.value)
162163
pixels = numpy.asarray(pillow)
163164
is_rgb = len(pixels.shape) >= 3 and pixels.shape[2] >= 3
164165
options_from_exif = extract_exif(pillow, evaluation)
@@ -644,7 +645,7 @@ def eval(self, image, angle, evaluation: Evaluation):
644645

645646
def rotate(im):
646647
return im.rotate(
647-
180 * py_angle / math.pi, resample=PIL.Image.BICUBIC, expand=True
648+
180 * py_angle / math.pi, resample=PILImage.BICUBIC, expand=True
648649
)
649650

650651
return image.filter(rotate)
@@ -1308,7 +1309,7 @@ def eval(self, image, n: Integer, evaluation: Evaluation):
13081309
if converted is None:
13091310
return
13101311
pixels = pixels_as_ubyte(converted.pixels)
1311-
im = PIL.Image.fromarray(pixels).quantize(py_value)
1312+
im = PILImage.fromarray(pixels).quantize(py_value)
13121313
im = im.convert("RGB")
13131314
return Image(numpy.array(im), "RGB")
13141315

@@ -2038,7 +2039,7 @@ def atom_to_boxes(self, form, evaluation: Evaluation) -> ImageBox:
20382039
pillow = deepcopy(self.pillow)
20392040
else:
20402041
pixels_format = "RGBA" if len(shape) >= 3 and shape[2] == 4 else "RGB"
2041-
pillow = PIL.Image.fromarray(pixels, pixels_format)
2042+
pillow = PILImage.fromarray(pixels, pixels_format)
20422043

20432044
# if the image is very small, scale it up using nearest neighbour.
20442045
min_size = 128
@@ -2047,7 +2048,7 @@ def atom_to_boxes(self, form, evaluation: Evaluation) -> ImageBox:
20472048
scaled_width = int(scale * width)
20482049
scaled_height = int(scale * height)
20492050
pillow = pillow.resize(
2050-
(scaled_height, scaled_width), resample=PIL.Image.NEAREST
2051+
(scaled_height, scaled_width), resample=PILImage.NEAREST
20512052
)
20522053

20532054
with warnings.catch_warnings():
@@ -2105,7 +2106,7 @@ def filter(self, f): # apply PIL filters component-wise
21052106
pixels = self.pixels
21062107
n = pixels.shape[2]
21072108
channels = [
2108-
f(PIL.Image.fromarray(c, "L")) for c in (pixels[:, :, i] for i in range(n))
2109+
f(PILImage.fromarray(c, "L")) for c in (pixels[:, :, i] for i in range(n))
21092110
]
21102111
return Image(numpy.dstack(channels), self.color_space)
21112112

@@ -2171,7 +2172,7 @@ def pil(self):
21712172
else:
21722173
raise NotImplementedError
21732174

2174-
return PIL.Image.fromarray(pixels, mode)
2175+
return PILImage.fromarray(pixels, mode)
21752176

21762177
def options(self):
21772178
return ListExpression(

mathics/builtin/layout.py

+186-7
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,32 @@
88
we can use ``Row``
99
1010
"""
11-
11+
from typing import Optional, Union
1212

1313
from mathics.builtin.base import BinaryOperator, Builtin, Operator
1414
from mathics.builtin.box.layout import GridBox, RowBox, to_boxes
1515
from mathics.builtin.lists import list_boxes
1616
from mathics.builtin.makeboxes import MakeBoxes
1717
from mathics.builtin.options import options_to_rules
18-
from mathics.core.atoms import Real, String
18+
from mathics.core.atoms import Integer, Integer1, Real, String
19+
from mathics.core.convert.op import operator_to_ascii, operator_to_unicode
20+
from mathics.core.element import BaseElement
1921
from mathics.core.expression import Expression
2022
from mathics.core.list import ListExpression
21-
from mathics.core.symbols import Symbol
22-
from mathics.core.systemsymbols import SymbolMakeBoxes
23-
from mathics.eval.makeboxes import format_element
24-
23+
from mathics.core.symbols import Atom, Symbol
24+
from mathics.core.systemsymbols import (
25+
SymbolFullForm,
26+
SymbolInfix,
27+
SymbolInputForm,
28+
SymbolLeft,
29+
SymbolMakeBoxes,
30+
SymbolNone,
31+
SymbolOutputForm,
32+
SymbolRight,
33+
)
34+
from mathics.eval.makeboxes import format_element, make_boxes_infix
35+
36+
SymbolNonAssociative = Symbol("System`NonAssociative")
2537
SymbolSubscriptBox = Symbol("System`SubscriptBox")
2638

2739

@@ -145,11 +157,178 @@ class Infix(Builtin):
145157
"""
146158

147159
messages = {
148-
"normal": "Nonatomic expression expected at position `1`",
160+
"argb": "Infix called with `1` arguments; between 1 and 4 arguments are expected.",
161+
"group": "Infix::group: Grouping specification `1` is not NonAssociative, None, Left, or Right.",
149162
"intm": "Machine-sized integer expected at position 3 in `1`",
163+
"normal": "Nonatomic expression expected at position `1`",
150164
}
151165
summary_text = "infix form"
152166

167+
# the right rule should be
168+
# mbexpression:MakeBoxes[Infix[___], form]
169+
170+
def eval_infix_1(self, expr: Expression, form: Symbol, evaluation):
171+
"""MakeBoxes[Infix[expr_],
172+
form:StandardForm|TraditionalForm|OutputForm|InputForm]"""
173+
return self.do_eval_infix(expr, String("~"), form, evaluation)
174+
175+
def eval_infix_2(
176+
self, expr: Expression, operator: BaseElement, form: Symbol, evaluation
177+
):
178+
"""MakeBoxes[Infix[expr_, operator_],
179+
form:StandardForm|TraditionalForm|OutputForm|InputForm]"""
180+
return self.do_eval_infix(expr, operator, form, evaluation)
181+
182+
def eval_infix_3(
183+
self,
184+
expr: Expression,
185+
operator: BaseElement,
186+
precedence: BaseElement,
187+
form: Symbol,
188+
evaluation,
189+
):
190+
"""MakeBoxes[Infix[expr_, operator_, precedence_],
191+
form:StandardForm|TraditionalForm|OutputForm|InputForm]"""
192+
193+
if not isinstance(precedence, Integer):
194+
evaluation.message(
195+
"Infix",
196+
"intm",
197+
Expression(
198+
SymbolFullForm, Expression(SymbolInfix, expr, operator, precedence)
199+
),
200+
)
201+
return
202+
203+
return self.do_eval_infix(expr, operator, form, evaluation, precedence.value)
204+
205+
def eval_infix_4(
206+
self,
207+
expr: Expression,
208+
operator: BaseElement,
209+
precedence: BaseElement,
210+
grouping: BaseElement,
211+
form: Symbol,
212+
evaluation,
213+
):
214+
"""MakeBoxes[Infix[expr_, operator_, precedence_, grouping_],
215+
form:StandardForm|TraditionalForm|OutputForm|InputForm]"""
216+
if not isinstance(precedence, Integer):
217+
fullform_expr = Expression(
218+
SymbolFullForm,
219+
Expression(SymbolInfix, expr, operator, precedence, grouping),
220+
)
221+
evaluation.message(
222+
"Infix",
223+
"intm",
224+
Expression(
225+
SymbolFullForm,
226+
Expression(SymbolInfix, expr, operator, precedence, grouping),
227+
),
228+
)
229+
return fullform_expr
230+
231+
if grouping is SymbolNone:
232+
return self.do_eval_infix(
233+
expr, operator, form, evaluation, precedence.value
234+
)
235+
if grouping in (SymbolNonAssociative, SymbolLeft, SymbolRight):
236+
return self.do_eval_infix(
237+
expr, operator, form, evaluation, precedence.value, grouping.get_name()
238+
)
239+
240+
evaluation.message("Infix", "argb", grouping)
241+
return Expression(
242+
SymbolFullForm,
243+
Expression(SymbolInfix, expr, operator, precedence, grouping),
244+
)
245+
246+
def eval_infix_default(self, parms, form: Symbol, evaluation):
247+
"""MakeBoxes[Infix[parms___],
248+
form:StandardForm|TraditionalForm|OutputForm|InputForm]"""
249+
evaluation.message("Infix", "argb", Integer(len(parms.get_sequence())))
250+
return None
251+
252+
def do_eval_infix(
253+
self,
254+
expr: Expression,
255+
operator: BaseElement,
256+
form: Symbol,
257+
evaluation,
258+
precedence_value: Optional[int] = None,
259+
grouping: Optional[str] = None,
260+
):
261+
"""Implements MakeBoxes[Infix[...]]"""
262+
263+
## FIXME: this should go into a some formatter.
264+
def format_operator(operator) -> Union[String, BaseElement]:
265+
"""
266+
Format infix operator `operator`. To do this outside parameter form is used.
267+
Sometimes no changes are made and operator is returned unchanged.
268+
269+
This function probably should be rewritten be more scalable across other forms
270+
and moved to a module that contiaing similar formatting routines.
271+
"""
272+
if not isinstance(operator, String):
273+
return MakeBoxes(operator, form)
274+
275+
op_str = operator.value
276+
277+
# FIXME: performing a check using the operator symbol representation feels a bit
278+
# fragile. The operator name seems more straightforward and more robust.
279+
if form == SymbolInputForm and op_str in ["*", "^", " "]:
280+
return operator
281+
elif (
282+
form in (SymbolInputForm, SymbolOutputForm)
283+
and not op_str.startswith(" ")
284+
and not op_str.endswith(" ")
285+
):
286+
# FIXME: Again, testing on specific forms is fragile and not scalable.
287+
op = String(" " + op_str + " ")
288+
return op
289+
return operator
290+
291+
if isinstance(expr, Atom):
292+
evaluation.message("Infix", "normal", Integer1)
293+
return None
294+
295+
elements = expr.elements
296+
if len(elements) > 1:
297+
if operator.has_form("List", len(elements) - 1):
298+
operator = [format_operator(op) for op in operator.elements]
299+
return make_boxes_infix(
300+
elements, operator, precedence_value, grouping, form
301+
)
302+
else:
303+
encoding_rule = evaluation.definitions.get_ownvalue(
304+
"$CharacterEncoding"
305+
)
306+
encoding = (
307+
"UTF8" if encoding_rule is None else encoding_rule.replace.value
308+
)
309+
op_str = (
310+
operator.value
311+
if isinstance(operator, String)
312+
else operator.short_name
313+
)
314+
if encoding == "ASCII":
315+
operator = format_operator(
316+
String(operator_to_ascii.get(op_str, op_str))
317+
)
318+
else:
319+
operator = format_operator(
320+
String(operator_to_unicode.get(op_str, op_str))
321+
)
322+
323+
return make_boxes_infix(
324+
elements, operator, precedence_value, grouping, form
325+
)
326+
327+
elif len(elements) == 1:
328+
return MakeBoxes(elements[0], form)
329+
else:
330+
return MakeBoxes(expr, form)
331+
153332

154333
class Left(Builtin):
155334
"""

0 commit comments

Comments
 (0)