diff --git a/mathics/builtin/box/expression.py b/mathics/builtin/box/expression.py index 6180b630f..8a0410530 100644 --- a/mathics/builtin/box/expression.py +++ b/mathics/builtin/box/expression.py @@ -83,7 +83,7 @@ def __new__(cls, *elements, **kwargs): def __init(self, *args, **kwargs): super().__init(args, kwargs) - self.boxes = [] + self.inner_box = None def do_format(self, evaluation, format): return self diff --git a/mathics/builtin/box/layout.py b/mathics/builtin/box/layout.py index d3b7a075b..b5270c369 100644 --- a/mathics/builtin/box/layout.py +++ b/mathics/builtin/box/layout.py @@ -108,8 +108,9 @@ class FormBox(BoxExpression): def init(self, *elems, **kwargs): self.box_options = kwargs self.form = elems[1] - self.boxes = elems[0] - assert isinstance(self.boxes, BoxElementMixin), f"{type(self.boxes)}" + + self.inner_box = elems[0] + assert isinstance(self.inner_box, BoxElementMixin), f"{type(self.inner_box)}" @property def elements(self): @@ -117,7 +118,7 @@ def elements(self): self._elements = elements_to_expressions( self, ( - self.boxes, + self.inner_box, self.form, ), self.box_options, @@ -276,12 +277,12 @@ class InterpretationBox(BoxExpression): summary_text = "box associated to an input expression" def __repr__(self): - result = "InterpretationBox\n " + repr(self.boxes) + result = "InterpretationBox\n " + repr(self.inner_box) result += f"\n {self.box_options}" return result def init(self, *expr, **options): - self.boxes = expr[0] + self.inner_box = expr[0] self.expr = expr[1] self.box_options = options @@ -291,7 +292,7 @@ def elements(self): self._elements = elements_to_expressions( self, ( - self.boxes, + self.inner_box, self.expr, ), self.box_options, @@ -321,11 +322,11 @@ def eval_to_expression2(self, boxexpr, form, evaluation): def eval_display(self, boxexpr, evaluation): """DisplayForm[boxexpr_InterpretationBox]""" - return boxexpr.boxes + return boxexpr.inner_box @property def is_multiline(self) -> bool: - return self.boxes.is_multiline + return self.inner_box.is_multiline class PaneBox(BoxExpression): @@ -349,12 +350,12 @@ class PaneBox(BoxExpression): def elements(self): if self._elements is None: self._elements = elements_to_expressions( - self, (self.boxes,), self.box_options + self, (self.inner_box,), self.box_options ) return self._elements def init(self, expr, **options): - self.boxes = expr + self.inner_box = expr self.box_options = options def eval_panebox1(self, expr, evaluation, options): @@ -373,7 +374,7 @@ def eval_display(boxexpr, evaluation): @property def is_multiline(self) -> bool: - return self.boxes.is_multiline + return self.inner_box.is_multiline class RowBox(BoxExpression): @@ -555,7 +556,7 @@ def __repr__(self): def elements(self): if self._elements is None: style = self.style - boxes = self.boxes + boxes = self.inner_box if style: self._elements = elements_to_expressions( self, (boxes, style), self.box_options @@ -579,27 +580,27 @@ def eval_style(self, boxes, style, evaluation: Evaluation, options: dict): return StyleBox(boxes, style=style, **options) def get_string_value(self) -> str: - box = self.boxes + box = self.inner_box if isinstance(box, String): return box.value return "" - def init(self, boxes, style=None, **options): + def init(self, box, style=None, **options): # This implementation supersedes Expression.process_style_box - if isinstance(boxes, StyleBox): - options.update(boxes.box_options) - boxes = boxes.boxes + if isinstance(box, StyleBox): + options.update(box.box_options) + box = box.inner_box self.style = style self.box_options = options assert options is not None - self.boxes = boxes + self.inner_box = box assert isinstance( - self.boxes, BoxElementMixin - ), f"{type(self.boxes)},{self.boxes}" + self.inner_box, BoxElementMixin + ), f"{type(self.inner_box)},{self.inner_box}" @property def is_multiline(self) -> bool: - return self.boxes.is_multiline + return self.inner_box.is_multiline class SubscriptBox(BoxExpression): @@ -759,8 +760,8 @@ class TagBox(BoxExpression): def init(self, *elems, **kwargs): self.box_options = kwargs self.form = elems[1] - self.boxes = elems[0] - assert isinstance(self.boxes, BoxElementMixin), f"{type(self.boxes)}" + self.inner_box = elems[0] + assert isinstance(self.inner_box, BoxElementMixin), f"{type(self.inner_box)}" @property def elements(self): @@ -768,7 +769,7 @@ def elements(self): self._elements = elements_to_expressions( self, ( - self.boxes, + self.inner_box, self.form, ), self.box_options, @@ -784,7 +785,7 @@ def eval_tagbox(self, expr, form: Symbol, evaluation: Evaluation): @property def is_multiline(self) -> bool: - return self.boxes.is_multiline + return self.inner_box.is_multiline class TemplateBox(BoxExpression): diff --git a/mathics/core/element.py b/mathics/core/element.py index ec0161cb7..231e4ce77 100644 --- a/mathics/core/element.py +++ b/mathics/core/element.py @@ -411,19 +411,19 @@ class BoxElementMixin(ImmutableValueMixin): def is_multiline(self) -> bool: return True - def boxes_to_format(self, format: str, **options) -> str: - from mathics.core.formatter import boxes_to_format + def box_to_format(self, format: str, **options) -> str: + from mathics.core.formatter import box_to_format - return boxes_to_format(self, format, **options) + return box_to_format(self, format, **options) def boxes_to_mathml(self, **options) -> str: """For compatibility deprecated""" - return self.boxes_to_format("mathml", **options) + return self.box_to_format("mathml", **options) def boxes_to_tex(self, **options) -> str: """For compatibility deprecated""" - return self.boxes_to_format("latex", **options) + return self.box_to_format("latex", **options) def boxes_to_text(self, **options) -> str: """For compatibility deprecated""" - return self.boxes_to_format("text", **options) + return self.box_to_format("text", **options) diff --git a/mathics/core/formatter.py b/mathics/core/formatter.py index 7ff54b4f0..227e4602f 100644 --- a/mathics/core/formatter.py +++ b/mathics/core/formatter.py @@ -54,34 +54,22 @@ def replace(match): return text -extra_operators = set( - ( - ",", - "(", - ")", - "[", - "]", - "{", - "}", - "\u301a", - "\u301b", - "\u00d7", - "\u2032", - "\u2032\u2032", - " ", - "\u2062", - "\u222b", - "\u2146", - ) -) +def box_to_format(box, format: str, **options) -> str: # Maybe Union[str, bytearray] + """ + Translates a box structure ``box`` to a file format ``format``. + This is used only at the root Box of a boxed expression. + """ + options["format_type"] = format + return convert_box_to_format(box, **options) -def boxes_to_format(boxes, format, **options) -> str: # Maybe Union[str, bytearray] +def convert_box_to_format(box, **options) -> str: """ - Translates a box structure ``boxes`` to a file format ``format``. - + Translates a box structure ``box`` to a file format ``format``. + This is used at either non-root-level boxes or from the + initial call from box_to_format. """ - return lookup_method(boxes, format)(boxes, **options) + return lookup_method(box, options["format_type"])(box, **options) def lookup_method(self, format: str) -> Callable: diff --git a/mathics/format/render/latex.py b/mathics/format/render/latex.py index c71dda479..f8238c5a8 100644 --- a/mathics/format/render/latex.py +++ b/mathics/format/render/latex.py @@ -239,14 +239,14 @@ def render(format, string_, in_text=False): def interpretation_box(box: InterpretationBox, **options): - return lookup_conversion_method(box.boxes, "latex")(box.boxes, **options) + return lookup_conversion_method(box.inner_box, "latex")(box.inner_box, **options) add_conversion_fn(InterpretationBox, interpretation_box) def pane_box(box: PaneBox, **options): - content = lookup_conversion_method(box.boxes, "latex")(box.boxes, **options) + content = lookup_conversion_method(box.inner_box, "latex")(box.inner_box, **options) options = box.box_options size = options.get("System`ImageSize", SymbolAutomatic).to_python() @@ -487,7 +487,9 @@ def rowbox(box: RowBox, **options) -> str: def stylebox(box: StyleBox, **options) -> str: # Note: values set in `options` take precedence over `box_options` child_options = {**box.box_options, **options} - return lookup_conversion_method(box.boxes, "latex")(box.boxes, **child_options) + return lookup_conversion_method(box.inner_box, "latex")( + box.inner_box, **child_options + ) add_conversion_fn(StyleBox, stylebox) @@ -775,7 +777,7 @@ def graphics3dbox(box: Graphics3DBox, elements=None, **options) -> str: def tag_and_form_box(box: BoxExpression, **options): - return lookup_conversion_method(box.boxes, "latex")(box.boxes, **options) + return lookup_conversion_method(box.inner_box, "latex")(box.inner_box, **options) add_conversion_fn(FormBox, tag_and_form_box) diff --git a/mathics/format/render/mathml.py b/mathics/format/render/mathml.py index c1ff6733c..673af2135 100644 --- a/mathics/format/render/mathml.py +++ b/mathics/format/render/mathml.py @@ -124,14 +124,16 @@ def render(format, string): def interpretation_box(box: InterpretationBox, **options): - return lookup_conversion_method(box.boxes, "mathml")(box.boxes, **options) + return lookup_conversion_method(box.inner_box, "mathml")(box.inner_box, **options) add_conversion_fn(InterpretationBox, interpretation_box) def pane_box(box: PaneBox, **options): - content = lookup_conversion_method(box.boxes, "mathml")(box.boxes, **options) + content = lookup_conversion_method(box.inner_box, "mathml")( + box.inner_box, **options + ) options = box.box_options size = options.get("System`ImageSize", SymbolAutomatic).to_python() if size is SymbolAutomatic: @@ -328,7 +330,9 @@ def is_list_interior(content): def stylebox(box: StyleBox, **options) -> str: child_options = {**options, **box.box_options} - return lookup_conversion_method(box.boxes, "mathml")(box.boxes, **child_options) + return lookup_conversion_method(box.inner_box, "mathml")( + box.inner_box, **child_options + ) add_conversion_fn(StyleBox, stylebox) @@ -337,7 +341,7 @@ def stylebox(box: StyleBox, **options) -> str: def graphicsbox(box: GraphicsBox, elements=None, **options) -> str: # FIXME: SVG is the only thing we can convert MathML into. # Handle other graphics formats. - svg_body = box.boxes_to_format("svg", **options) + svg_body = box.box_to_format("svg", **options) # mglyph, which is what we have been using, is bad because MathML standard changed. # metext does not work because the way in which we produce the svg images is also based on this outdated mglyph @@ -371,7 +375,7 @@ def graphics3dbox(box, elements=None, **options) -> str: def tag_and_form_box(box: BoxExpression, **options): - return lookup_conversion_method(box.boxes, "mathml")(box.boxes, **options) + return lookup_conversion_method(box.inner_box, "mathml")(box.inner_box, **options) add_conversion_fn(FormBox, tag_and_form_box) diff --git a/mathics/format/render/text.py b/mathics/format/render/text.py index fa83a2b86..86130580c 100644 --- a/mathics/format/render/text.py +++ b/mathics/format/render/text.py @@ -3,7 +3,6 @@ Mathics3 box rendering to plain text. """ - from mathics.builtin.box.expression import BoxExpression from mathics.builtin.box.graphics import GraphicsBox from mathics.builtin.box.graphics3d import Graphics3DBox @@ -23,20 +22,13 @@ ) from mathics.core.atoms import String from mathics.core.exceptions import BoxConstructError -from mathics.core.formatter import ( - add_conversion_fn, - lookup_method as lookup_conversion_method, -) +from mathics.core.formatter import add_conversion_fn, convert_box_to_format from mathics.core.symbols import Atom, SymbolTrue from mathics.format.box.graphics import prepare_elements as prepare_elements2d from mathics.format.box.graphics3d import prepare_elements as prepare_elements3d from mathics.format.form.util import _WrongFormattedExpression, text_cells_to_grid -def boxes_to_text(boxes, **options) -> str: - return lookup_conversion_method(boxes, "text")(boxes, **options) - - def string(s: String, **options) -> str: value = s.value show_string_characters = ( @@ -52,14 +44,14 @@ def string(s: String, **options) -> str: def interpretation_box(box: InterpretationBox, **options): - return boxes_to_text(box.boxes, **options) + return convert_box_to_format(box.inner_box, **options) add_conversion_fn(InterpretationBox, interpretation_box) def pane_box(box: PaneBox, **options): - return boxes_to_text(box.boxes, **options) + return convert_box_to_format(box.inner_box, **options) add_conversion_fn(PaneBox, pane_box) @@ -68,8 +60,8 @@ def pane_box(box: PaneBox, **options): def fractionbox(box: FractionBox, **options) -> str: # Note: values set in `options` take precedence over `box_options` child_options = {**options, **box.box_options} - num_text = boxes_to_text(box.num, **child_options) - den_text = boxes_to_text(box.den, **child_options) + num_text = convert_box_to_format(box.num, **child_options) + den_text = convert_box_to_format(box.den, **child_options) if isinstance(box.num, RowBox): num_text = f"({num_text})" if isinstance(box.den, RowBox): @@ -96,11 +88,11 @@ def gridbox(box: GridBox, elements=None, **box_options) -> str: ( [ # TODO: check if this evaluation is necessary. - boxes_to_text(item, **box_options) + convert_box_to_format(item, **box_options) for item in row ] if isinstance(row, tuple) - else boxes_to_text(row, **box_options) + else convert_box_to_format(row, **box_options) ) for row in items ] @@ -119,10 +111,10 @@ def sqrtbox(box: SqrtBox, **options) -> str: child_options = {**options, **box.box_options} if box.index: return "Sqrt[%s,%s]" % ( - boxes_to_text(box.radicand, **child_options), - boxes_to_text(box.index, **child_options), + convert_box_to_format(box.radicand, **child_options), + convert_box_to_format(box.index, **child_options), ) - return "Sqrt[%s]" % (boxes_to_text(box.radicand, **child_options)) + return "Sqrt[%s]" % (convert_box_to_format(box.radicand, **child_options)) add_conversion_fn(SqrtBox, sqrtbox) @@ -135,7 +127,7 @@ def superscriptbox(box: SuperscriptBox, **options) -> str: index = box.superindex while not isinstance(index, Atom): if isinstance(index, StyleBox): - index = index.boxes + index = index.inner_box else: break if isinstance(index, FractionBox): @@ -143,8 +135,8 @@ def superscriptbox(box: SuperscriptBox, **options) -> str: fmt_str = "%s^%s" if no_parenthesize else "%s^(%s)" return fmt_str % ( - boxes_to_text(box.base, **child_options), - boxes_to_text(box.superindex, **child_options), + convert_box_to_format(box.base, **child_options), + convert_box_to_format(box.superindex, **child_options), ) @@ -155,8 +147,8 @@ def subscriptbox(box: SubscriptBox, **options) -> str: # Note: values set in `options` take precedence over `box_options` child_options = {**box.box_options, **options} return "Subscript[%s, %s]" % ( - boxes_to_text(box.base, **child_options), - boxes_to_text(box.subindex, **child_options), + convert_box_to_format(box.base, **child_options), + convert_box_to_format(box.subindex, **child_options), ) @@ -167,9 +159,9 @@ def subsuperscriptbox(box: SubsuperscriptBox, **options) -> str: # Note: values set in `options` take precedence over `box_options` child_options = {**box.box_options, **options} return "Subsuperscript[%s, %s, %s]" % ( - boxes_to_text(box.base, **child_options), - boxes_to_text(box.subindex, **child_options), - boxes_to_text(box.superindex, **child_options), + convert_box_to_format(box.base, **child_options), + convert_box_to_format(box.subindex, **child_options), + convert_box_to_format(box.superindex, **child_options), ) @@ -179,7 +171,9 @@ def subsuperscriptbox(box: SubsuperscriptBox, **options) -> str: def rowbox(box: RowBox, elements=None, **options) -> str: # Note: values set in `options` take precedence over `box_options` child_options = {**box.box_options, **options} - parts_str = [boxes_to_text(element, **child_options) for element in box.items] + parts_str = [ + convert_box_to_format(element, **child_options) for element in box.items + ] if len(parts_str) == 0: return "" if len(parts_str) == 1: @@ -209,7 +203,7 @@ def rowbox(box: RowBox, elements=None, **options) -> str: def stylebox(box: StyleBox, **options) -> str: # Note: values set in `options` take precedence over `box_options` child_options = {**box.box_options, **options} - return boxes_to_text(box.boxes, **child_options) + return convert_box_to_format(box.inner_box, **child_options) add_conversion_fn(StyleBox, stylebox) @@ -236,7 +230,7 @@ def graphics3dbox(box: Graphics3DBox, elements=None, **options) -> str: def tag_and_form_box(box: BoxExpression, **options): - return boxes_to_text(box.boxes, **options) + return convert_box_to_format(box.inner_box, **options) add_conversion_fn(FormBox, tag_and_form_box) diff --git a/test/builtin/drawing/test_plot_detail.py b/test/builtin/drawing/test_plot_detail.py index 7081a0bfb..168b57c92 100644 --- a/test/builtin/drawing/test_plot_detail.py +++ b/test/builtin/drawing/test_plot_detail.py @@ -253,7 +253,7 @@ def one_test(name: str, str_expr: str, vec: bool, svg: bool, opts: str): boxed_expr = Expression(Symbol("System`ToBoxes"), act_expr).evaluate( session.evaluation ) - act_svg = boxed_expr.boxes_to_format("svg") + act_svg = boxed_expr.box_to_format("svg") act_svg = outline_svg( act_svg, precision=2, include_text=True, include_tail=True ) @@ -271,7 +271,7 @@ def one_test(name: str, str_expr: str, vec: bool, svg: bool, opts: str): boxed_expr = Expression(Symbol("System`ToBoxes"), act_expr).evaluate( session.evaluation ) - act_svg = boxed_expr.boxes_to_format("svg") + act_svg = boxed_expr.box_to_format("svg") act_svg = inject_font_style(act_svg) cairosvg.svg2png( bytestring=act_svg.encode("utf-8"),