From a7333491c87f196c63fbc57cf24daac74f23ae62 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Wed, 21 Jan 2026 16:08:00 -0300 Subject: [PATCH 1/2] add FormBox --- mathics/builtin/box/layout.py | 44 +++++++++++++++++++++++++++++++++ mathics/format/render/latex.py | 6 +++-- mathics/format/render/mathml.py | 6 +++-- mathics/format/render/text.py | 6 +++-- 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/mathics/builtin/box/layout.py b/mathics/builtin/box/layout.py index 4c1c33c55..e532a5999 100644 --- a/mathics/builtin/box/layout.py +++ b/mathics/builtin/box/layout.py @@ -85,6 +85,50 @@ def is_constant_list(list): return True +class FormBox(BoxExpression): + """ + + :WMA link: + https://reference.wolfram.com/language/ref/FormBox.html + +
+
'FormBox[boxes, form]' +
is a low-level box construct that displays as \ + boxes and keep information about the form used to generate \ + the box representation. +
+ """ + + attributes = A_PROTECTED | A_READ_PROTECTED + summary_text = "box with an associated form" + + def init(self, *elems, **kwargs): + self.box_options = kwargs + self.form = elems[1] + self.boxed = elems[0] + assert isinstance(self.boxed, BoxElementMixin), f"{type(self.boxes)}" + + @property + def elements(self): + if self._elements is None: + self._elements = elements_to_expressions( + self, + ( + self.boxed, + self.form, + ), + self.box_options, + ) + return self._elements + + def eval_tagbox(self, expr, form: Symbol, evaluation: Evaluation): + """FormBox[expr_, form_Symbol]""" + options = {} + expr = to_boxes(expr, evaluation, options) + assert isinstance(expr, BoxElementMixin), f"{expr}" + return FormBox(expr, form, **options) + + class FractionBox(BoxExpression): """ diff --git a/mathics/format/render/latex.py b/mathics/format/render/latex.py index fe9adc75c..fc1d54163 100644 --- a/mathics/format/render/latex.py +++ b/mathics/format/render/latex.py @@ -17,6 +17,7 @@ from mathics.builtin.box.graphics import GraphicsBox from mathics.builtin.box.graphics3d import Graphics3DBox from mathics.builtin.box.layout import ( + FormBox, FractionBox, GridBox, InterpretationBox, @@ -646,8 +647,9 @@ def graphics3dbox(self, elements=None, **options) -> str: add_conversion_fn(Graphics3DBox, graphics3dbox) -def tag_box(self, **options): +def tag_and_form_box(self, **options): return lookup_conversion_method(self.boxed, "latex")(self.boxed, **options) -add_conversion_fn(TagBox, tag_box) +add_conversion_fn(FormBox, tag_and_form_box) +add_conversion_fn(TagBox, tag_and_form_box) diff --git a/mathics/format/render/mathml.py b/mathics/format/render/mathml.py index d48a2eb6f..e229e0c18 100644 --- a/mathics/format/render/mathml.py +++ b/mathics/format/render/mathml.py @@ -13,6 +13,7 @@ from mathics.builtin.box.graphics import GraphicsBox from mathics.builtin.box.graphics3d import Graphics3DBox from mathics.builtin.box.layout import ( + FormBox, FractionBox, GridBox, InterpretationBox, @@ -371,8 +372,9 @@ def graphics3dbox(self, elements=None, **options) -> str: add_conversion_fn(Graphics3DBox, graphics3dbox) -def tag_box(self, **options): +def tag_and_form_box(self, **options): return lookup_conversion_method(self.boxed, "mathml")(self.boxed, **options) -add_conversion_fn(TagBox, tag_box) +add_conversion_fn(FormBox, tag_and_form_box) +add_conversion_fn(TagBox, tag_and_form_box) diff --git a/mathics/format/render/text.py b/mathics/format/render/text.py index 59e9236c2..49a71a510 100644 --- a/mathics/format/render/text.py +++ b/mathics/format/render/text.py @@ -7,6 +7,7 @@ from mathics.builtin.box.graphics import GraphicsBox from mathics.builtin.box.graphics3d import Graphics3DBox from mathics.builtin.box.layout import ( + FormBox, FractionBox, GridBox, InterpretationBox, @@ -235,8 +236,9 @@ def graphics3dbox(self, elements=None, **options) -> str: add_conversion_fn(Graphics3DBox, graphics3dbox) -def tag_box(self, **options): +def tag_and_form_box(self, **options): return boxes_to_text(self.boxed, **options) -add_conversion_fn(TagBox, tag_box) +add_conversion_fn(FormBox, tag_and_form_box) +add_conversion_fn(TagBox, tag_and_form_box) From ba922a1a789952662fbebc6e5e0a0034f94be623 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sat, 24 Jan 2026 16:07:29 -0300 Subject: [PATCH 2/2] Makeboxes overhault (#1642) This PR does the largest step so far in making the formatting process in Mathics compatible with the one in WMA. The main change is in the sequence of formatting. Now, `MakeBoxes` rules are not `Downvalues`, of the `MakeBoxes` symbol, but are stored as `FormatValues` of the corresponding symbols. Rules in `MakeBoxes` are now restricted to call the `format_element` function, and return a Box expression that represents its input. Hence, in loading definitions, `MakeBoxes` is not a special symbol anymore. Also, the default implementation for formatting basic elements like symbols, expressions, and lists does not pass through the evaluation process until explicit rules are set by the user. --- mathics/builtin/box/layout.py | 6 +- mathics/builtin/forms/data.py | 2 +- mathics/builtin/forms/print.py | 93 ++++----- mathics/builtin/layout.py | 57 ++++- mathics/builtin/list/constructing.py | 10 +- mathics/builtin/makeboxes.py | 55 +---- mathics/builtin/patterns/defaults.py | 25 ++- mathics/core/atoms/strings.py | 15 +- mathics/core/builtin.py | 9 +- mathics/core/load_builtin.py | 5 +- mathics/doc/documentation/1-Manual.mdoc | 3 +- mathics/eval/assignments/assignment.py | 29 ++- mathics/eval/lists.py | 3 +- mathics/format/box/__init__.py | 4 - mathics/format/box/makeboxes.py | 195 ++++++++++++------ mathics/format/box/outputforms.py | 47 ++++- test/builtin/box/test_custom_boxexpression.py | 48 ++++- test/format/format_tests.yaml | 47 +++++ test/format/makeboxes_tests.yaml | 24 +-- test/format/test_makeboxes.py | 8 +- test/helper.py | 4 +- 21 files changed, 430 insertions(+), 259 deletions(-) diff --git a/mathics/builtin/box/layout.py b/mathics/builtin/box/layout.py index e532a5999..b38ab63ab 100644 --- a/mathics/builtin/box/layout.py +++ b/mathics/builtin/box/layout.py @@ -209,7 +209,7 @@ def elements(self): return self._elements def init(self, *elems, **kwargs): - self.options = kwargs + self.box_options = kwargs self.items = elems self._elements = elems @@ -217,7 +217,7 @@ def get_array(self, elements, evaluation): if not elements: raise BoxConstructError - options = self.options + options = self.box_options expr = elements[0] if not expr.has_form("List", None): @@ -514,8 +514,6 @@ class StyleBox(BoxExpression): """ options = { - "ShowStringCharacters": "False", - "ShowSpecialCharacters": "False", "$OptionSyntax": "Ignore", } attributes = A_PROTECTED | A_READ_PROTECTED diff --git a/mathics/builtin/forms/data.py b/mathics/builtin/forms/data.py index 6ce2f7d3f..6d042f27d 100644 --- a/mathics/builtin/forms/data.py +++ b/mathics/builtin/forms/data.py @@ -791,7 +791,7 @@ class MatrixForm(TableForm): in_printforms = False summary_text = "format as a matrix" - def eval_makeboxes_matrix(self, table, form, evaluation, options): + def eval_makeboxes(self, table, form, evaluation, options): """MakeBoxes[MatrixForm[table_, OptionsPattern[]], (form:StandardForm|TraditionalForm)]""" result = super().eval_makeboxes(table, form, evaluation, options) diff --git a/mathics/builtin/forms/print.py b/mathics/builtin/forms/print.py index 59efe7c56..01ca58e8f 100644 --- a/mathics/builtin/forms/print.py +++ b/mathics/builtin/forms/print.py @@ -12,19 +12,16 @@ below are the functions that appear in '$PrintForms' at startup. """ -from mathics.builtin.box.layout import InterpretationBox, StyleBox, TagBox +from mathics.builtin.box.layout import InterpretationBox, PaneBox, StyleBox from mathics.builtin.forms.base import FormBaseClass from mathics.core.atoms import String +from mathics.core.element import BaseElement +from mathics.core.evaluation import Evaluation from mathics.core.expression import Expression -from mathics.core.symbols import SymbolFalse, SymbolFullForm, SymbolTrue +from mathics.core.symbols import SymbolFalse, SymbolTrue from mathics.core.systemsymbols import SymbolInputForm, SymbolOutputForm -from mathics.format.box import ( - eval_makeboxes_fullform, - eval_makeboxes_outputform, - eval_mathmlform, - eval_texform, -) -from mathics.format.form import render_input_form +from mathics.format.box.makeboxes import is_print_form_callback +from mathics.format.form import render_input_form, render_output_form sort_order = "mathics.builtin.forms.general-purpose-forms" @@ -52,19 +49,6 @@ class FullForm(FormBaseClass): in_printforms = False summary_text = "format expression in underlying M-Expression representation" - def eval_makeboxes(self, expr, fmt, evaluation): - """MakeBoxes[FullForm[expr_], fmt_]""" - fullform_box = eval_makeboxes_fullform(expr, evaluation) - style_box = StyleBox( - fullform_box, - **{ - "System`ShowSpecialCharacters": SymbolFalse, - "System`ShowStringCharacters": SymbolTrue, - "System`NumberMarks": SymbolTrue, - }, - ) - return TagBox(style_box, SymbolFullForm) - class InputForm(FormBaseClass): r""" @@ -116,26 +100,6 @@ class InputForm(FormBaseClass): in_printforms = True summary_text = "format expression suitable for Mathics3 input" - # TODO: eventually, remove OutputForm in the second argument. - def eval_makeboxes(self, expr, evaluation): - """MakeBoxes[InputForm[expr_], StandardForm|TraditionalForm]""" - - inputform = String(render_input_form(expr, evaluation)) - inputform = StyleBox( - inputform, - **{ - "System`ShowSpecialCharacters": SymbolFalse, - "System`ShowStringCharacters": SymbolTrue, - "System`NumberMarks": SymbolTrue, - }, - ) - expr = Expression(SymbolInputForm, expr) - return InterpretationBox( - inputform, - expr, - **{"System`Editable": SymbolTrue, "System`AutoDelete": SymbolTrue}, - ) - class MathMLForm(FormBaseClass): """ @@ -169,10 +133,6 @@ class MathMLForm(FormBaseClass): summary_text = "format expression as MathML commands" - def eval_mathml(self, expr, evaluation) -> Expression: - "MakeBoxes[MathMLForm[expr_], StandardForm|TraditionalForm]" - return eval_mathmlform(expr, evaluation) - class OutputForm(FormBaseClass): """ @@ -202,13 +162,6 @@ class OutputForm(FormBaseClass): formats = {"OutputForm[s_String]": "s"} summary_text = "format expression in plain text" - def eval_makeboxes(self, expr, form, evaluation): - """MakeBoxes[OutputForm[expr_], form_]""" - pane = eval_makeboxes_outputform(expr, evaluation, form) - return InterpretationBox( - pane, Expression(SymbolOutputForm, expr), **{"System`Editable": SymbolFalse} - ) - class StandardForm(FormBaseClass): """ @@ -276,8 +229,34 @@ class TeXForm(FormBaseClass): in_printforms = True summary_text = "format expression as LaTeX commands" - def eval_tex(self, expr, evaluation) -> Expression: - "MakeBoxes[TeXForm[expr_], StandardForm|TraditionalForm]" - # TeXForm by default uses `TraditionalForm` - return eval_texform(expr, evaluation) +@is_print_form_callback("System`InputForm") +def eval_makeboxes_inputform(expr: BaseElement, evaluation: Evaluation): + """MakeBoxes[InputForm[expr_], StandardForm|TraditionalForm]""" + inputform = String(render_input_form(expr, evaluation)) + inputform = StyleBox( + inputform, + **{ + "System`ShowStringCharacters": SymbolTrue, + "System`NumberMarks": SymbolTrue, + }, + ) + expr = Expression(SymbolInputForm, expr) + return InterpretationBox( + inputform, + expr, + **{"System`Editable": SymbolTrue, "System`AutoDelete": SymbolTrue}, + ) + + +@is_print_form_callback("System`OutputForm") +def eval_makeboxes_outputform(expr: BaseElement, evaluation: Evaluation, **kwargs): + """ + Build a 2D representation of the expression using only keyboard characters. + """ + + text_outputform = str(render_output_form(expr, evaluation, **kwargs)) + pane = PaneBox(String('"' + text_outputform + '"')) + return InterpretationBox( + pane, Expression(SymbolOutputForm, expr), **{"System`Editable": SymbolFalse} + ) diff --git a/mathics/builtin/layout.py b/mathics/builtin/layout.py index a3785cce3..eb9884e32 100644 --- a/mathics/builtin/layout.py +++ b/mathics/builtin/layout.py @@ -11,13 +11,19 @@ from mathics.builtin.box.layout import GridBox, PaneBox, RowBox, to_boxes from mathics.builtin.makeboxes import MakeBoxes -from mathics.core.atoms import Real, String +from mathics.core.atoms import Integer, Real, String from mathics.core.builtin import Builtin, Operator, PostfixOperator, PrefixOperator from mathics.core.expression import Evaluation, Expression from mathics.core.list import ListExpression -from mathics.core.systemsymbols import SymbolMakeBoxes, SymbolSubscriptBox +from mathics.core.symbols import Symbol +from mathics.core.systemsymbols import ( + SymbolMakeBoxes, + SymbolPostfix, + SymbolPrefix, + SymbolSubscriptBox, +) from mathics.eval.lists import list_boxes -from mathics.format.box import format_element +from mathics.format.box import eval_infix, eval_postprefix, format_element, parenthesize class Center(Builtin): @@ -172,8 +178,20 @@ class Infix(Builtin): = a + b - c """ + rules = { + ( + "MakeBoxes[Infix[head_[elements___]], " + " f:StandardForm|TraditionalForm]" + ): ('MakeBoxes[Infix[head[elements], StringForm["~`1`~", head]], f]'), + } summary_text = "infix form" + def eval_makeboxes_infix( + self, expr, operator, precedence: Integer, grouping, form: Symbol, evaluation + ): + """MakeBoxes[Infix[expr_, operator_, precedence_:None, grouping_:None], form:StandardForm|TraditionalForm]""" + return eval_infix(self, expr, operator, precedence, grouping, form, evaluation) + class Left(Builtin): """ @@ -278,6 +296,13 @@ class Postfix(PostfixOperator): operator_display = None summary_text = "postfix form" + def eval_makeboxes_postfix(self, expr, h, precedence, form, evaluation): + """MakeBoxes[Postfix[expr_, h_, precedence_:None], + form:StandardForm|TraditionalForm]""" + return eval_postprefix( + self, SymbolPostfix, expr, h, precedence, form, evaluation + ) + class Precedence(Builtin): """ @@ -332,8 +357,20 @@ class PrecedenceForm(Builtin):
'PrecedenceForm'[$expr$, $prec$]
format $expr$ parenthesized as it would be if it contained an operator of precedence $prec$. + + >> PrecedenceForm[x/y, 12] - z + = -z + (x / y) + """ + def eval_outerprecedenceform(self, expr, precedence, form, evaluation): + """MakeBoxes[PrecedenceForm[expr_, precedence_], + form:StandardForm|TraditionalForm]""" + + py_precedence = precedence.get_int_value() + boxes = format_element(expr, evaluation, form) + return parenthesize(py_precedence, expr, boxes, True) + summary_text = "parenthesize with a precedence" @@ -370,6 +407,13 @@ class Prefix(PrefixOperator): operator_display = None summary_text = "prefix form" + def eval_makeboxes_prefix(self, expr, h, precedence, form, evaluation): + """MakeBoxes[Prefix[expr_, h_, precedence_:None], + form:StandardForm|TraditionalForm]""" + return eval_postprefix( + self, SymbolPrefix, expr, h, precedence, form, evaluation + ) + class Right(Builtin): """ @@ -456,7 +500,12 @@ class Style(Builtin): summary_text = "wrapper for styles and style options to apply" options = {"ImageSizeMultipliers": "Automatic"} - + rules = { + "MakeBoxes[Style[expr_, OptionsPattern[Style]], f_]": ( + "StyleBox[MakeBoxes[expr, f], " + "ImageSizeMultipliers -> OptionValue[ImageSizeMultipliers]]" + ), + } rules = { "MakeBoxes[Style[expr_, OptionsPattern[Style]], f_]": ( "StyleBox[MakeBoxes[expr, f], " diff --git a/mathics/builtin/list/constructing.py b/mathics/builtin/list/constructing.py index 2e084b542..9bcd1b64f 100644 --- a/mathics/builtin/list/constructing.py +++ b/mathics/builtin/list/constructing.py @@ -12,7 +12,6 @@ from itertools import permutations from typing import Optional, Tuple -from mathics.builtin.box.layout import RowBox from mathics.core.atoms import ByteArray, Integer, Integer1, is_integer_rational_or_real from mathics.core.attributes import A_HOLD_FIRST, A_LISTABLE, A_LOCKED, A_PROTECTED from mathics.core.builtin import BasePattern, Builtin, IterationFunction @@ -24,7 +23,7 @@ from mathics.core.list import ListExpression from mathics.core.symbols import Atom, Symbol from mathics.core.systemsymbols import SymbolNormal, SymbolTuples -from mathics.eval.lists import get_tuples, list_boxes +from mathics.eval.lists import get_tuples class Array(Builtin): @@ -165,13 +164,6 @@ def eval(self, elements, evaluation: Evaluation): elements_part_of_elements__ = elements.get_sequence() return ListExpression(*elements_part_of_elements__) - def eval_makeboxes(self, items, f, evaluation): - """MakeBoxes[{items___}, - (f:StandardForm|TraditionalForm)]""" - - items = items.get_sequence() - return RowBox(*list_boxes(items, f, evaluation, "{", "}")) - class Normal(Builtin): """ diff --git a/mathics/builtin/makeboxes.py b/mathics/builtin/makeboxes.py index 8fea8b539..2dea6be10 100644 --- a/mathics/builtin/makeboxes.py +++ b/mathics/builtin/makeboxes.py @@ -3,19 +3,9 @@ Low-level Format definitions """ - -from mathics.core.atoms import Integer from mathics.core.attributes import A_HOLD_ALL_COMPLETE, A_READ_PROTECTED from mathics.core.builtin import Builtin, Predefined -from mathics.core.symbols import Symbol -from mathics.format.box import ( - eval_generic_makeboxes, - eval_infix, - eval_makeboxes_fullform, - eval_postprefix, - format_element, - parenthesize, -) +from mathics.format.box import format_element # TODO: Differently from the current implementation, MakeBoxes should only # accept as its format field the symbols in `$BoxForms`. This is something to @@ -91,53 +81,15 @@ class MakeBoxes(Builtin): attributes = A_HOLD_ALL_COMPLETE rules = { - "MakeBoxes[Infix[head_[elements___]], " - " f:StandardForm|TraditionalForm]": ( - 'MakeBoxes[Infix[head[elements], StringForm["~`1`~", head]], f]' - ), "MakeBoxes[expr_]": "MakeBoxes[expr, StandardForm]", # The following rule is temporal. "MakeBoxes[expr_, form:(TeXForm|MathMLForm)]": "MakeBoxes[form[expr], StandardForm]", - ( - "MakeBoxes[(form:StandardForm|TraditionalForm)" - "[expr_], StandardForm|TraditionalForm]" - ): ("MakeBoxes[expr, form]"), - # BoxForms goes as second argument - "MakeBoxes[PrecedenceForm[expr_, prec_], f_]": "MakeBoxes[expr, f]", - "MakeBoxes[Style[expr_, OptionsPattern[Style]], f_]": ( - "StyleBox[MakeBoxes[expr, f], " - "ImageSizeMultipliers -> OptionValue[ImageSizeMultipliers]]" - ), } summary_text = "settable low-level translator from expression to display boxes" - def eval_fullform(self, expr, evaluation): - """MakeBoxes[expr_, FullForm]""" - return eval_makeboxes_fullform(expr, evaluation) - def eval_general(self, expr, f, evaluation): - """MakeBoxes[expr_, - f:TraditionalForm|StandardForm]""" - return eval_generic_makeboxes(expr, f, evaluation) - - def eval_outerprecedenceform(self, expr, precedence, form, evaluation): - """MakeBoxes[PrecedenceForm[expr_, precedence_], - form:StandardForm|TraditionalForm]""" - - py_precedence = precedence.get_int_value() - boxes = MakeBoxes(expr, form) - return parenthesize(py_precedence, expr, boxes, True) - - def eval_postprefix(self, p, expr, h, precedence, form, evaluation): - """MakeBoxes[(p:Prefix|Postfix)[expr_, h_, precedence_:None], - form:StandardForm|TraditionalForm]""" - return eval_postprefix(self, p, expr, h, precedence, form, evaluation) - - def eval_infix( - self, expr, operator, precedence: Integer, grouping, form: Symbol, evaluation - ): - """MakeBoxes[Infix[expr_, operator_, precedence_:None, grouping_:None], form:StandardForm|TraditionalForm]""" - return eval_infix(self, expr, operator, precedence, grouping, form, evaluation) + """MakeBoxes[expr_, f:TraditionalForm|StandardForm]""" + return format_element(expr, evaluation, f) class ToBoxes(Builtin): @@ -169,5 +121,6 @@ def eval(self, expr, form, evaluation): form_name = form.get_name() if form_name is None: evaluation.message("ToBoxes", "boxfmt", form) + boxes = format_element(expr, evaluation, form) return boxes diff --git a/mathics/builtin/patterns/defaults.py b/mathics/builtin/patterns/defaults.py index ca38a8ea8..828771360 100644 --- a/mathics/builtin/patterns/defaults.py +++ b/mathics/builtin/patterns/defaults.py @@ -72,8 +72,29 @@ class Optional(InfixOperator, PatternObject): } grouping = "Right" rules = { - "MakeBoxes[Verbatim[Optional][Verbatim[Pattern][symbol_Symbol, Verbatim[_]]], (f:StandardForm|TraditionalForm)]": 'MakeBoxes[symbol, f] <> "_."', - "MakeBoxes[Verbatim[Optional][Verbatim[_]], (f:StandardForm|TraditionalForm)]": '"_."', + ( + "MakeBoxes[Verbatim[Optional][" + "Verbatim[Pattern][symbol_Symbol," + "(kind:(Verbatim[Blank]|Verbatim[BlankSequence]|Verbatim[BlankNullSequence])[])]], " + "(f:StandardForm|TraditionalForm)]" + ): 'MakeBoxes[symbol, f] <> ToString[kind, f] <>"."', + ( + "MakeBoxes[Verbatim[Optional][" + "(kind:(Verbatim[Blank]|Verbatim[BlankSequence]|Verbatim[BlankNullSequence])[])], " + "(f:StandardForm|TraditionalForm)]" + ): 'ToString[kind, f]<>"."', + # Two arguments + ( + "MakeBoxes[Verbatim[Optional][" + "Verbatim[Pattern][symbol_Symbol," + "(kind:(Verbatim[Blank]|Verbatim[BlankSequence]|Verbatim[BlankNullSequence])[]), value_]], " + "(f:StandardForm|TraditionalForm)]" + ): 'RowBox[{MakeBoxes[symbol, f], ToString[kind, f], ":",MakeBoxes[value, f]}]', + ( + "MakeBoxes[Verbatim[Optional][" + "(kind:(Verbatim[Blank]|Verbatim[BlankSequence]|Verbatim[BlankNullSequence])[]), value_], " + "(f:StandardForm|TraditionalForm)]" + ): 'RowBox[{ToString[kind, f], ":", MakeBoxes[value, f]}]', } summary_text = "an optional argument with a default value" diff --git a/mathics/core/atoms/strings.py b/mathics/core/atoms/strings.py index a58a0a0fe..3243bf475 100644 --- a/mathics/core/atoms/strings.py +++ b/mathics/core/atoms/strings.py @@ -9,7 +9,7 @@ from mathics.core.element import BoxElementMixin from mathics.core.keycomparable import BASIC_ATOM_STRING_ELT_ORDER -from mathics.core.symbols import Atom, Symbol, SymbolTrue, symbol_set +from mathics.core.symbols import Atom, Symbol, SymbolFalse, SymbolTrue, symbol_set from mathics.core.systemsymbols import SymbolFullForm, SymbolInputForm SymbolString = Symbol("String") @@ -41,8 +41,17 @@ def atom_to_boxes(self, f, evaluation): inner = str(self.value) if f in SYSTEM_SYMBOLS_INPUT_OR_FULL_FORM: - inner = '"' + inner.replace("\\", "\\\\") + '"' - return _boxed_string(inner, **{"System`ShowStringCharacters": SymbolTrue}) + inner = inner.replace("\\", "\\\\") + inner = inner.replace('"', '\\"') + inner = f'"{inner}"' + return _boxed_string( + inner, + **{ + "System`NumberMarks": SymbolTrue, + "System`ShowSpecialCharacters": SymbolFalse, + "System`ShowStringCharacters": SymbolTrue, + }, + ) return String('"' + inner + '"') def do_copy(self) -> "String": diff --git a/mathics/core/builtin.py b/mathics/core/builtin.py index 09d001408..dc8a9ac0e 100644 --- a/mathics/core/builtin.py +++ b/mathics/core/builtin.py @@ -347,7 +347,7 @@ def contextify_form_name(f): """Handle adding 'System`' to a form name, unless it's "" (meaning the rule applies to all forms). """ - return "" if f == "" else ensure_context(f) + return f if f in ("", "_MakeBoxes") else ensure_context(f) if isinstance(pattern, tuple): forms, pattern = pattern @@ -383,6 +383,9 @@ def contextify_form_name(f): formatvalues[form].append( Rule(pattern, parse_builtin_rule(replace), system=True) ) + + formatvalues.setdefault("_MakeBoxes", []).extend(box_rules) + for form, formatrules in formatvalues.items(): formatrules.sort(key=lambda x: x.pattern_precedence) @@ -434,10 +437,6 @@ def contextify_form_name(f): else: definitions.builtin[name] = definition - makeboxes_def = definitions.builtin["System`MakeBoxes"] - for rule in box_rules: - makeboxes_def.add_rule(rule) - # This method is used to produce generic argument mismatch errors # (tags: "argx", "argr", "argrx", "argt", or "argtu") for builtin # functions that define this as an eval method. e.g. For example diff --git a/mathics/core/load_builtin.py b/mathics/core/load_builtin.py index dfff80c10..418a7527d 100644 --- a/mathics/core/load_builtin.py +++ b/mathics/core/load_builtin.py @@ -133,11 +133,8 @@ def definition_contribute(definitions): Load the Definition objects associated to all the builtins on `Definitions` """ - # let MakeBoxes contribute first - _builtins["System`MakeBoxes"].contribute(definitions) for name, item in _builtins.items(): - if name != "System`MakeBoxes": - item.contribute(definitions) + item.contribute(definitions) from mathics.core.definitions import Definition from mathics.core.expression import ensure_context diff --git a/mathics/doc/documentation/1-Manual.mdoc b/mathics/doc/documentation/1-Manual.mdoc index f3f6ac18e..433a65c38 100644 --- a/mathics/doc/documentation/1-Manual.mdoc +++ b/mathics/doc/documentation/1-Manual.mdoc @@ -912,14 +912,13 @@ In a similar way, in the CLI, we can ask for TraditionalForm explicitly = c 'MakeBoxes' for another form: - >> MakeBoxes[TeXForm[b], form_] = "d"; >> b // TeXForm = ... You can cause a much bigger mess by overriding 'MakeBoxes' than by sticking to 'Format', e.g. generate invalid XML: - >> MakeBoxes[MathMLForm[c], form_] = "> MakeBoxes[MathMLForm[c], form_] := "> c // MathMLForm //StandardForm = RadicalBox[3, StandardForm] + if not lhs.has_form("MakeBoxes", 2): + evaluation.message("MakeBoxes", "argrx", Integer(len(lhs.elements))) + raise AssignmentException(lhs, None) + target, form = lhs.elements + # Check second argument + makeboxes_rule = Rule(lhs, rhs, system=False) + tags = [] if tags is None else tags + if upset: + tags = tags + [target.get_lookup_name()] + else: + if not tags: + tags = ["System`MakeBoxes"] + definitions = evaluation.definitions - definitions.add_rule("System`MakeBoxes", makeboxes_rule, "downvalues") - # makeboxes_defs = evaluation.definitions.builtin["System`MakeBoxes"] - # makeboxes_defs.add_rule(makeboxes_rule) + for tag in tags: + if is_protected(tag, definitions): + evaluation.message(self.get_name(), "wrsym", Symbol(tag)) + return False + definitions.add_format(tag, makeboxes_rule, "_MakeBoxes") return True diff --git a/mathics/eval/lists.py b/mathics/eval/lists.py index 130d9ffb1..f20173ed6 100644 --- a/mathics/eval/lists.py +++ b/mathics/eval/lists.py @@ -1,4 +1,3 @@ -from mathics.builtin.box.layout import RowBox from mathics.core.atoms import String from mathics.core.convert.expression import to_expression from mathics.core.exceptions import PartDepthError, PartRangeError @@ -61,6 +60,8 @@ def get_tuples(items): def list_boxes(items, f, evaluation, open=None, close=None): + from mathics.builtin.box.layout import RowBox + result = [ Expression(SymbolMakeBoxes, item, f).evaluate(evaluation) for item in items ] diff --git a/mathics/format/box/__init__.py b/mathics/format/box/__init__.py index 42d3f798c..48688dd37 100644 --- a/mathics/format/box/__init__.py +++ b/mathics/format/box/__init__.py @@ -6,9 +6,7 @@ from mathics.format.box.makeboxes import ( _boxed_string, eval_generic_makeboxes, - eval_makeboxes, eval_makeboxes_fullform, - eval_makeboxes_outputform, format_element, to_boxes, ) @@ -36,9 +34,7 @@ "eval_baseform", "eval_generic_makeboxes", "eval_infix", - "eval_makeboxes", "eval_makeboxes_fullform", - "eval_makeboxes_outputform", "eval_mathmlform", "eval_postprefix", "eval_tableform", diff --git a/mathics/format/box/makeboxes.py b/mathics/format/box/makeboxes.py index 2090080c4..e365570a7 100644 --- a/mathics/format/box/makeboxes.py +++ b/mathics/format/box/makeboxes.py @@ -9,49 +9,41 @@ from typing import List from mathics.core.atoms import Complex, Rational, String -from mathics.core.element import BaseElement, BoxElementMixin +from mathics.core.element import BaseElement, BoxElementMixin, EvalMixin from mathics.core.evaluation import Evaluation from mathics.core.expression import Expression from mathics.core.symbols import ( Atom, Symbol, + SymbolFalse, SymbolFullForm, SymbolList, SymbolMakeBoxes, + SymbolTrue, ) from mathics.core.systemsymbols import ( # SymbolRule, SymbolRuleDelayed, + SymbolAborted, SymbolComplex, SymbolRational, SymbolStandardForm, SymbolTraditionalForm, ) +from mathics.eval.lists import list_boxes from mathics.format.box.formatvalues import do_format from mathics.format.box.precedence import parenthesize BOX_FORMS = {SymbolStandardForm, SymbolTraditionalForm} +PRINT_FORMS_CALLBACK = {} -def to_boxes(x, evaluation: Evaluation, options={}) -> BoxElementMixin: - """ - This function takes the expression ``x`` - and tries to reduce it to a ``BoxElementMixin`` - expression using an evaluation object. - """ - if isinstance(x, BoxElementMixin): - return x - if isinstance(x, Atom): - x = x.atom_to_boxes(SymbolStandardForm, evaluation) - return to_boxes(x, evaluation, options) - if isinstance(x, Expression): - if x.has_form("MakeBoxes", None): - x_boxed = x.evaluate(evaluation) - else: - x_boxed = eval_makeboxes(x, evaluation) - if isinstance(x_boxed, BoxElementMixin): - return x_boxed - if isinstance(x_boxed, Atom): - return to_boxes(x_boxed, evaluation, options) - return eval_makeboxes_fullform(x, evaluation) +def is_print_form_callback(head_name: str): + """Decorator for register print form callbacks""" + + def _register(func): + PRINT_FORMS_CALLBACK[head_name] = func + return func + + return _register # this temporarily replaces the _BoxedString class @@ -61,10 +53,86 @@ def _boxed_string(string: str, **options): return StyleBox(String(string), **options) +@is_print_form_callback("System`StandardForm") +def eval_makeboxes_standard_form(expr, evaluation): + from mathics.builtin.box.layout import FormBox, TagBox + + boxed = apply_makeboxes_rules(expr, evaluation, SymbolStandardForm) + boxed = FormBox(boxed, SymbolStandardForm) + boxed = TagBox(boxed, SymbolStandardForm, **{"System`Editable": SymbolTrue}) + return boxed + + +@is_print_form_callback("System`TraditionalForm") +def eval_makeboxes_traditional_form(expr, evaluation): + from mathics.builtin.box.layout import FormBox, TagBox + + boxed = apply_makeboxes_rules(expr, evaluation, SymbolTraditionalForm) + boxed = FormBox(boxed, SymbolTraditionalForm) + boxed = TagBox(boxed, SymbolTraditionalForm, **{"System`Editable": SymbolTrue}) + return boxed + + +def apply_makeboxes_rules( + expr: BaseElement, evaluation: Evaluation, form: Symbol = SymbolStandardForm +) -> BoxElementMixin: + """ + This function takes the definitions provided by the evaluation + object, and produces a boxed fullform for expr. + + Basically: MakeBoxes[expr, form] + """ + assert form in BOX_FORMS, f"{form} not in BOX_FORMS" + + def yield_rules(): + # Look + for lookup in (expr.get_lookup_name(), "System`MakeBoxes"): + definition = evaluation.definitions.get_definition(lookup) + for rule in definition.formatvalues.get("_MakeBoxes", []): + yield rule + + mb_expr = Expression(SymbolMakeBoxes, expr, form) + boxed = mb_expr + for rule in yield_rules(): + try: + boxed = rule.apply(mb_expr, evaluation, fully=False) + except OverflowError: + evaluation.message("General", "ovfl") + boxed = mb_expr + continue + if boxed is mb_expr or boxed is None or boxed.sameQ(mb_expr): + continue + if boxed is SymbolAborted: + return String("Aborted") + if isinstance(boxed, EvalMixin): + return boxed.evaluate(evaluation) + if isinstance(boxed, BoxElementMixin): + return boxed + return eval_generic_makeboxes(expr, form, evaluation) + + # TODO: evaluation is needed because `atom_to_boxes` uses it. Can we remove this # argument? +@is_print_form_callback("System`FullForm") def eval_makeboxes_fullform( - element: BaseElement, evaluation: Evaluation + element: BaseElement, evaluation: Evaluation, **kwargs +) -> BoxElementMixin: + from mathics.builtin.box.layout import StyleBox, TagBox + + result = eval_makeboxes_fullform_recursive(element, evaluation, **kwargs) + style_box = StyleBox( + result, + **{ + "System`ShowSpecialCharacters": SymbolFalse, + "System`ShowStringCharacters": SymbolTrue, + "System`NumberMarks": SymbolTrue, + }, + ) + return TagBox(style_box, SymbolFullForm) + + +def eval_makeboxes_fullform_recursive( + element: BaseElement, evaluation: Evaluation, **kwargs ) -> BoxElementMixin: """Same as MakeBoxes[FullForm[expr_], f_]""" from mathics.builtin.box.expression import BoxExpression @@ -90,7 +158,7 @@ def eval_makeboxes_fullform( head, elements = expr.head, expr.elements boxed_elements = tuple( - (eval_makeboxes_fullform(element, evaluation) for element in elements) + (eval_makeboxes_fullform_recursive(element, evaluation) for element in elements) ) # In some places it would be less verbose to use special outputs for # `List`, `Rule` and `RuleDelayed`. WMA does not that, but we do it for @@ -106,7 +174,7 @@ def eval_makeboxes_fullform( result_elements = [left] else: left, right, sep = (String(ch) for ch in ("[", "]", ",")) - result_elements = [eval_makeboxes_fullform(head, evaluation), left] + result_elements = [eval_makeboxes_fullform_recursive(head, evaluation), left] if len(boxed_elements) > 1: arguments: List[BoxElementMixin] = [] @@ -121,32 +189,24 @@ def eval_makeboxes_fullform( return RowBox(*result_elements) -def eval_makeboxes_outputform( - expr: BaseElement, evaluation: Evaluation, form: Symbol, **kwargs -): - """ - Build a 2D representation of the expression using only keyboard characters. - """ - from mathics.builtin.box.layout import PaneBox - from mathics.format.form.outputform import render_output_form - - text_outputform = str(render_output_form(expr, evaluation, **kwargs)) - elem1 = PaneBox(String('"' + text_outputform + '"')) - return elem1 - - def eval_generic_makeboxes(expr, f, evaluation): """MakeBoxes[expr_, f:TraditionalForm|StandardForm]""" from mathics.builtin.box.layout import RowBox + assert f in BOX_FORMS, f"{f} not in BOX_FORMS" if isinstance(expr, BoxElementMixin): expr = expr.to_expression() if isinstance(expr, Atom): return expr.atom_to_boxes(f, evaluation) + if expr.has_form("List", None): + return RowBox(*list_boxes(expr.elements, f, evaluation, "{", "}")) else: head = expr.head elements = expr.elements + printform_callback = PRINT_FORMS_CALLBACK.get(head.get_name(), None) + if printform_callback is not None: + return printform_callback(elements[0], evaluation) f_name = f.get_name() if f_name == "System`TraditionalForm": @@ -170,6 +230,7 @@ def eval_generic_makeboxes(expr, f, evaluation): "System`InputForm", "System`OutputForm", ): + raise ValueError sep = ", " else: sep = "," @@ -194,41 +255,41 @@ def eval_generic_makeboxes(expr, f, evaluation): return RowBox(*result) -def eval_makeboxes( - expr, evaluation: Evaluation, form=SymbolStandardForm -) -> BoxElementMixin: - """ - This function takes the definitions provided by the evaluation - object, and produces a boxed fullform for expr. - - Basically: MakeBoxes[expr // form] - """ - # This is going to be reimplemented. By now, much of the formatting - # relies in rules of the form `MakeBoxes[expr, OutputForm]` - # which is wrong. - if form is SymbolFullForm: - return eval_makeboxes_fullform(expr, evaluation) - if form not in BOX_FORMS: - # print(form, "not in", BOX_FORMS) - expr = Expression(form, expr) - form = SymbolStandardForm - mb_expr = Expression(SymbolMakeBoxes, expr, form) - # print(" evaluate", mb_expr) - return mb_expr.evaluate(evaluation) - - def format_element( element: BaseElement, evaluation: Evaluation, form: Symbol, **kwargs ) -> BoxElementMixin: """ Applies formats associated to the expression, and then calls Makeboxes """ - if form is SymbolFullForm: - return eval_makeboxes_fullform(element, evaluation) - evaluation.is_boxing = True formatted_expr = do_format(element, evaluation, form) - result_box = eval_makeboxes(formatted_expr, evaluation, form) + if form not in BOX_FORMS: + formatted_expr = Expression(form, formatted_expr) + form = SymbolStandardForm + result_box = apply_makeboxes_rules(formatted_expr, evaluation, form) if isinstance(result_box, BoxElementMixin): return result_box - return eval_makeboxes_fullform(element, evaluation) + return eval_makeboxes_fullform_recursive(element, evaluation) + + +def to_boxes(x, evaluation: Evaluation, options={}) -> BoxElementMixin: + """ + This function takes the expression ``x`` + and tries to reduce it to a ``BoxElementMixin`` + expression using an evaluation object. + """ + if isinstance(x, BoxElementMixin): + return x + if isinstance(x, Atom): + x = x.atom_to_boxes(SymbolStandardForm, evaluation) + return to_boxes(x, evaluation, options) + if isinstance(x, Expression): + if x.has_form("MakeBoxes", 1, 2): + x_boxed = x.evaluate(evaluation) + if isinstance(x_boxed, BoxElementMixin): + return x_boxed + if isinstance(x_boxed, Atom): + return to_boxes(x_boxed, evaluation, options) + else: + return apply_makeboxes_rules(x, evaluation) + return eval_makeboxes_fullform_recursive(x, evaluation) diff --git a/mathics/format/box/outputforms.py b/mathics/format/box/outputforms.py index a653ee5f8..60d187f4d 100644 --- a/mathics/format/box/outputforms.py +++ b/mathics/format/box/outputforms.py @@ -1,18 +1,33 @@ import re from mathics.core.atoms import Integer, String +from mathics.core.element import BaseElement, BoxElementMixin +from mathics.core.evaluation import Evaluation from mathics.core.expression import BoxError, Expression from mathics.core.list import ListExpression -from mathics.core.symbols import SymbolFalse, SymbolFullForm, SymbolList -from mathics.core.systemsymbols import SymbolRowBox, SymbolTraditionalForm +from mathics.core.symbols import ( + Symbol, + SymbolFalse, + SymbolFullForm, + SymbolList, + SymbolTrue, +) +from mathics.core.systemsymbols import ( + SymbolMathMLForm, + SymbolTeXForm, + SymbolTraditionalForm, +) from mathics.eval.testing_expressions import expr_min -from mathics.format.box.makeboxes import format_element +from mathics.format.box.makeboxes import format_element, is_print_form_callback MULTI_NEWLINE_RE = re.compile(r"\n{2,}") -def eval_mathmlform(expr, evaluation) -> Expression: +@is_print_form_callback("System`MathMLForm") +def eval_mathmlform(expr: BaseElement, evaluation: Evaluation) -> BoxElementMixin: "MakeBoxes[MathMLForm[expr_], form_]" + from mathics.builtin.box.layout import InterpretationBox + boxes = format_element(expr, evaluation, SymbolTraditionalForm) try: mathml = boxes.boxes_to_mathml(evaluation=evaluation) @@ -34,14 +49,23 @@ def eval_mathmlform(expr, evaluation) -> Expression: mathml = '%s' % mathml mathml = '%s' % mathml # convert_box(boxes) - return Expression(SymbolRowBox, ListExpression(String(mathml))) + return InterpretationBox( + String(f'"{mathml}"'), + Expression(SymbolMathMLForm, expr), + **{"System`AutoDelete": SymbolTrue, "System`Editable": SymbolTrue}, + ) -def eval_tableform(self, table, f, evaluation, options): +def eval_tableform( + self, table: BaseElement, f: Symbol, evaluation: Evaluation, options +): """MakeBoxes[TableForm[table_], f_]""" from mathics.builtin.box.layout import GridBox from mathics.builtin.tensors import get_dimensions + if not isinstance(table, Expression): + return format_element(table, evaluation, f) + dims = len(get_dimensions(table, head=SymbolList)) depth = self.get_option(options, "TableDepth", evaluation, pop=True) options["System`TableDepth"] = depth @@ -93,7 +117,10 @@ def transform_item(item): return result -def eval_texform(expr, evaluation) -> Expression: +@is_print_form_callback("System`TeXForm") +def eval_texform(expr: BaseElement, evaluation: Evaluation) -> BoxElementMixin: + from mathics.builtin.box.layout import InterpretationBox + boxes = format_element(expr, evaluation, SymbolTraditionalForm) try: # Here we set ``show_string_characters`` to False, to reproduce @@ -114,4 +141,8 @@ def eval_texform(expr, evaluation) -> Expression: Expression(SymbolFullForm, expr).evaluate(evaluation), ) tex = "" - return Expression(SymbolRowBox, ListExpression(String(tex))) + return InterpretationBox( + String(f'"{tex}"'), + Expression(SymbolTeXForm, expr), + **{"System`AutoDelete": SymbolTrue, "System`Editable": SymbolTrue}, + ) diff --git a/test/builtin/box/test_custom_boxexpression.py b/test/builtin/box/test_custom_boxexpression.py index d3b36fdcb..aaac0e8d2 100644 --- a/test/builtin/box/test_custom_boxexpression.py +++ b/test/builtin/box/test_custom_boxexpression.py @@ -6,6 +6,7 @@ from mathics.core.builtin import Predefined from mathics.core.evaluation import Evaluation from mathics.core.expression import Expression +from mathics.core.rules import BaseRule, FunctionApplyRule, Rule from mathics.core.symbols import Symbol SymbolCustomGraphicsBox = Symbol("CustomGraphicsBox") @@ -42,12 +43,28 @@ class CustomAtom(Predefined): "N[System`CustomAtom]": "37", } - def eval_to_boxes(self, evaluation): - "System`MakeBoxes[System`CustomAtom, StandardForm|TraditionalForm|OutputForm]" + # Since this is a Mathics3 Module which is loaded after + # the core symbols are loaded, it is safe to assume that `MakeBoxes` + # definition was already loaded. We can add then rules to it. + # This modified `contribute` method do that, adding specific + # makeboxes rules for this kind of atoms. + def contribute(self, definitions, is_pymodule=True): + super().contribute(definitions, is_pymodule) + # Add specific MakeBoxes rules + name = self.get_name() + + for pattern, function in self.get_functions("makeboxes_"): + mb_rule = FunctionApplyRule( + name, pattern, function, None, attributes=None, system=True + ) + definitions.add_format("System`MakeBoxes", mb_rule, "_MakeBoxes") + + def makeboxes_general(self, evaluation): + "System`MakeBoxes[System`CustomAtom, StandardForm|TraditionalForm]" return CustomBoxExpression(evaluation=evaluation) - def eval_to_boxes_inputform(self, evaluation): - "System`MakeBoxes[InputForm[System`CustomAtom], StandardForm|TraditionalForm|OutputForm]" + def makeboxes_inputform(self, evaluation): + "System`MakeBoxes[InputForm[System`CustomAtom], StandardForm|TraditionalForm]" return CustomBoxExpression(evaluation=evaluation) @@ -57,6 +74,22 @@ class CustomGraphicsBox(BoxExpression): options = GRAPHICS_OPTIONS attributes = A_HOLD_ALL | A_PROTECTED | A_READ_PROTECTED + # Since this is a Mathics3 Module which is loaded after + # the core symbols are loaded, it is safe to assume that `MakeBoxes` + # definition was already loaded. We can add then rules to it. + # This modified `contribute` method do that, adding specific + # makeboxes rules for this kind of BoxExpression. + def contribute(self, definitions, is_pymodule=True): + super().contribute(definitions, is_pymodule) + # Add specific MakeBoxes rules + name = self.get_name() + + for pattern, function in self.get_functions("makeboxes_"): + mb_rule = FunctionApplyRule( + name, pattern, function, None, attributes=None, system=True + ) + definitions.add_format("System`MakeBoxes", mb_rule, "_MakeBoxes") + def init(self, *elems, **options): self._elements = elems self.evaluation = options.pop("evaluation", None) @@ -65,16 +98,15 @@ def init(self, *elems, **options): def to_expression(self): return Expression(SymbolCustomGraphicsBox, *self.elements) - def eval_box(self, expr, evaluation: Evaluation, options: dict): + def makeboxes_graphics(self, expr, evaluation: Evaluation, options: dict): """System`MakeBoxes[System`Graphics[System`expr_, System`OptionsPattern[System`Graphics]], - System`StandardForm|System`TraditionalForm|System`OutputForm]""" + System`StandardForm|System`TraditionalForm]""" instance = CustomGraphicsBox(*(expr.elements), evaluation=evaluation) return instance - def eval_box_outputForm(self, expr, evaluation: Evaluation, options: dict): + def makeboxes_outputForm(self, expr, evaluation: Evaluation, options: dict): """System`MakeBoxes[System`OutputForm[System`Graphics[System`expr_, System`OptionsPattern[System`Graphics]]], System`StandardForm|System`TraditionalForm]""" - print("MakeBoxes OutputForm") instance = CustomGraphicsBox(*(expr.elements), evaluation=evaluation) return instance diff --git a/test/format/format_tests.yaml b/test/format/format_tests.yaml index b8eb032e6..a9cfed0ad 100644 --- a/test/format/format_tests.yaml +++ b/test/format/format_tests.yaml @@ -23,6 +23,7 @@ # because we use both in documentation and in the web interface. # + '"-7.32"': msg: A String with a number latex: @@ -813,6 +814,8 @@ TableForm[{{a,b},{c,d}}]: System`OutputForm: 'α' System`StandardForm: "α" System`TraditionalForm: "α" + + a: msg: A Symbol latex: @@ -867,3 +870,47 @@ a^4: System`OutputForm: a ^ 4 System`StandardForm: a^4 System`TraditionalForm: a^4 + + +Optional[x__]: + msg: Optional with one argument + latex: + System`OutputForm: '\text{x\_\_.}' + System`StandardForm: '\text{x\_\_.}' + mathml: + System`OutputForm: 'x__.' + System`StandardForm: 'x__.' + text: + System`InputForm: '(x__.)' + System`OutputForm: 'x__.' + System`StandardForm: 'x__.' + System`TraditionalForm: 'x__.' + + +Optional[x__, a+b]: + msg: Optional with two arguments + latex: + System`OutputForm: ' \text{x\_\_ : a + b}' + System`StandardForm: '\text{x\_\_}:a+b' + mathml: + System`OutputForm: 'x__ : a + b' + text: + System`InputForm: 'x__ : a + b' + System`OutputForm: 'x__ : a + b' + System`StandardForm: 'x__:a+b' + System`TraditionalForm: 'x__:a+b' + + +a+PrecedenceForm[b+c,10]: + msg: "PrecedenceForm" + latex: + System`OutputForm: '\text{a + (b + c)}' + System`StandardForm: 'a+\left(b+c\right)' + mathml: + System`OutputForm: 'a + (b + c)' + System`StandardForm: 'a + ( b + c )' + text: + System`InputForm: 'a + (PrecedenceForm[b + c, 10])' + System`OutputForm: 'a + (b + c)' + System`StandardForm: 'a+(b+c)' + System`TraditionalForm: 'a+(b+c)' diff --git a/test/format/makeboxes_tests.yaml b/test/format/makeboxes_tests.yaml index 23d2e5990..4363450bb 100644 --- a/test/format/makeboxes_tests.yaml +++ b/test/format/makeboxes_tests.yaml @@ -65,10 +65,10 @@ Basic Forms: Arithmetic: FullForm: - expect: TagBox[StyleBox[RowBox[{"Plus", "[", RowBox[{"a", ",", RowBox[{"Times", "[", RowBox[{RowBox[{"-", "1"}], ",", "b"}], "]"}]}], "]"}], ShowSpecialCharacters-> False, ShowStringCharacters -> True, NumberMarks -> True], FullForm] + expect: 'TagBox[StyleBox[RowBox[{"Plus", "[", RowBox[{"a", ",", RowBox[{"Times", "[", RowBox[{RowBox[{"-", "1"}], ",", "b"}], "]"}]}], "]"}], System`ShowSpecialCharacters-> False, System`ShowStringCharacters -> True, System`NumberMarks -> True], FullForm]' expr: MakeBoxes[a-b//FullForm] InputForm: - expect: InterpretationBox[StyleBox["a - b", ShowStringCharacters -> True, NumberMarks-> True], InputForm[a - b], Editable -> True, AutoDelete -> True] + expect: InterpretationBox[StyleBox["a - b", System`ShowStringCharacters -> True, NumberMarks-> True], InputForm[a - b], Editable -> True, AutoDelete -> True] expr: MakeBoxes[a-b//InputForm] OutputForm: expect: InterpretationBox[PaneBox["\"a - b\""], OutputForm[a - b], Editable-> False] @@ -87,10 +87,10 @@ Basic Forms: expect: TagBox[FormBox[RowBox[List["F", "(", "x", ")"]], TraditionalForm], TraditionalForm, Editable-> True] expr: MakeBoxes[F[x]//TraditionalForm] FullForm: - expect: TagBox[StyleBox[RowBox[{"F", "[", "x", "]"}], ShowSpecialCharacters-> False, ShowStringCharacters -> True, NumberMarks -> True], FullForm] + expect: TagBox[StyleBox[RowBox[{"F", "[", "x", "]"}], ShowSpecialCharacters-> False, System`ShowStringCharacters -> True, System`NumberMarks -> True], FullForm] expr: MakeBoxes[F[x]//FullForm] InputForm: - expect: InterpretationBox[StyleBox["F[x]", ShowStringCharacters -> True, NumberMarks-> True], InputForm[F[x]], Editable -> True, AutoDelete -> True] + expect: InterpretationBox[StyleBox["F[x]", System`ShowStringCharacters -> True, NumberMarks-> True], InputForm[F[x]], Editable -> True, AutoDelete -> True] expr: MakeBoxes[F[x]//InputForm] OutputForm: expect: InterpretationBox[PaneBox["\"F[x]\""], OutputForm[F[x]], Editable ->False] @@ -103,10 +103,10 @@ Basic Forms: expr: MakeBoxes[F[x]//TeXForm] Integer_negative: FullForm: - expect: TagBox[StyleBox[RowBox[{"-", "14"}], ShowSpecialCharacters-> False, ShowStringCharacters -> True, NumberMarks -> True], FullForm] + expect: TagBox[StyleBox[RowBox[{"-", "14"}], ShowSpecialCharacters-> False, System`ShowStringCharacters -> True, System`NumberMarks -> True], FullForm] expr: MakeBoxes[-14//FullForm] InputForm: - expect: InterpretationBox[StyleBox["-14", ShowStringCharacters -> True, NumberMarks -> True], InputForm[-14], Editable -> True, AutoDelete -> True] + expect: InterpretationBox[StyleBox["-14", System`ShowStringCharacters -> True, System`NumberMarks -> True], InputForm[-14], Editable -> True, AutoDelete -> True] expr: MakeBoxes[-14//InputForm] OutputForm: expect: InterpretationBox[PaneBox["\"-14\""], OutputForm[-14], Editable -> False] @@ -119,10 +119,10 @@ Basic Forms: expr: MakeBoxes[-14//TeXForm] Integer_positive: FullForm: - expect: TagBox[StyleBox["14", ShowSpecialCharacters -> False, ShowStringCharacters-> True, NumberMarks -> True], FullForm] + expect: TagBox[StyleBox["14", System`ShowSpecialCharacters -> False, System`ShowStringCharacters-> True, System`NumberMarks -> True], FullForm] expr: MakeBoxes[14//FullForm] InputForm: - expect: InterpretationBox[StyleBox["14", ShowStringCharacters -> True, NumberMarks-> True], InputForm[14], Editable -> True, AutoDelete -> True] + expect: InterpretationBox[StyleBox["14", System`ShowStringCharacters -> True, NumberMarks-> True], InputForm[14], Editable -> True, AutoDelete -> True] expr: MakeBoxes[14//InputForm] OutputForm: expect: InterpretationBox[PaneBox["\"14\""], OutputForm[14], Editable -> False] @@ -135,12 +135,12 @@ Basic Forms: expr: MakeBoxes[14//TeXForm] PrecisionReal: FullForm: - expect: TagBox[StyleBox[RowBox[{"-", "14.`3."}], ShowSpecialCharacters -> False, ShowStringCharacters-> True, NumberMarks -> True], FullForm] + expect: TagBox[StyleBox[RowBox[{"-", "14.`3."}], System`ShowSpecialCharacters -> False, System`ShowStringCharacters-> True, System`NumberMarks -> True], FullForm] expr: MakeBoxes[-14.`3//FullForm] msg: "In Mathics3, precision is always an integer number." InputForm: expr: MakeBoxes[-14.`3//InputForm] - expect: InterpretationBox[StyleBox["-14.`3.", ShowStringCharacters -> True, NumberMarks-> True], InputForm[-14.`3], Editable -> True, AutoDelete -> True] + expect: InterpretationBox[StyleBox["-14.`3.", System`ShowStringCharacters -> True, NumberMarks-> True], InputForm[-14.`3], Editable -> True, AutoDelete -> True] OutputForm: expect: InterpretationBox[PaneBox["\"-14.\""], OutputForm[-14.0], Editable-> False] expr: MakeBoxes[-14.0//OutputForm] @@ -153,10 +153,10 @@ Basic Forms: -> True] Symbol: FullForm: - expect: TagBox[StyleBox["x", ShowSpecialCharacters -> False, ShowStringCharacters-> True, NumberMarks -> True], FullForm] + expect: TagBox[StyleBox["x", System`ShowSpecialCharacters -> False, System`ShowStringCharacters-> True, System`NumberMarks -> True], FullForm] expr: MakeBoxes[x//FullForm] InputForm: - expect: InterpretationBox[StyleBox["x", ShowStringCharacters -> True, NumberMarks-> True], InputForm[x], Editable -> True, AutoDelete -> True] + expect: InterpretationBox[StyleBox["x", System`ShowStringCharacters -> True, NumberMarks-> True], InputForm[x], Editable -> True, AutoDelete -> True] expr: MakeBoxes[x//InputForm] OutputForm: expect: InterpretationBox[PaneBox["\"x\""], OutputForm[x], Editable -> False] diff --git a/test/format/test_makeboxes.py b/test/format/test_makeboxes.py index e4a1386b1..31b39a6bf 100644 --- a/test/format/test_makeboxes.py +++ b/test/format/test_makeboxes.py @@ -28,8 +28,8 @@ def makeboxes_basic_forms_iterator(block): for key, tests in MAKEBOXES_TESTS[block].items(): for form, entry in tests.items(): msg = f"{key}, {form}" - expr = entry["expr"] - expect = entry["expect"] + expr = entry["expr"] + "//InputForm" + expect = entry["expect"] + "//InputForm" yield expr, expect, msg @@ -44,7 +44,7 @@ def test_makeboxes_basic_forms(str_expr, str_expected, fail_msg): str_expected, to_string_expr=True, to_string_expected=True, - hold_expected=True, + hold_expected=False, failure_message=fail_msg, ) @@ -62,7 +62,7 @@ def test_makeboxes_real(str_expr, str_expected, msg): str_expected, to_string_expr=True, to_string_expected=True, - hold_expected=True, + hold_expected=False, failure_message=msg, ) diff --git a/test/helper.py b/test/helper.py index f9df14b31..0baa2205c 100644 --- a/test/helper.py +++ b/test/helper.py @@ -126,10 +126,10 @@ def check_evaluation( print(time.asctime()) if failure_message: - print(f"got: {result}, expect: {expected} -- {failure_message}") + print(f"got: \n{result}\nexpect:\n{expected}\n -- {failure_message}") assert result == expected, failure_message else: - print(f"got: {result}, expect: {expected}") + print(f"got: \n{result}\nexpect:\n{expected}\n --") if isinstance(expected, re.Pattern): assert expected.match(result) else: