1# -*- coding: utf-8 -*-
2
3"""
4Input and Output
5"""
6
7import re
8import mpmath
9
10import typing
11from typing import Any
12
13from mathics.version import __version__  # noqa used in loading to check consistency.
14
15from mathics.builtin.base import (
16    Builtin,
17    BinaryOperator,
18    BoxConstruct,
19    BoxConstructError,
20    Operator,
21)
22from mathics.builtin.tensors import get_dimensions
23from mathics.builtin.comparison import expr_min
24from mathics.builtin.lists import list_boxes
25from mathics.builtin.options import options_to_rules
26from mathics.core.expression import (
27    Expression,
28    String,
29    StringFromPython,
30    Symbol,
31    Integer,
32    Real,
33    BoxError,
34    from_python,
35    MachineReal,
36    PrecisionReal,
37    SymbolList,
38    SymbolMakeBoxes,
39    SymbolRule
40)
41from mathics.core.numbers import (
42    dps,
43    convert_base,
44    machine_precision,
45    reconstruct_digits,
46)
47
48from mathics.core.evaluation import Message as EvaluationMessage
49
50MULTI_NEWLINE_RE = re.compile(r"\n{2,}")
51
52
53class Format(Builtin):
54    """
55    <dl>
56      <dt>'Format[$expr$]'
57      <dd>holds values specifying how $expr$ should be printed.
58    </dl>
59
60    Assign values to 'Format' to control how particular expressions
61    should be formatted when printed to the user.
62    >> Format[f[x___]] := Infix[{x}, "~"]
63    >> f[1, 2, 3]
64     = 1 ~ 2 ~ 3
65    >> f[1]
66     = 1
67
68    Raw objects cannot be formatted:
69    >> Format[3] = "three";
70     : Cannot assign to raw object 3.
71
72    Format types must be symbols:
73    >> Format[r, a + b] = "r";
74     : Format type a + b is not a symbol.
75
76    Formats must be attached to the head of an expression:
77    >> f /: Format[g[f]] = "my f";
78     : Tag f not found or too deep for an assigned rule.
79    """
80
81    messages = {"fttp": "Format type `1` is not a symbol."}
82
83
84def parenthesize(precedence, leaf, leaf_boxes, when_equal):
85    from mathics.builtin import builtins_precedence
86
87    while leaf.has_form("HoldForm", 1):
88        leaf = leaf.leaves[0]
89    if leaf.has_form(("Infix", "Prefix", "Postfix"), 3, None):
90        leaf_prec = leaf.leaves[2].get_int_value()
91    elif leaf.has_form("PrecedenceForm", 2):
92        leaf_prec = leaf.leaves[1].get_int_value()
93    else:
94        leaf_prec = builtins_precedence.get(leaf.get_head_name())
95    if precedence is not None and leaf_prec is not None:
96        if precedence > leaf_prec or (precedence == leaf_prec and when_equal):
97            return Expression(
98                "RowBox", Expression(SymbolList, String("("), leaf_boxes, String(")"))
99            )
100    return leaf_boxes
101
102
103def make_boxes_infix(leaves, ops, precedence, grouping, form):
104    result = []
105    for index, leaf in enumerate(leaves):
106        if index > 0:
107            result.append(ops[index - 1])
108        parenthesized = False
109        if grouping == "System`NonAssociative":
110            parenthesized = True
111        elif grouping == "System`Left" and index > 0:
112            parenthesized = True
113        elif grouping == "System`Right" and index == 0:
114            parenthesized = True
115
116        leaf_boxes = MakeBoxes(leaf, form)
117        leaf = parenthesize(precedence, leaf, leaf_boxes, parenthesized)
118
119        result.append(leaf)
120    return Expression("RowBox", Expression(SymbolList, *result))
121
122
123def real_to_s_exp(expr, n):
124    if expr.is_zero:
125        s = "0"
126        sign_prefix = ""
127        if expr.is_machine_precision():
128            exp = 0
129        else:
130            p = expr.get_precision()
131            exp = -dps(p)
132        nonnegative = 1
133    else:
134        if n is None:
135            if expr.is_machine_precision():
136                value = expr.get_float_value()
137                s = repr(value)
138            else:
139                with mpmath.workprec(expr.get_precision()):
140                    value = expr.to_mpmath()
141                    s = mpmath.nstr(value, dps(expr.get_precision()) + 1)
142        else:
143            with mpmath.workprec(expr.get_precision()):
144                value = expr.to_mpmath()
145                s = mpmath.nstr(value, n)
146
147        # sign prefix
148        if s[0] == "-":
149            assert value < 0
150            nonnegative = 0
151            s = s[1:]
152        else:
153            assert value >= 0
154            nonnegative = 1
155
156        # exponent (exp is actual, pexp is printed)
157        if "e" in s:
158            s, exp = s.split("e")
159            exp = int(exp)
160            if len(s) > 1 and s[1] == ".":
161                # str(float) doesn't always include '.' if 'e' is present.
162                s = s[0] + s[2:].rstrip("0")
163        else:
164            exp = s.index(".") - 1
165            s = s[: exp + 1] + s[exp + 2 :].rstrip("0")
166
167            # consume leading '0's.
168            i = 0
169            while s[i] == "0":
170                i += 1
171                exp -= 1
172            s = s[i:]
173
174        # add trailing zeros for precision reals
175        if n is not None and not expr.is_machine_precision() and len(s) < n:
176            s = s + "0" * (n - len(s))
177    return s, exp, nonnegative
178
179
180def int_to_s_exp(expr, n):
181    n = expr.get_int_value()
182    if n < 0:
183        nonnegative = 0
184        s = str(-n)
185    else:
186        nonnegative = 1
187        s = str(n)
188    exp = len(s) - 1
189    return s, exp, nonnegative
190
191
192def number_form(expr, n, f, evaluation, options):
193    """
194    Converts a Real or Integer instance to Boxes.
195
196    n digits of precision with f (can be None) digits after the decimal point.
197    evaluation (can be None) is used for messages.
198
199    The allowed options are python versions of the options permitted to
200    NumberForm and must be supplied. See NumberForm or Real.make_boxes
201    for correct option examples.
202    """
203
204    assert isinstance(n, int) and n > 0 or n is None
205    assert f is None or (isinstance(f, int) and f >= 0)
206
207    is_int = False
208    if isinstance(expr, Integer):
209        assert n is not None
210        s, exp, nonnegative = int_to_s_exp(expr, n)
211        if f is None:
212            is_int = True
213    elif isinstance(expr, Real):
214        if n is not None:
215            n = min(n, dps(expr.get_precision()) + 1)
216        s, exp, nonnegative = real_to_s_exp(expr, n)
217        if n is None:
218            n = len(s)
219    else:
220        raise ValueError("Expected Real or Integer.")
221
222    assert isinstance(n, int) and n > 0
223
224    sign_prefix = options["NumberSigns"][nonnegative]
225
226    # round exponent to ExponentStep
227    rexp = (exp // options["ExponentStep"]) * options["ExponentStep"]
228
229    if is_int:
230        # integer never uses scientific notation
231        pexp = ""
232    else:
233        method = options["ExponentFunction"]
234        pexp = method(Integer(rexp)).get_int_value()
235        if pexp is not None:
236            exp -= pexp
237            pexp = str(pexp)
238        else:
239            pexp = ""
240
241    # pad right with '0'.
242    if len(s) < exp + 1:
243        if evaluation is not None:
244            evaluation.message("NumberForm", "sigz")
245        # TODO NumberPadding?
246        s = s + "0" * (1 + exp - len(s))
247    # pad left with '0'.
248    if exp < 0:
249        s = "0" * (-exp) + s
250        exp = 0
251
252    # left and right of NumberPoint
253    left, right = s[: exp + 1], s[exp + 1 :]
254
255    def _round(number, ndigits):
256        """
257        python round() for integers but with correct rounding.
258        e.g. `_round(14225, -1)` is `14230` not `14220`.
259        """
260        assert isinstance(ndigits, int)
261        assert ndigits < 0
262        assert isinstance(number, int)
263        assert number >= 0
264        number += 5 * int(10 ** -(1 + ndigits))
265        number //= int(10 ** -ndigits)
266        return number
267
268    # pad with NumberPadding
269    if f is not None:
270        if len(right) < f:
271            # pad right
272            right = right + (f - len(right)) * options["NumberPadding"][1]
273        elif len(right) > f:
274            # round right
275            tmp = int(left + right)
276            tmp = _round(tmp, f - len(right))
277            tmp = str(tmp)
278            left, right = tmp[: exp + 1], tmp[exp + 1 :]
279
280    def split_string(s, start, step):
281        if start > 0:
282            yield s[:start]
283        for i in range(start, len(s), step):
284            yield s[i : i + step]
285
286    # insert NumberSeparator
287    digit_block = options["DigitBlock"]
288    if digit_block[0] != 0:
289        left = split_string(left, len(left) % digit_block[0], digit_block[0])
290        left = options["NumberSeparator"][0].join(left)
291    if digit_block[1] != 0:
292        right = split_string(right, 0, digit_block[1])
293        right = options["NumberSeparator"][1].join(right)
294
295    left_padding = 0
296    max_sign_len = max(len(options["NumberSigns"][0]), len(options["NumberSigns"][1]))
297    l = len(sign_prefix) + len(left) + len(right) - max_sign_len
298    if l < n:
299        left_padding = n - l
300    elif len(sign_prefix) < max_sign_len:
301        left_padding = max_sign_len - len(sign_prefix)
302    left_padding = left_padding * options["NumberPadding"][0]
303
304    # insert NumberPoint
305    if options["SignPadding"]:
306        prefix = sign_prefix + left_padding
307    else:
308        prefix = left_padding + sign_prefix
309
310    if is_int:
311        s = prefix + left
312    else:
313        s = prefix + left + options["NumberPoint"] + right
314
315    # base
316    base = "10"
317
318    # build number
319    method = options["NumberFormat"]
320    return method(String(s), String(base), String(pexp), options)
321
322
323class MakeBoxes(Builtin):
324    """
325    <dl>
326    <dt>'MakeBoxes[$expr$]'
327        <dd>is a low-level formatting primitive that converts $expr$
328        to box form, without evaluating it.
329    <dt>'\\( ... \\)'
330        <dd>directly inputs box objects.
331    </dl>
332
333    String representation of boxes
334    >> \\(x \\^ 2\\)
335     = SuperscriptBox[x, 2]
336
337    >> \\(x \\_ 2\\)
338     = SubscriptBox[x, 2]
339
340    >> \\( a \\+ b \\% c\\)
341     = UnderoverscriptBox[a, b, c]
342
343    >> \\( a \\& b \\% c\\)
344     = UnderoverscriptBox[a, c, b]
345
346    #> \\( \\@ 5 \\)
347     = SqrtBox[5]
348
349    >> \\(x \\& y \\)
350     = OverscriptBox[x, y]
351
352    >> \\(x \\+ y \\)
353     = UnderscriptBox[x, y]
354
355    #> \\( x \\^ 2 \\_ 4 \\)
356     = SuperscriptBox[x, SubscriptBox[2, 4]]
357
358    ## Tests for issue 151 (infix operators in heads)
359    #> (a + b)[x]
360     = (a + b)[x]
361    #> (a b)[x]
362     = (a b)[x]
363    #> (a <> b)[x]
364     : String expected.
365     = (a <> b)[x]
366    """
367
368    # TODO: Convert operators to appropriate representations e.g. 'Plus' to '+'
369    """
370    >> \\(a + b\\)
371     = RowBox[{a, +, b}]
372
373    >> \\(TraditionalForm \\` a + b\\)
374     = FormBox[RowBox[{a, +, b}], TraditionalForm]
375
376    >> \\(x \\/ \\(y + z\\)\\)
377     =  FractionBox[x, RowBox[{y, +, z}]]
378    """
379
380    # TODO: Constructing boxes from Real
381    """
382    ## Test Real MakeBoxes
383    #> MakeBoxes[1.4]
384     = 1.4`
385    #> MakeBoxes[1.4`]
386     = 1.4`
387    #> MakeBoxes[1.5`20]
388     = 1.5`20.
389    #> MakeBoxes[1.4`20]
390     = 1.4`20.
391    #> MakeBoxes[1.5``20]
392     = 1.5`20.1760912591
393    #> MakeBoxes[-1.4]
394     = RowBox[{-, 1.4`}]
395    #> MakeBoxes[34.*^3]
396     = 34000.`
397
398    #> MakeBoxes[0`]
399     = 0.`
400    #> MakeBoxes[0`3]
401     = 0
402    #> MakeBoxes[0``30]
403     = 0.``30.
404    #> MakeBoxes[0.`]
405     = 0.`
406    #> MakeBoxes[0.`3]
407     = 0.`
408    #> MakeBoxes[0.``30]
409     = 0.``30.
410
411    #> MakeBoxes[14]
412     = 14
413    #> MakeBoxes[-14]
414     = RowBox[{-, 14}]
415    """
416
417    # TODO: Correct precedence
418    """
419    >> \\(x \\/ y + z\\)
420     = RowBox[{FractionBox[x, y], +, z}]
421    >> \\(x \\/ (y + z)\\)
422     = FractionBox[x, RowBox[{(, RowBox[{y, +, z}], )}]]
423
424    #> \\( \\@ a + b \\)
425     = RowBox[{SqrtBox[a], +, b}]
426    """
427
428    # FIXME: Don't insert spaces with brackets
429    """
430    #> \\(c (1 + x)\\)
431     = RowBox[{c, RowBox[{(, RowBox[{1, +, x}], )}]}]
432    """
433
434    # TODO: Required MakeExpression
435    """
436    #> \\!\\(x \\^ 2\\)
437     = x ^ 2
438    #> FullForm[%]
439     = Power[x, 2]
440    """
441
442    # TODO: Fix Infix operators
443    """
444    >> MakeBoxes[1 + 1]
445     = RowBox[{1, +, 1}]
446    """
447
448    # TODO: Parsing of special characters (like commas)
449    """
450    >> \\( a, b \\)
451     = RowBox[{a, ,, b}]
452    """
453
454    attributes = ("HoldAllComplete", "Unprotected")
455
456    rules = {
457        "MakeBoxes[Infix[head_[leaves___]], "
458        "    f:StandardForm|TraditionalForm|OutputForm|InputForm]": (
459            'MakeBoxes[Infix[head[leaves], StringForm["~`1`~", head]], f]'
460        ),
461        "MakeBoxes[expr_]": "MakeBoxes[expr, StandardForm]",
462        "MakeBoxes[(form:StandardForm|TraditionalForm|OutputForm|TeXForm|"
463        "MathMLForm)[expr_], StandardForm|TraditionalForm]": ("MakeBoxes[expr, form]"),
464        "MakeBoxes[(form:OutputForm|MathMLForm|TeXForm)[expr_], OutputForm]": "MakeBoxes[expr, form]",
465        "MakeBoxes[StandardForm[expr_], OutputForm]": "MakeBoxes[expr, OutputForm]",
466        "MakeBoxes[FullForm[expr_], StandardForm|TraditionalForm|OutputForm]": "StyleBox[MakeBoxes[expr, FullForm], ShowStringCharacters->True]",
467        "MakeBoxes[InputForm[expr_], StandardForm|TraditionalForm|OutputForm]": "StyleBox[MakeBoxes[expr, InputForm], ShowStringCharacters->True]",
468        "MakeBoxes[PrecedenceForm[expr_, prec_], f_]": "MakeBoxes[expr, f]",
469        "MakeBoxes[Style[expr_, OptionsPattern[Style]], f_]": (
470            "StyleBox[MakeBoxes[expr, f], "
471            "ImageSizeMultipliers -> OptionValue[ImageSizeMultipliers]]"
472        ),
473    }
474
475    def apply_general(self, expr, f, evaluation):
476        """MakeBoxes[expr_,
477            f:TraditionalForm|StandardForm|OutputForm|InputForm|FullForm]"""
478
479        if expr.is_atom():
480            return expr.atom_to_boxes(f, evaluation)
481        else:
482            head = expr.head
483            leaves = expr.leaves
484
485            f_name = f.get_name()
486            if f_name == "System`TraditionalForm":
487                left, right = "(", ")"
488            else:
489                left, right = "[", "]"
490
491            # Parenthesize infix operators at the head of expressions,
492            # like (a + b)[x], but not f[a] in f[a][b].
493            #
494            head_boxes = parenthesize(670, head, MakeBoxes(head, f), False)
495            result = [head_boxes, String(left)]
496
497            if len(leaves) > 1:
498                row = []
499                if f_name in (
500                    "System`InputForm",
501                    "System`OutputForm",
502                    "System`FullForm",
503                ):
504                    sep = ", "
505                else:
506                    sep = ","
507                for index, leaf in enumerate(leaves):
508                    if index > 0:
509                        row.append(String(sep))
510                    row.append(MakeBoxes(leaf, f))
511                result.append(RowBox(Expression(SymbolList, *row)))
512            elif len(leaves) == 1:
513                result.append(MakeBoxes(leaves[0], f))
514            result.append(String(right))
515            return RowBox(Expression(SymbolList, *result))
516
517    def _apply_atom(self, x, f, evaluation):
518        """MakeBoxes[x_?AtomQ,
519            f:TraditionalForm|StandardForm|OutputForm|InputForm|FullForm]"""
520
521        return x.atom_to_boxes(f, evaluation)
522
523    def apply_outerprecedenceform(self, expr, prec, f, evaluation):
524        """MakeBoxes[OuterPrecedenceForm[expr_, prec_],
525            f:StandardForm|TraditionalForm|OutputForm|InputForm]"""
526
527        precedence = prec.get_int_value()
528        boxes = MakeBoxes(expr)
529        return parenthesize(precedence, expr, boxes, True)
530
531    def apply_postprefix(self, p, expr, h, prec, f, evaluation):
532        """MakeBoxes[(p:Prefix|Postfix)[expr_, h_, prec_:None],
533            f:StandardForm|TraditionalForm|OutputForm|InputForm]"""
534
535        if not isinstance(h, String):
536            h = MakeBoxes(h, f)
537
538        precedence = prec.get_int_value()
539
540        leaves = expr.get_leaves()
541        if len(leaves) == 1:
542            leaf = leaves[0]
543            leaf_boxes = MakeBoxes(leaf, f)
544            leaf = parenthesize(precedence, leaf, leaf_boxes, True)
545            if p.get_name() == "System`Postfix":
546                args = (leaf, h)
547            else:
548                args = (h, leaf)
549
550            return Expression("RowBox", Expression(SymbolList, *args))
551        else:
552            return MakeBoxes(expr, f)
553
554    def apply_infix(self, expr, h, prec, grouping, f, evaluation):
555        """MakeBoxes[Infix[expr_, h_, prec_:None, grouping_:None],
556            f:StandardForm|TraditionalForm|OutputForm|InputForm]"""
557
558        def get_op(op):
559            if not isinstance(op, String):
560                op = MakeBoxes(op, f)
561            else:
562                op_value = op.get_string_value()
563                if f.get_name() == "System`InputForm" and op_value in ["*", "^"]:
564                    pass
565                elif (
566                    f.get_name() in ("System`InputForm", "System`OutputForm")
567                    and not op_value.startswith(" ")
568                    and not op_value.endswith(" ")
569                ):
570                    op = String(" " + op_value + " ")
571            return op
572
573        precedence = prec.get_int_value()
574        grouping = grouping.get_name()
575
576        leaves = expr.get_leaves()
577        if len(leaves) > 1:
578            if h.has_form("List", len(leaves) - 1):
579                ops = [get_op(op) for op in h.leaves]
580            else:
581                ops = [get_op(h)] * (len(leaves) - 1)
582            return make_boxes_infix(leaves, ops, precedence, grouping, f)
583        elif len(leaves) == 1:
584            return MakeBoxes(leaves[0], f)
585        else:
586            return MakeBoxes(expr, f)
587
588
589class ToBoxes(Builtin):
590    """
591    <dl>
592    <dt>'ToBoxes[$expr$]'
593        <dd>evaluates $expr$ and converts the result to box form.
594    </dl>
595
596    Unlike 'MakeBoxes', 'ToBoxes' evaluates its argument:
597    >> ToBoxes[a + a]
598     = RowBox[{2,  , a}]
599
600    >> ToBoxes[a + b]
601     = RowBox[{a, +, b}]
602    >> ToBoxes[a ^ b] // FullForm
603     = SuperscriptBox["a", "b"]
604    """
605
606    def apply(self, expr, form, evaluation):
607        "ToBoxes[expr_, form_:StandardForm]"
608
609        form_name = form.get_name()
610        if form_name is None:
611            evaluation.message("ToBoxes", "boxfmt", form)
612        boxes = expr.format(evaluation, form_name)
613        return boxes
614
615
616class RowBox(Builtin):
617    """
618    <dl>
619    <dt>'RowBox[{...}]'
620        <dd>is a box construct that represents a sequence of boxes
621        arranged in a horizontal row.
622    </dl>
623    """
624
625
626class BoxData(Builtin):
627    """
628    <dl>
629    <dt>'BoxData[...]'
630        <dd>is a low-level representation of the contents of a typesetting
631    cell.
632    </dl>
633    """
634
635
636class TextData(Builtin):
637    """
638    <dl>
639    <dt>'TextData[...]'
640        <dd>is a low-level representation of the contents of a textual
641    cell.
642    </dl>
643    """
644
645
646class TooltipBox(Builtin):
647    """
648    <dl>
649    <dt>'TooltipBox[{...}]'
650        <dd>undocumented...
651    </dl>
652    """
653
654
655class InterpretationBox(Builtin):
656    """
657    <dl>
658    <dt>'InterpretationBox[{...}]'
659        <dd> is a low-level box construct that displays as
660    boxes but is interpreted on input as expr.
661    </dl>
662    """
663
664    attributes = ("HoldAllComplete", "Protected", "ReadProtected")
665
666
667class StyleBox(Builtin):
668    """
669    <dl>
670    <dt>'StyleBox[boxes, options]'
671        <dd> is a low-level representation of boxes
672     to be shown with the specified option settings.
673    <dt>'StyleBox[boxes, style]'
674        <dd> uses the option setting for the specified style in
675    the current notebook.
676    </dl>
677    """
678
679    attributes = ("Protected", "ReadProtected")
680
681
682class ButtonBox(Builtin):
683    """
684    <dl>
685    <dt>'ButtonBox[$boxes$]'
686        <dd> is a low-level box construct that represents a button in a
687    notebook expression.
688    </dl>
689    """
690
691    attributes = ("Protected", "ReadProtected")
692
693
694class TagBox(Builtin):
695    """
696    <dl>
697    <dt>'TagBox[boxes, tag]'
698        <dd> is a low-level box construct that displays as
699    boxes but is interpreted on input as expr
700    </dl>
701    """
702
703    attributes = ("HoldAllComplete", "Protected", "ReadProtected")
704
705
706class TemplateBox(Builtin):
707    """
708    <dl>
709    <dt>'TemplateBox[{$box_1$, $box_2$,...}, tag]'
710        <dd>is a low-level box structure that parameterizes the display and evaluation     of the boxes $box_i$ .
711    </dl>
712    """
713
714    attributes = ("HoldAllComplete", "Protected", "ReadProtected")
715
716
717class Row(Builtin):
718    """
719    <dl>
720    <dt>'Row[{$expr$, ...}]'
721        <dd>formats several expressions inside a 'RowBox'.
722    </dl>
723    """
724
725    def apply_makeboxes(self, items, sep, f, evaluation):
726        """MakeBoxes[Row[{items___}, sep_:""],
727            f:StandardForm|TraditionalForm|OutputForm]"""
728
729        items = items.get_sequence()
730        if not isinstance(sep, String):
731            sep = MakeBoxes(sep, f)
732        if len(items) == 1:
733            return MakeBoxes(items[0], f)
734        else:
735            result = []
736            for index, item in enumerate(items):
737                if index > 0 and not sep.sameQ(String("")):
738                    result.append(sep)
739                result.append(MakeBoxes(item, f))
740            return RowBox(Expression(SymbolList, *result))
741
742
743def is_constant(list):
744    if list:
745        return all(item == list[0] for item in list[1:])
746    return True
747
748
749class GridBox(BoxConstruct):
750    r"""
751    <dl>
752    <dt>'GridBox[{{...}, {...}}]'
753        <dd>is a box construct that represents a sequence of boxes
754        arranged in a grid.
755    </dl>
756
757    #> Grid[{{a,bc},{d,e}}, ColumnAlignments:>Symbol["Rig"<>"ht"]]
758     = a   bc
759     .
760     . d   e
761
762    #> TeXForm@Grid[{{a,bc},{d,e}}, ColumnAlignments->Left]
763     = \begin{array}{ll} a & \text{bc}\\ d & e\end{array}
764
765    #> TeXForm[TableForm[{{a,b},{c,d}}]]
766     = \begin{array}{cc} a & b\\ c & d\end{array}
767
768    # >> MathMLForm[TableForm[{{a,b},{c,d}}]]
769    #  = ...
770    """
771
772    options = {"ColumnAlignments": "Center"}
773
774    def get_array(self, leaves, evaluation):
775        options = self.get_option_values(leaves=leaves[1:], evaluation=evaluation)
776        if not leaves:
777            raise BoxConstructError
778        expr = leaves[0]
779        if not expr.has_form("List", None):
780            if not all(leaf.has_form("List", None) for leaf in expr.leaves):
781                raise BoxConstructError
782        items = [leaf.leaves for leaf in expr.leaves]
783        if not is_constant([len(row) for row in items]):
784            raise BoxConstructError
785        return items, options
786
787    def boxes_to_tex(self, leaves=None, **box_options) -> str:
788        if not leaves:
789            leaves = self._leaves
790        evaluation = box_options.get("evaluation")
791        items, options = self.get_array(leaves, evaluation)
792        new_box_options = box_options.copy()
793        new_box_options["inside_list"] = True
794        column_alignments = options["System`ColumnAlignments"].get_name()
795        try:
796            column_alignments = {
797                "System`Center": "c",
798                "System`Left": "l",
799                "System`Right": "r",
800            }[column_alignments]
801        except KeyError:
802            # invalid column alignment
803            raise BoxConstructError
804        column_count = 0
805        for row in items:
806            column_count = max(column_count, len(row))
807        result = r"\begin{array}{%s} " % (column_alignments * column_count)
808        for index, row in enumerate(items):
809            result += " & ".join(
810                item.evaluate(evaluation).boxes_to_tex(**new_box_options)
811                for item in row
812            )
813            if index != len(items) - 1:
814                result += "\\\\ "
815        result += r"\end{array}"
816        return result
817
818    def boxes_to_mathml(self, leaves=None, **box_options) -> str:
819        if not leaves:
820            leaves = self._leaves
821        evaluation = box_options.get("evaluation")
822        items, options = self.get_array(leaves, evaluation)
823        attrs = {}
824        column_alignments = options["System`ColumnAlignments"].get_name()
825        try:
826            attrs["columnalign"] = {
827                "System`Center": "center",
828                "System`Left": "left",
829                "System`Right": "right",
830            }[column_alignments]
831        except KeyError:
832            # invalid column alignment
833            raise BoxConstructError
834        joined_attrs = " ".join(
835            '{0}="{1}"'.format(name, value) for name, value in attrs.items()
836        )
837        result = "<mtable {0}>\n".format(joined_attrs)
838        new_box_options = box_options.copy()
839        new_box_options["inside_list"] = True
840        for row in items:
841            result += "<mtr>"
842            for item in row:
843                result += "<mtd {0}>{1}</mtd>".format(
844                    joined_attrs,
845                    item.evaluate(evaluation).boxes_to_mathml(**new_box_options),
846                )
847            result += "</mtr>\n"
848        result += "</mtable>"
849        return result
850
851    def boxes_to_text(self, leaves=None, **box_options) -> str:
852        if not leaves:
853            leaves = self._leaves
854        evaluation = box_options.get("evaluation")
855        items, options = self.get_array(leaves, evaluation)
856        result = ""
857        if not items:
858            return ""
859        widths = [0] * len(items[0])
860        cells = [
861            [
862                item.evaluate(evaluation).boxes_to_text(**box_options).splitlines()
863                for item in row
864            ]
865            for row in items
866        ]
867        for row in cells:
868            for index, cell in enumerate(row):
869                if index >= len(widths):
870                    raise BoxConstructError
871                for line in cell:
872                    widths[index] = max(widths[index], len(line))
873        for row_index, row in enumerate(cells):
874            if row_index > 0:
875                result += "\n"
876            k = 0
877            while True:
878                line_exists = False
879                line = ""
880                for cell_index, cell in enumerate(row):
881                    if len(cell) > k:
882                        line_exists = True
883                        text = cell[k]
884                    else:
885                        text = ""
886                    line += text
887                    if cell_index < len(row) - 1:
888                        line += " " * (widths[cell_index] - len(text))
889                        # if cell_index < len(row) - 1:
890                        line += "   "
891                if line_exists:
892                    result += line + "\n"
893                else:
894                    break
895                k += 1
896        return result
897
898
899class Grid(Builtin):
900    """
901    <dl>
902    <dt>'Grid[{{$a1$, $a2$, ...}, {$b1$, $b2$, ...}, ...}]'
903        <dd>formats several expressions inside a 'GridBox'.
904    </dl>
905
906    >> Grid[{{a, b}, {c, d}}]
907     = a   b
908     .
909     . c   d
910    """
911
912    options = GridBox.options
913
914    def apply_makeboxes(self, array, f, evaluation, options) -> Expression:
915        """MakeBoxes[Grid[array_?MatrixQ, OptionsPattern[Grid]],
916            f:StandardForm|TraditionalForm|OutputForm]"""
917        return GridBox(
918            Expression(
919                "List",
920                *(
921                    Expression(
922                        "List",
923                        *(Expression(SymbolMakeBoxes, item, f) for item in row.leaves)
924                    )
925                    for row in array.leaves
926                )
927            ),
928            *options_to_rules(options)
929        )
930
931
932#        return Expression('GridBox',Expression('List', *(Expression('List', *(Expression('MakeBoxes', item, f) for item in row.leaves)) for row in array.leaves)),            *options_to_rules(options))
933
934
935class TableForm(Builtin):
936    """
937    <dl>
938    <dt>'TableForm[$expr$]'
939        <dd>displays $expr$ as a table.
940    </dl>
941
942    >> TableForm[Array[a, {3,2}],TableDepth->1]
943     = {a[1, 1], a[1, 2]}
944     .
945     . {a[2, 1], a[2, 2]}
946     .
947     . {a[3, 1], a[3, 2]}
948
949    A table of Graphics:
950    >> Table[Style[Graphics[{EdgeForm[{Black}], RGBColor[r,g,b], Rectangle[]}], ImageSizeMultipliers->{0.2, 1}], {r,0,1,1/2}, {g,0,1,1/2}, {b,0,1,1/2}] // TableForm
951     = -Graphics-   -Graphics-   -Graphics-
952     .
953     . -Graphics-   -Graphics-   -Graphics-
954     .
955     . -Graphics-   -Graphics-   -Graphics-
956     .
957     . -Graphics-   -Graphics-   -Graphics-
958     .
959     . -Graphics-   -Graphics-   -Graphics-
960     .
961     . -Graphics-   -Graphics-   -Graphics-
962     .
963     . -Graphics-   -Graphics-   -Graphics-
964     .
965     . -Graphics-   -Graphics-   -Graphics-
966     .
967     . -Graphics-   -Graphics-   -Graphics-
968
969    #> TableForm[{}]
970     = #<--#
971    """
972
973    options = {"TableDepth": "Infinity"}
974
975    def apply_makeboxes(self, table, f, evaluation, options):
976        """MakeBoxes[%(name)s[table_, OptionsPattern[%(name)s]],
977            f:StandardForm|TraditionalForm|OutputForm]"""
978
979        dims = len(get_dimensions(table, head=Symbol("List")))
980        depth = self.get_option(options, "TableDepth", evaluation).unformatted
981        depth = expr_min((Integer(dims), depth))
982        depth = depth.get_int_value()
983        if depth is None:
984            evaluation.message(self.get_name(), "int")
985            return
986
987        if depth <= 0:
988            return Expression(SymbolMakeBoxes, table, f)
989        elif depth == 1:
990            return GridBox(
991                Expression(
992                    "List",
993                    *(
994                        Expression(SymbolList, Expression(SymbolMakeBoxes, item, f))
995                        for item in table.leaves
996                    )
997                )
998            )
999            # return Expression(
1000            #    'GridBox', Expression('List', *(
1001            #        Expression('List', Expression('MakeBoxes', item, f))
1002            #        for item in table.leaves)))
1003        else:
1004            new_depth = Expression(SymbolRule, Symbol("TableDepth"), depth - 2)
1005
1006            def transform_item(item):
1007                if depth > 2:
1008                    return Expression(self.get_name(), item, new_depth)
1009                else:
1010                    return item
1011
1012            return GridBox(
1013                Expression(
1014                    "List",
1015                    *(
1016                        Expression(
1017                            "List",
1018                            *(
1019                                Expression(SymbolMakeBoxes, transform_item(item), f)
1020                                for item in row.leaves
1021                            )
1022                        )
1023                        for row in table.leaves
1024                    )
1025                )
1026            )
1027
1028
1029class MatrixForm(TableForm):
1030    """
1031    <dl>
1032    <dt>'MatrixForm[$m$]'
1033        <dd>displays a matrix $m$, hiding the underlying list
1034        structure.
1035    </dl>
1036
1037    >> Array[a,{4,3}]//MatrixForm
1038     = a[1, 1]   a[1, 2]   a[1, 3]
1039     .
1040     . a[2, 1]   a[2, 2]   a[2, 3]
1041     .
1042     . a[3, 1]   a[3, 2]   a[3, 3]
1043     .
1044     . a[4, 1]   a[4, 2]   a[4, 3]
1045
1046    ## Issue #182
1047    #> {{2*a, 0},{0,0}}//MatrixForm
1048     = 2 a   0
1049     .
1050     . 0     0
1051    """
1052
1053    def apply_makeboxes_matrix(self, table, f, evaluation, options):
1054        """MakeBoxes[%(name)s[table_, OptionsPattern[%(name)s]],
1055            f:StandardForm|TraditionalForm]"""
1056
1057        result = super(MatrixForm, self).apply_makeboxes(table, f, evaluation, options)
1058        if result.get_head_name() == "System`GridBox":
1059            return Expression(
1060                "RowBox", Expression(SymbolList, String("("), result, String(")"))
1061            )
1062        return result
1063
1064
1065class Superscript(Builtin):
1066    """
1067    <dl>
1068    <dt>'Superscript[$x$, $y$]'
1069        <dd>displays as $x$^$y$.
1070    </dl>
1071
1072    >> Superscript[x,3] // TeXForm
1073     = x^3
1074    """
1075
1076    rules = {
1077        "MakeBoxes[Superscript[x_, y_], f:StandardForm|TraditionalForm]": (
1078            "SuperscriptBox[MakeBoxes[x, f], MakeBoxes[y, f]]"
1079        )
1080    }
1081
1082
1083class SuperscriptBox(Builtin):
1084    pass
1085
1086
1087class Subscript(Builtin):
1088    """
1089    <dl>
1090    <dt>'Subscript[$a$, $i$]'
1091        <dd>displays as $a_i$.
1092    </dl>
1093
1094    >> Subscript[x,1,2,3] // TeXForm
1095     = x_{1,2,3}
1096    """
1097
1098    def apply_makeboxes(self, x, y, f, evaluation) -> Expression:
1099        "MakeBoxes[Subscript[x_, y__], f:StandardForm|TraditionalForm]"
1100
1101        y = y.get_sequence()
1102        return Expression(
1103            "SubscriptBox", Expression(SymbolMakeBoxes, x, f), *list_boxes(y, f)
1104        )
1105
1106
1107class SubscriptBox(Builtin):
1108    pass
1109
1110
1111class Subsuperscript(Builtin):
1112    """
1113    <dl>
1114    <dt>'Subsuperscript[$a$, $b$, $c$]'
1115        <dd>displays as $a_b^c$.
1116    </dl>
1117
1118    >> Subsuperscript[a, b, c] // TeXForm
1119     = a_b^c
1120    """
1121
1122    rules = {
1123        "MakeBoxes[Subsuperscript[x_, y_, z_], "
1124        "f:StandardForm|TraditionalForm]": (
1125            "SubsuperscriptBox[MakeBoxes[x, f], MakeBoxes[y, f], " "MakeBoxes[z, f]]"
1126        )
1127    }
1128
1129
1130class SubsuperscriptBox(Builtin):
1131    pass
1132
1133
1134class Postfix(BinaryOperator):
1135    """
1136    <dl>
1137    <dt>'$x$ // $f$'
1138        <dd>is equivalent to '$f$[$x$]'.
1139    </dl>
1140
1141    >> b // a
1142     = a[b]
1143    >> c // b // a
1144     = a[b[c]]
1145
1146    The postfix operator '//' is parsed to an expression before evaluation:
1147    >> Hold[x // a // b // c // d // e // f]
1148     = Hold[f[e[d[c[b[a[x]]]]]]]
1149    """
1150
1151    operator = "//"
1152    operator_display = None
1153    precedence = 70
1154    grouping = "Left"
1155
1156
1157class Prefix(BinaryOperator):
1158    """
1159    <dl>
1160    <dt>'$f$ @ $x$'
1161        <dd>is equivalent to '$f$[$x$]'.
1162    </dl>
1163
1164    >> a @ b
1165     = a[b]
1166    >> a @ b @ c
1167     = a[b[c]]
1168    >> Format[p[x_]] := Prefix[{x}, "*"]
1169    >> p[3]
1170     = *3
1171    >> Format[q[x_]] := Prefix[{x}, "~", 350]
1172    >> q[a+b]
1173     = ~(a + b)
1174    >> q[a*b]
1175     = ~a b
1176    >> q[a]+b
1177     = b + ~a
1178
1179    The prefix operator '@' is parsed to an expression before evaluation:
1180    >> Hold[a @ b @ c @ d @ e @ f @ x]
1181     = Hold[a[b[c[d[e[f[x]]]]]]]
1182    """
1183
1184    operator = "@"
1185    operator_display = None
1186    precedence = 640
1187    grouping = "Right"
1188
1189
1190class Infix(Builtin):
1191    """
1192    <dl>
1193    <dt>'Infix[$expr$, $oper$, $prec$, $assoc$]'
1194        <dd>displays $expr$ with the infix operator $oper$, with
1195        precedence $prec$ and associativity $assoc$.
1196    </dl>
1197
1198    'Infix' can be used with 'Format' to display certain forms with
1199    user-defined infix notation:
1200    >> Format[g[x_, y_]] := Infix[{x, y}, "#", 350, Left]
1201    >> g[a, g[b, c]]
1202     = a # (b # c)
1203    >> g[g[a, b], c]
1204     = a # b # c
1205    >> g[a + b, c]
1206     = (a + b) # c
1207    >> g[a * b, c]
1208     = a b # c
1209    >> g[a, b] + c
1210     = c + a # b
1211    >> g[a, b] * c
1212     = c (a # b)
1213
1214    >> Infix[{a, b, c}, {"+", "-"}]
1215     = a + b - c
1216
1217    #> Format[r[items___]] := Infix[If[Length[{items}] > 1, {items}, {ab}], "~"]
1218    #> r[1, 2, 3]
1219     = 1 ~ 2 ~ 3
1220    #> r[1]
1221     = ab
1222    """
1223
1224
1225class NonAssociative(Builtin):
1226    """
1227    <dl>
1228    <dt>'NonAssociative'
1229        <dd>is used with operator formatting constructs to specify a
1230        non-associative operator.
1231    </dl>
1232    """
1233
1234
1235class Left(Builtin):
1236    """
1237    <dl>
1238    <dt>'Left'
1239        <dd>is used with operator formatting constructs to specify a
1240        left-associative operator.
1241    </dl>
1242    """
1243
1244
1245class Right(Builtin):
1246    """
1247    <dl>
1248    <dt>'Right'
1249        <dd>is used with operator formatting constructs to specify a
1250        right-associative operator.
1251    </dl>
1252    """
1253
1254
1255class Center(Builtin):
1256    """
1257    <dl>
1258    <dt>'Center'
1259        <dd>is used with the 'ColumnAlignments' option to 'Grid' or
1260        'TableForm' to specify a centered column.
1261    </dl>
1262    """
1263
1264
1265class StringForm(Builtin):
1266    """
1267    <dl>
1268    <dt>'StringForm[$str$, $expr1$, $expr2$, ...]'
1269        <dd>displays the string $str$, replacing placeholders in $str$
1270        with the corresponding expressions.
1271    </dl>
1272
1273    >> StringForm["`1` bla `2` blub `` bla `2`", a, b, c]
1274     = a bla b blub c bla b
1275    """
1276
1277    def apply_makeboxes(self, s, args, f, evaluation):
1278        """MakeBoxes[StringForm[s_String, args___],
1279            f:StandardForm|TraditionalForm|OutputForm]"""
1280
1281        s = s.value
1282        args = args.get_sequence()
1283        result = []
1284        pos = 0
1285        last_index = 0
1286        for match in re.finditer(r"(\`(\d*)\`)", s):
1287            start, end = match.span(1)
1288            if match.group(2):
1289                index = int(match.group(2))
1290            else:
1291                index = last_index + 1
1292            if index > last_index:
1293                last_index = index
1294            if start > pos:
1295                result.append(String(s[pos:start]))
1296            pos = end
1297            if 1 <= index <= len(args):
1298                arg = args[index - 1]
1299                result.append(MakeBoxes(arg, f))
1300        if pos < len(s):
1301            result.append(String(s[pos:]))
1302        return RowBox(Expression(SymbolList, *result))
1303
1304
1305class Message(Builtin):
1306    """
1307    <dl>
1308    <dt>'Message[$symbol$::$msg$, $expr1$, $expr2$, ...]'
1309        <dd>displays the specified message, replacing placeholders in
1310        the message text with the corresponding expressions.
1311    </dl>
1312
1313    >> a::b = "Hello world!"
1314     = Hello world!
1315    >> Message[a::b]
1316     : Hello world!
1317    >> a::c := "Hello `1`, Mr 00`2`!"
1318    >> Message[a::c, "you", 3 + 4]
1319     : Hello you, Mr 007!
1320    """
1321
1322    attributes = ("HoldFirst",)
1323
1324    messages = {
1325        "name": "Message name `1` is not of the form symbol::name or symbol::name::language."
1326    }
1327
1328    def apply(self, symbol, tag, params, evaluation):
1329        "Message[MessageName[symbol_Symbol, tag_String], params___]"
1330
1331        params = params.get_sequence()
1332        evaluation.message(symbol.name, tag.value, *params)
1333        return Symbol("Null")
1334
1335
1336def check_message(expr) -> bool:
1337    "checks if an expression is a valid message"
1338    if expr.has_form("MessageName", 2):
1339        symbol, tag = expr.get_leaves()
1340        if symbol.get_name() and tag.get_string_value():
1341            return True
1342    return False
1343
1344
1345class Check(Builtin):
1346    """
1347    <dl>
1348    <dt>'Check[$expr$, $failexpr$]'
1349        <dd>evaluates $expr$, and returns the result, unless messages were generated, in which case it evaluates and $failexpr$ will be returned.
1350    <dt>'Check[$expr$, $failexpr$, {s1::t1,s2::t2,…}]'
1351        <dd>checks only for the specified messages.
1352    </dl>
1353
1354    Return err when a message is generated:
1355    >> Check[1/0, err]
1356     : Infinite expression 1 / 0 encountered.
1357     = err
1358
1359    #> Check[1^0, err]
1360     = 1
1361
1362    Check only for specific messages:
1363    >> Check[Sin[0^0], err, Sin::argx]
1364     : Indeterminate expression 0 ^ 0 encountered.
1365     = Indeterminate
1366
1367    >> Check[1/0, err, Power::infy]
1368     : Infinite expression 1 / 0 encountered.
1369     = err
1370
1371    #> Check[1 + 2]
1372     : Check called with 1 argument; 2 or more arguments are expected.
1373     = Check[1 + 2]
1374
1375    #> Check[1 + 2, err, 3 + 1]
1376     : Message name 3 + 1 is not of the form symbol::name or symbol::name::language.
1377     = Check[1 + 2, err, 3 + 1]
1378
1379    #> Check[1 + 2, err, hello]
1380     : Message name hello is not of the form symbol::name or symbol::name::language.
1381     = Check[1 + 2, err, hello]
1382
1383    #> Check[1/0, err, Compile::cpbool]
1384     : Infinite expression 1 / 0 encountered.
1385     = ComplexInfinity
1386
1387    #> Check[{0^0, 1/0}, err]
1388     : Indeterminate expression 0 ^ 0 encountered.
1389     : Infinite expression 1 / 0 encountered.
1390     = err
1391
1392    #> Check[0^0/0, err, Power::indet]
1393     : Indeterminate expression 0 ^ 0 encountered.
1394     : Infinite expression 1 / 0 encountered.
1395     = err
1396
1397    #> Check[{0^0, 3/0}, err, Power::indet]
1398     : Indeterminate expression 0 ^ 0 encountered.
1399     : Infinite expression 1 / 0 encountered.
1400     = err
1401
1402    #> Check[1 + 2, err, {a::b, 2 + 5}]
1403     : Message name 2 + 5 is not of the form symbol::name or symbol::name::language.
1404     = Check[1 + 2, err, {a::b, 2 + 5}]
1405
1406    #> Off[Power::infy]
1407    #> Check[1 / 0, err]
1408     = ComplexInfinity
1409
1410    #> On[Power::infy]
1411    #> Check[1 / 0, err]
1412     : Infinite expression 1 / 0 encountered.
1413     = err
1414    """
1415
1416    attributes = ("HoldAll",)
1417
1418    messages = {
1419        "argmu": "Check called with 1 argument; 2 or more arguments are expected.",
1420        "name": "Message name `1` is not of the form symbol::name or symbol::name::language.",
1421    }
1422
1423    def apply_1_argument(self, expr, evaluation):
1424        "Check[expr_]"
1425        return evaluation.message("Check", "argmu")
1426
1427    def apply(self, expr, failexpr, params, evaluation):
1428        "Check[expr_, failexpr_, params___]"
1429
1430        # Todo: To implement the third form of this function , we need to implement the function $MessageGroups first
1431        # <dt>'Check[$expr$, $failexpr$, "name"]'
1432        # <dd>checks only for messages in the named message group.
1433
1434        def get_msg_list(exprs):
1435            messages = []
1436            for expr in exprs:
1437                if expr.has_form("List", None):
1438                    messages.extend(get_msg_list(expr.leaves))
1439                elif check_message(expr):
1440                    messages.append(expr)
1441                else:
1442                    raise Exception(expr)
1443            return messages
1444
1445        check_messages = set(evaluation.get_quiet_messages())
1446        display_fail_expr = False
1447
1448        params = params.get_sequence()
1449        if len(params) == 0:
1450            result = expr.evaluate(evaluation)
1451            if len(evaluation.out):
1452                display_fail_expr = True
1453        else:
1454            try:
1455                msgs = get_msg_list(params)
1456                for x in msgs:
1457                    check_messages.add(x)
1458            except Exception as inst:
1459                evaluation.message("Check", "name", inst.args[0])
1460                return
1461            curr_msg = len(evaluation.out)
1462            result = expr.evaluate(evaluation)
1463            own_messages = evaluation.out[curr_msg:]
1464            for out_msg in own_messages:
1465                if type(out_msg) is not EvaluationMessage:
1466                    continue
1467                pattern = Expression(
1468                    "MessageName", Symbol(out_msg.symbol), String(out_msg.tag)
1469                )
1470                if pattern in check_messages:
1471                    display_fail_expr = True
1472                    break
1473        return failexpr if display_fail_expr is True else result
1474
1475
1476class Quiet(Builtin):
1477    """
1478    <dl>
1479    <dt>'Quiet[$expr$, {$s1$::$t1$, ...}]'
1480        <dd>evaluates $expr$, without messages '{$s1$::$t1$, ...}' being displayed.
1481    <dt>'Quiet[$expr$, All]'
1482        <dd>evaluates $expr$, without any messages being displayed.
1483    <dt>'Quiet[$expr$, None]'
1484        <dd>evaluates $expr$, without all messages being displayed.
1485    <dt>'Quiet[$expr$, $off$, $on$]'
1486        <dd>evaluates $expr$, with messages $off$ being suppressed, but messages $on$ being displayed.
1487    </dl>
1488
1489    Evaluate without generating messages:
1490    >> Quiet[1/0]
1491     = ComplexInfinity
1492
1493    Same as above:
1494    >> Quiet[1/0, All]
1495     = ComplexInfinity
1496
1497    >> a::b = "Hello";
1498    >> Quiet[x+x, {a::b}]
1499     = 2 x
1500
1501    >> Quiet[Message[a::b]; x+x, {a::b}]
1502     = 2 x
1503
1504    >> Message[a::b]; y=Quiet[Message[a::b]; x+x, {a::b}]; Message[a::b]; y
1505     : Hello
1506     : Hello
1507     = 2 x
1508
1509    #> Quiet[expr, All, All]
1510     : Arguments 2 and 3 of Quiet[expr, All, All] should not both be All.
1511     = Quiet[expr, All, All]
1512    >> Quiet[x + x, {a::b}, {a::b}]
1513     : In Quiet[x + x, {a::b}, {a::b}] the message name(s) {a::b} appear in both the list of messages to switch off and the list of messages to switch on.
1514     = Quiet[x + x, {a::b}, {a::b}]
1515    """
1516
1517    attributes = ("HoldAll",)
1518
1519    messages = {
1520        "anmlist": (
1521            "Argument `1` of `2` should be All, None, a message name, "
1522            "or a list of message names."
1523        ),
1524        "allall": "Arguments 2 and 3 of `1` should not both be All.",
1525        "conflict": (
1526            "In `1` the message name(s) `2` appear in both the list of "
1527            "messages to switch off and the list of messages to switch on."
1528        ),
1529    }
1530
1531    rules = {
1532        "Quiet[expr_]": "Quiet[expr, All]",
1533        "Quiet[expr_, moff_]": "Quiet[expr, moff, None]",
1534    }
1535
1536    def apply(self, expr, moff, mon, evaluation):
1537        "Quiet[expr_, moff_, mon_]"
1538
1539        def get_msg_list(expr):
1540            if check_message(expr):
1541                expr = Expression(SymbolList, expr)
1542            if expr.get_name() == "System`All":
1543                all = True
1544                messages = []
1545            elif expr.get_name() == "System`None":
1546                all = False
1547                messages = []
1548            elif expr.has_form("List", None):
1549                all = False
1550                messages = []
1551                for item in expr.leaves:
1552                    if check_message(item):
1553                        messages.append(item)
1554                    else:
1555                        raise ValueError
1556            else:
1557                raise ValueError
1558            return all, messages
1559
1560        old_quiet_all = evaluation.quiet_all
1561        old_quiet_messages = set(evaluation.get_quiet_messages())
1562        quiet_messages = old_quiet_messages.copy()
1563        try:
1564            quiet_expr = Expression("Quiet", expr, moff, mon)
1565            try:
1566                off_all, off_messages = get_msg_list(moff)
1567            except ValueError:
1568                evaluation.message("Quiet", "anmlist", 2, quiet_expr)
1569                return
1570            try:
1571                on_all, on_messages = get_msg_list(mon)
1572            except ValueError:
1573                evaluation.message("Quiet", "anmlist", 2, quiet_expr)
1574                return
1575            if off_all and on_all:
1576                evaluation.message("Quiet", "allall", quiet_expr)
1577                return
1578            evaluation.quiet_all = off_all
1579            conflict = []
1580            for off in off_messages:
1581                if off in on_messages:
1582                    conflict.append(off)
1583                    break
1584            if conflict:
1585                evaluation.message(
1586                    "Quiet", "conflict", quiet_expr, Expression(SymbolList, *conflict)
1587                )
1588                return
1589            for off in off_messages:
1590                quiet_messages.add(off)
1591            for on in on_messages:
1592                quiet_messages.discard(on)
1593            if on_all:
1594                quiet_messages = set()
1595            evaluation.set_quiet_messages(quiet_messages)
1596
1597            return expr.evaluate(evaluation)
1598        finally:
1599            evaluation.quiet_all = old_quiet_all
1600            evaluation.set_quiet_messages(old_quiet_messages)
1601
1602
1603class Off(Builtin):
1604    """
1605    <dl>
1606    <dt>'Off[$symbol$::$tag$]'
1607        <dd>turns a message off so it is no longer printed.
1608    </dl>
1609
1610    >> Off[Power::infy]
1611    >> 1 / 0
1612     = ComplexInfinity
1613
1614    >> Off[Power::indet, Syntax::com]
1615    >> {0 ^ 0,}
1616     = {Indeterminate, Null}
1617
1618    #> Off[1]
1619     :  Message name 1 is not of the form symbol::name or symbol::name::language.
1620    #> Off[Message::name, 1]
1621
1622    #> On[Power::infy, Power::indet, Syntax::com]
1623    """
1624
1625    attributes = ("HoldAll",)
1626
1627    def apply(self, expr, evaluation):
1628        "Off[expr___]"
1629
1630        seq = expr.get_sequence()
1631        quiet_messages = set(evaluation.get_quiet_messages())
1632
1633        if not seq:
1634            # TODO Off[s::trace] for all symbols
1635            return
1636
1637        for e in seq:
1638            if isinstance(e, Symbol):
1639                quiet_messages.add(Expression("MessageName", e, String("trace")))
1640            elif check_message(e):
1641                quiet_messages.add(e)
1642            else:
1643                evaluation.message("Message", "name", e)
1644            evaluation.set_quiet_messages(quiet_messages)
1645
1646        return Symbol("Null")
1647
1648
1649class On(Builtin):
1650    """
1651    <dl>
1652    <dt>'On[$symbol$::$tag$]'
1653        <dd>turns a message on for printing.
1654    </dl>
1655
1656    >> Off[Power::infy]
1657    >> 1 / 0
1658     = ComplexInfinity
1659    >> On[Power::infy]
1660    >> 1 / 0
1661     : Infinite expression 1 / 0 encountered.
1662     = ComplexInfinity
1663    """
1664
1665    # TODO
1666    """
1667    #> On[f::x]
1668     : Message f::x not found.
1669    """
1670
1671    attributes = ("HoldAll",)
1672
1673    def apply(self, expr, evaluation):
1674        "On[expr___]"
1675
1676        seq = expr.get_sequence()
1677        quiet_messages = set(evaluation.get_quiet_messages())
1678
1679        if not seq:
1680            # TODO On[s::trace] for all symbols
1681            return
1682
1683        for e in seq:
1684            if isinstance(e, Symbol):
1685                quiet_messages.discard(Expression("MessageName", e, String("trace")))
1686            elif check_message(e):
1687                quiet_messages.discard(e)
1688            else:
1689                evaluation.message("Message", "name", e)
1690            evaluation.set_quiet_messages(quiet_messages)
1691        return Symbol("Null")
1692
1693
1694class MessageName(BinaryOperator):
1695    """
1696    <dl>
1697    <dt>'MessageName[$symbol$, $tag$]'</dt>
1698    <dt>'$symbol$::$tag$'</dt>
1699        <dd>identifies a message.
1700    </dl>
1701
1702    'MessageName' is the head of message IDs of the form 'symbol::tag'.
1703    >> FullForm[a::b]
1704     = MessageName[a, "b"]
1705
1706    The second parameter 'tag' is interpreted as a string.
1707    >> FullForm[a::"b"]
1708     = MessageName[a, "b"]
1709    """
1710
1711    messages = {"messg": "Message cannot be set to `1`. It must be set to a string."}
1712
1713    operator = "::"
1714    precedence = 750
1715    attributes = ("HoldFirst",)
1716
1717    default_formats = False
1718
1719    formats: typing.Dict[str, Any] = {}
1720
1721    rules = {
1722        "MakeBoxes[MessageName[symbol_Symbol, tag_String], "
1723        "f:StandardForm|TraditionalForm|OutputForm]": (
1724            'RowBox[{MakeBoxes[symbol, f], "::", MakeBoxes[tag, f]}]'
1725        ),
1726        "MakeBoxes[MessageName[symbol_Symbol, tag_String], InputForm]": (
1727            'RowBox[{MakeBoxes[symbol, InputForm], "::", tag}]'
1728        ),
1729    }
1730
1731    def apply(self, symbol, tag, evaluation):
1732        "MessageName[symbol_Symbol, tag_String]"
1733
1734        pattern = Expression("MessageName", symbol, tag)
1735        return evaluation.definitions.get_value(
1736            symbol.get_name(), "System`Messages", pattern, evaluation
1737        )
1738
1739
1740class Syntax(Builtin):
1741    r"""
1742    <dl>
1743    <dt>'Syntax'
1744        <dd>is a symbol to which all syntax messages are assigned.
1745    </dl>
1746
1747    >> 1 +
1748     : Incomplete expression; more input is needed (line 1 of "<test>").
1749
1750    >> Sin[1)
1751     : "Sin[1" cannot be followed by ")" (line 1 of "<test>").
1752
1753    >> ^ 2
1754     : Expression cannot begin with "^ 2" (line 1 of "<test>").
1755
1756    >> 1.5``
1757     : "1.5`" cannot be followed by "`" (line 1 of "<test>").
1758
1759    #> (x]
1760     : "(x" cannot be followed by "]" (line 1 of "<test>").
1761
1762    #> (x,)
1763     : "(x" cannot be followed by ",)" (line 1 of "<test>").
1764
1765    #> {x]
1766     : "{x" cannot be followed by "]" (line 1 of "<test>").
1767
1768    #> f[x)
1769     : "f[x" cannot be followed by ")" (line 1 of "<test>").
1770
1771    #> a[[x)]
1772     : "a[[x" cannot be followed by ")]" (line 1 of "<test>").
1773
1774    #> x /: y , z
1775     : "x /: y " cannot be followed by ", z" (line 1 of "<test>").
1776
1777    #> a :: 1
1778     : "a :: " cannot be followed by "1" (line 1 of "<test>").
1779
1780    #> a ? b ? c
1781     : "a ? b " cannot be followed by "? c" (line 1 of "<test>").
1782
1783    #> \:000G
1784     : 4 hexadecimal digits are required after \: to construct a 16-bit character (line 1 of "<test>").
1785     : Expression cannot begin with "\:000G" (line 1 of "<test>").
1786
1787    #> \:000
1788     : 4 hexadecimal digits are required after \: to construct a 16-bit character (line 1 of "<test>").
1789     : Expression cannot begin with "\:000" (line 1 of "<test>").
1790
1791    #> \009
1792     : 3 octal digits are required after \ to construct an 8-bit character (line 1 of "<test>").
1793     : Expression cannot begin with "\009" (line 1 of "<test>").
1794
1795    #> \00
1796     : 3 octal digits are required after \ to construct an 8-bit character (line 1 of "<test>").
1797     : Expression cannot begin with "\00" (line 1 of "<test>").
1798
1799    #> \.0G
1800     : 2 hexadecimal digits are required after \. to construct an 8-bit character (line 1 of "<test>").
1801     : Expression cannot begin with "\.0G" (line 1 of "<test>").
1802
1803    #> \.0
1804     : 2 hexadecimal digits are required after \. to construct an 8-bit character (line 1 of "<test>").
1805     : Expression cannot begin with "\.0" (line 1 of "<test>").
1806
1807    #> "abc \[fake]"
1808     : Unknown unicode longname "fake" (line 1 of "<test>").
1809     = abc \[fake]
1810
1811    #> a ~ b + c
1812     : "a ~ b " cannot be followed by "+ c" (line 1 of "<test>").
1813
1814    #> {1,}
1815     : Warning: comma encountered with no adjacent expression. The expression will be treated as Null (line 1 of "<test>").
1816     = {1, Null}
1817    #> {, 1}
1818     : Warning: comma encountered with no adjacent expression. The expression will be treated as Null (line 1 of "<test>").
1819     = {Null, 1}
1820    #> {,,}
1821     : Warning: comma encountered with no adjacent expression. The expression will be treated as Null (line 1 of "<test>").
1822     : Warning: comma encountered with no adjacent expression. The expression will be treated as Null (line 1 of "<test>").
1823     : Warning: comma encountered with no adjacent expression. The expression will be treated as Null (line 1 of "<test>").
1824     = {Null, Null, Null}
1825    """
1826
1827    # Extension: MMA does not provide lineno and filename in its error messages
1828    messages = {
1829        "snthex": r"4 hexadecimal digits are required after \: to construct a 16-bit character (line `4` of `5`).",
1830        "sntoct1": r"3 octal digits are required after \ to construct an 8-bit character (line `4` of `5`).",
1831        "sntoct2": r"2 hexadecimal digits are required after \. to construct an 8-bit character (line `4` of `5`).",
1832        "sntxi": "Incomplete expression; more input is needed (line `4` of `5`).",
1833        "sntxb": "Expression cannot begin with `1` (line `4` of `5`).",
1834        "sntxf": "`1` cannot be followed by `2` (line `4` of `5`).",
1835        "bktwrn": "`1` represents multiplication; use `2` to represent a function (line `4` of `5`).",  # TODO
1836        "bktmch": "`1` must be followed by `2`, not `3` (line `4` of `5`).",
1837        "sntue": "Unexpected end of file; probably unfinished expression (line `4` of `5`).",
1838        "sntufn": "Unknown unicode longname `1` (line `4` of `5`).",
1839        "com": "Warning: comma encountered with no adjacent expression. The expression will be treated as Null (line `4` of `5`).",
1840    }
1841
1842
1843class General(Builtin):
1844    """
1845    <dl>
1846    <dt>'General'
1847        <dd>is a symbol to which all general-purpose messages are assigned.
1848    </dl>
1849
1850    >> General::argr
1851     = `1` called with 1 argument; `2` arguments are expected.
1852    >> Message[Rule::argr, Rule, 2]
1853     : Rule called with 1 argument; 2 arguments are expected.
1854    """
1855
1856    messages = {
1857        "argb": (
1858            "`1` called with `2` arguments; "
1859            "between `3` and `4` arguments are expected."
1860        ),
1861        "argct": "`1` called with `2` arguments.",
1862        "argctu": "`1` called with 1 argument.",
1863        "argr": "`1` called with 1 argument; `2` arguments are expected.",
1864        "argrx": "`1` called with `2` arguments; `3` arguments are expected.",
1865        "argx": "`1` called with `2` arguments; 1 argument is expected.",
1866        "argt": (
1867            "`1` called with `2` arguments; " "`3` or `4` arguments are expected."
1868        ),
1869        "argtu": ("`1` called with 1 argument; `2` or `3` arguments are expected."),
1870        "base": "Requested base `1` in `2` should be between 2 and `3`.",
1871        "boxfmt": "`1` is not a box formatting type.",
1872        "color": "`1` is not a valid color or gray-level specification.",
1873        "cxt": "`1` is not a valid context name.",
1874        "divz": "The argument `1` should be nonzero.",
1875        "digit": "Digit at position `1` in `2` is too large to be used in base `3`.",
1876        "exact": "Argument `1` is not an exact number.",
1877        "fnsym": (
1878            "First argument in `1` is not a symbol " "or a string naming a symbol."
1879        ),
1880        "heads": "Heads `1` and `2` are expected to be the same.",
1881        "ilsnn": (
1882            "Single or list of non-negative integers expected at " "position `1`."
1883        ),
1884        "indet": "Indeterminate expression `1` encountered.",
1885        "innf": "Non-negative integer or Infinity expected at position `1`.",
1886        "int": "Integer expected.",
1887        "intp": "Positive integer expected.",
1888        "intnn": "Non-negative integer expected.",
1889        "iterb": "Iterator does not have appropriate bounds.",
1890        "ivar": "`1` is not a valid variable.",
1891        "level": ("Level specification `1` is not of the form n, " "{n}, or {m, n}."),
1892        "locked": "Symbol `1` is locked.",
1893        "matsq": "Argument `1` is not a non-empty square matrix.",
1894        "newpkg": "In WL, there is a new package for this.",
1895        "noopen": "Cannot open `1`.",
1896        "nord": "Invalid comparison with `1` attempted.",
1897        "normal": "Nonatomic expression expected.",
1898        "noval": ("Symbol `1` in part assignment does not have an immediate value."),
1899        "obspkg": "In WL, this package is obsolete.",
1900        "openx": "`1` is not open.",
1901        "optb": "Optional object `1` in `2` is not a single blank.",
1902        "ovfl": "Overflow occurred in computation.",
1903        "partd": "Part specification is longer than depth of object.",
1904        "partw": "Part `1` of `2` does not exist.",
1905        "plld": "Endpoints in `1` must be distinct machine-size real numbers.",
1906        "plln": "Limiting value `1` in `2` is not a machine-size real number.",
1907        "pspec": (
1908            "Part specification `1` is neither an integer nor " "a list of integer."
1909        ),
1910        "seqs": "Sequence specification expected, but got `1`.",
1911        "setp": "Part assignment to `1` could not be made",
1912        "setps": "`1` in the part assignment is not a symbol.",
1913        "span": "`1` is not a valid Span specification.",
1914        "stream": "`1` is not string, InputStream[], or OutputStream[]",
1915        "string": "String expected.",
1916        "sym": "Argument `1` at position `2` is expected to be a symbol.",
1917        "tag": "Rule for `1` can only be attached to `2`.",
1918        "take": "Cannot take positions `1` through `2` in `3`.",
1919        "vrule": (
1920            "Cannot set `1` to `2`, " "which is not a valid list of replacement rules."
1921        ),
1922        "write": "Tag `1` in `2` is Protected.",
1923        "wrsym": "Symbol `1` is Protected.",
1924        "ucdec": "An invalid unicode sequence was encountered and ignored.",
1925        "charcode": "The character encoding `1` is not supported. Use $CharacterEncodings to list supported encodings.",
1926        # Self-defined messages
1927        # 'rep': "`1` is not a valid replacement rule.",
1928        "options": "`1` is not a valid list of option rules.",
1929        "timeout": "Timeout reached.",
1930        "syntax": "`1`",
1931        "invalidargs": "Invalid arguments.",
1932        "notboxes": "`1` is not a valid box structure.",
1933        "pyimport": '`1`[] is not available. Your Python installation misses the "`2`" module.',
1934    }
1935
1936
1937class Print(Builtin):
1938    """
1939    <dl>
1940    <dt>'Print[$expr$, ...]'
1941        <dd>prints each $expr$ in string form.
1942    </dl>
1943
1944    >> Print["Hello world!"]
1945     | Hello world!
1946    >> Print["The answer is ", 7 * 6, "."]
1947     | The answer is 42.
1948
1949    #> Print["-Hola\\n-Qué tal?"]
1950     | -Hola
1951     . -Qué tal?
1952    """
1953
1954    def apply(self, expr, evaluation):
1955        "Print[expr__]"
1956
1957        expr = expr.get_sequence()
1958        expr = Expression("Row", Expression(SymbolList, *expr))
1959        evaluation.print_out(expr)
1960        return Symbol("Null")
1961
1962
1963class FullForm(Builtin):
1964    """
1965    <dl>
1966    <dt>'FullForm[$expr$]'
1967        <dd>displays the underlying form of $expr$.
1968    </dl>
1969
1970    >> FullForm[a + b * c]
1971     = Plus[a, Times[b, c]]
1972    >> FullForm[2/3]
1973     = Rational[2, 3]
1974    >> FullForm["A string"]
1975     = "A string"
1976    """
1977
1978
1979class StandardForm(Builtin):
1980    """
1981    <dl>
1982    <dt>'StandardForm[$expr$]'
1983        <dd>displays $expr$ in the default form.
1984    </dl>
1985
1986    >> StandardForm[a + b * c]
1987     = a + b c
1988    >> StandardForm["A string"]
1989     = A string
1990    'StandardForm' is used by default:
1991    >> "A string"
1992     = A string
1993    >> f'[x]
1994     = f'[x]
1995    """
1996
1997
1998class InputForm(Builtin):
1999    r"""
2000    <dl>
2001    <dt>'InputForm[$expr$]'
2002        <dd>displays $expr$ in an unambiguous form suitable for input.
2003    </dl>
2004
2005    >> InputForm[a + b * c]
2006     = a + b*c
2007    >> InputForm["A string"]
2008     = "A string"
2009    >> InputForm[f'[x]]
2010     = Derivative[1][f][x]
2011    >> InputForm[Derivative[1, 0][f][x]]
2012     = Derivative[1, 0][f][x]
2013    #> InputForm[2 x ^ 2 + 4z!]
2014     = 2*x^2 + 4*z!
2015    #> InputForm["\$"]
2016     = "\\$"
2017    """
2018
2019
2020class OutputForm(Builtin):
2021    """
2022    <dl>
2023    <dt>'OutputForm[$expr$]'
2024        <dd>displays $expr$ in a plain-text form.
2025    </dl>
2026
2027    >> OutputForm[f'[x]]
2028     = f'[x]
2029    >> OutputForm[Derivative[1, 0][f][x]]
2030     = Derivative[1, 0][f][x]
2031    >> OutputForm["A string"]
2032     = A string
2033    >> OutputForm[Graphics[Rectangle[]]]
2034     = -Graphics-
2035    """
2036
2037
2038class MathMLForm(Builtin):
2039    """
2040    <dl>
2041    <dt>'MathMLForm[$expr$]'
2042        <dd>displays $expr$ as a MathML expression.
2043    </dl>
2044
2045    >> MathMLForm[HoldForm[Sqrt[a^3]]]
2046     = ...
2047
2048    ## Test cases for Unicode - redo please as a real test
2049    >> MathMLForm[\\[Mu]]
2050    = ...
2051
2052    # This can causes the TeX to fail
2053    # >> MathMLForm[Graphics[Text["\u03bc"]]]
2054    #  = ...
2055
2056    ## The <mo> should contain U+2062 INVISIBLE TIMES
2057    ## MathMLForm[MatrixForm[{{2*a, 0},{0,0}}]]
2058    = ...
2059    """
2060
2061    def apply_mathml(self, expr, evaluation) -> Expression:
2062        "MakeBoxes[expr_, MathMLForm]"
2063
2064        boxes = MakeBoxes(expr).evaluate(evaluation)
2065        try:
2066            xml = boxes.boxes_to_mathml(evaluation=evaluation)
2067        except BoxError:
2068            evaluation.message(
2069                "General",
2070                "notboxes",
2071                Expression("FullForm", boxes).evaluate(evaluation),
2072            )
2073            xml = ""
2074        is_a_picture = xml[:6] == "<mtext"
2075
2076        # mathml = '<math><mstyle displaystyle="true">%s</mstyle></math>' % xml
2077        # #convert_box(boxes)
2078        query = evaluation.parse("Settings`$UseSansSerif")
2079        usesansserif = query.evaluate(evaluation).to_python()
2080        if not is_a_picture:
2081            if isinstance(usesansserif, bool) and usesansserif:
2082                xml = '<mstyle mathvariant="sans-serif">%s</mstyle>' % xml
2083
2084        mathml = '<math display="block">%s</math>' % xml  # convert_box(boxes)
2085        return Expression("RowBox", Expression(SymbolList, String(mathml)))
2086
2087
2088class PythonForm(Builtin):
2089    """
2090    <dl>
2091      <dt>'PythonForm[$expr$]'
2092      <dd>returns an approximate equivalent of $expr$ in Python, when that is possible. We assume
2093      that Python has sympy imported. No explicit import will be include in the result.
2094    </dl>
2095
2096    >> PythonForm[Infinity]
2097    = math.inf
2098    >> PythonForm[Pi]
2099    = sympy.pi
2100    >> E // PythonForm
2101    = sympy.E
2102    >> {1, 2, 3} // PythonForm
2103    = [1, 2, 3]
2104    """
2105
2106    # >> PythonForm[HoldForm[Sqrt[a^3]]]
2107    #  = sympy.sqrt{a**3} # or something like this
2108
2109    def apply_python(self, expr, evaluation) -> Expression:
2110        "MakeBoxes[expr_, PythonForm]"
2111
2112        try:
2113            # from trepan.api import debug; debug()
2114            python_equivalent = expr.to_python(python_form=True)
2115        except:
2116            return
2117        return StringFromPython(python_equivalent)
2118
2119    def apply(self, expr, evaluation) -> Expression:
2120        "PythonForm[expr_]"
2121        return self.apply_python(expr, evaluation)
2122
2123
2124class SympyForm(Builtin):
2125    """
2126    <dl>
2127      <dt>'SympyForm[$expr$]'
2128      <dd>returns an Sympy $expr$ in Python. Sympy is used internally
2129      to implement a number of Mathics functions, like Simplify.
2130    </dl>
2131
2132    >> SympyForm[Pi^2]
2133    = pi**2
2134    >> E^2 + 3E // SympyForm
2135    = exp(2) + 3*E
2136    """
2137
2138    def apply_sympy(self, expr, evaluation) -> Expression:
2139        "MakeBoxes[expr_, SympyForm]"
2140
2141        try:
2142            # from trepan.api import debug; debug()
2143            sympy_equivalent = expr.to_sympy()
2144        except:
2145            return
2146        return StringFromPython(sympy_equivalent)
2147
2148    def apply(self, expr, evaluation) -> Expression:
2149        "SympyForm[expr_]"
2150        return self.apply_sympy(expr, evaluation)
2151
2152
2153class TeXForm(Builtin):
2154    r"""
2155    <dl>
2156    <dt>'TeXForm[$expr$]'
2157        <dd>displays $expr$ using TeX math mode commands.
2158    </dl>
2159
2160    >> TeXForm[HoldForm[Sqrt[a^3]]]
2161     = \sqrt{a^3}
2162
2163    #> {"hi","you"} //InputForm //TeXForm
2164     = \left\{\text{"hi"}, \text{"you"}\right\}
2165
2166    #> TeXForm[a+b*c]
2167     = a+b c
2168    #> TeXForm[InputForm[a+b*c]]
2169     = a\text{ + }b*c
2170    """
2171
2172    def apply_tex(self, expr, evaluation) -> Expression:
2173        "MakeBoxes[expr_, TeXForm]"
2174
2175        boxes = MakeBoxes(expr).evaluate(evaluation)
2176        try:
2177            tex = boxes.boxes_to_tex(evaluation=evaluation)
2178
2179            # Replace multiple newlines by a single one e.g. between asy-blocks
2180            tex = MULTI_NEWLINE_RE.sub("\n", tex)
2181
2182            tex = tex.replace(" \uF74c", " \\, d")  # tmp hack for Integrate
2183        except BoxError:
2184            evaluation.message(
2185                "General",
2186                "notboxes",
2187                Expression("FullForm", boxes).evaluate(evaluation),
2188            )
2189            tex = ""
2190        return Expression("RowBox", Expression(SymbolList, String(tex)))
2191
2192
2193class Style(Builtin):
2194    options = {"ImageSizeMultipliers": "Automatic"}
2195
2196    rules = {
2197        "MakeBoxes[Style[expr_, OptionsPattern[Style]], f_]": (
2198            "StyleBox[MakeBoxes[expr, f], "
2199            "ImageSizeMultipliers -> OptionValue[ImageSizeMultipliers]]"
2200        )
2201    }
2202
2203
2204class Precedence(Builtin):
2205    """
2206    <dl>
2207    <dt>'Precedence[$op$]'
2208        <dd>returns the precedence of the built-in operator $op$.
2209    </dl>
2210
2211    >> Precedence[Plus]
2212     = 310.
2213    >> Precedence[Plus] < Precedence[Times]
2214     = True
2215
2216    Unknown symbols have precedence 670:
2217    >> Precedence[f]
2218     = 670.
2219    Other expressions have precedence 1000:
2220    >> Precedence[a + b]
2221     = 1000.
2222    """
2223
2224    def apply(self, expr, evaluation) -> Real:
2225        "Precedence[expr_]"
2226
2227        name = expr.get_name()
2228        precedence = 1000
2229        if name:
2230            builtin = evaluation.definitions.get_definition(name, only_if_exists=True)
2231            if builtin:
2232                builtin = builtin.builtin
2233            if builtin is not None and isinstance(builtin, Operator):
2234                precedence = builtin.precedence
2235            else:
2236                precedence = 670
2237        return Real(precedence)
2238
2239
2240class _NumberForm(Builtin):
2241    """
2242    Base class for NumberForm, AccountingForm, EngineeringForm, and ScientificForm.
2243    """
2244
2245    default_ExponentFunction = None
2246    default_NumberFormat = None
2247
2248    messages = {
2249        "npad": "Value for option NumberPadding -> `1` should be a string or a pair of strings.",
2250        "dblk": "Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.",
2251        "npt": "Value for option `1` -> `2` is expected to be a string.",
2252        "nsgn": "Value for option NumberSigns -> `1` should be a pair of strings or two pairs of strings.",
2253        "nspr": "Value for option NumberSeparator -> `1` should be a string or a pair of strings.",
2254        "opttf": "Value of option `1` -> `2` should be True or False.",
2255        "estep": "Value of option `1` -> `2` is not a positive integer.",
2256        "iprf": "Formatting specification `1` should be a positive integer or a pair of positive integers.",  # NumberFormat only
2257        "sigz": "In addition to the number of digits requested, one or more zeros will appear as placeholders.",
2258    }
2259
2260    def check_options(self, options, evaluation):
2261        """
2262        Checks options are valid and converts them to python.
2263        """
2264        result = {}
2265        for option_name in self.options:
2266            method = getattr(self, "check_" + option_name)
2267            arg = options["System`" + option_name]
2268            value = method(arg, evaluation)
2269            if value is None:
2270                return None
2271            result[option_name] = value
2272        return result
2273
2274    def check_DigitBlock(self, value, evaluation):
2275        py_value = value.get_int_value()
2276        if value.sameQ(Symbol("Infinity")):
2277            return [0, 0]
2278        elif py_value is not None and py_value > 0:
2279            return [py_value, py_value]
2280        elif value.has_form("List", 2):
2281            nleft, nright = value.leaves
2282            py_left, py_right = nleft.get_int_value(), nright.get_int_value()
2283            if nleft.sameQ(Symbol("Infinity")):
2284                nleft = 0
2285            elif py_left is not None and py_left > 0:
2286                nleft = py_left
2287            else:
2288                nleft = None
2289            if nright.sameQ(Symbol("Infinity")):
2290                nright = 0
2291            elif py_right is not None and py_right > 0:
2292                nright = py_right
2293            else:
2294                nright = None
2295            result = [nleft, nright]
2296            if None not in result:
2297                return result
2298        return evaluation.message(self.get_name(), "dblk", value)
2299
2300    def check_ExponentFunction(self, value, evaluation):
2301        if value.sameQ(Symbol("Automatic")):
2302            return self.default_ExponentFunction
2303
2304        def exp_function(x):
2305            return Expression(value, x).evaluate(evaluation)
2306
2307        return exp_function
2308
2309    def check_NumberFormat(self, value, evaluation):
2310        if value.sameQ(Symbol("Automatic")):
2311            return self.default_NumberFormat
2312
2313        def num_function(man, base, exp, options):
2314            return Expression(value, man, base, exp).evaluate(evaluation)
2315
2316        return num_function
2317
2318    def check_NumberMultiplier(self, value, evaluation):
2319        result = value.get_string_value()
2320        if result is None:
2321            evaluation.message(self.get_name(), "npt", "NumberMultiplier", value)
2322        return result
2323
2324    def check_NumberPoint(self, value, evaluation):
2325        result = value.get_string_value()
2326        if result is None:
2327            evaluation.message(self.get_name(), "npt", "NumberPoint", value)
2328        return result
2329
2330    def check_ExponentStep(self, value, evaluation):
2331        result = value.get_int_value()
2332        if result is None or result <= 0:
2333            return evaluation.message(self.get_name(), "estep", "ExponentStep", value)
2334        return result
2335
2336    def check_SignPadding(self, value, evaluation):
2337        if value.sameQ(Symbol("True")):
2338            return True
2339        elif value.sameQ(Symbol("False")):
2340            return False
2341        return evaluation.message(self.get_name(), "opttf", value)
2342
2343    def _check_List2str(self, value, msg, evaluation):
2344        if value.has_form("List", 2):
2345            result = [leaf.get_string_value() for leaf in value.leaves]
2346            if None not in result:
2347                return result
2348        return evaluation.message(self.get_name(), msg, value)
2349
2350    def check_NumberSigns(self, value, evaluation):
2351        return self._check_List2str(value, "nsgn", evaluation)
2352
2353    def check_NumberPadding(self, value, evaluation):
2354        return self._check_List2str(value, "npad", evaluation)
2355
2356    def check_NumberSeparator(self, value, evaluation):
2357        py_str = value.get_string_value()
2358        if py_str is not None:
2359            return [py_str, py_str]
2360        return self._check_List2str(value, "nspr", evaluation)
2361
2362
2363class NumberForm(_NumberForm):
2364    """
2365    <dl>
2366    <dt>'NumberForm[$expr$, $n$]'
2367        <dd>prints a real number $expr$ with $n$-digits of precision.
2368    <dt>'NumberForm[$expr$, {$n$, $f$}]'
2369        <dd>prints with $n$-digits and $f$ digits to the right of the decimal point.
2370    </dl>
2371
2372    >> NumberForm[N[Pi], 10]
2373     = 3.141592654
2374
2375    >> NumberForm[N[Pi], {10, 5}]
2376     = 3.14159
2377
2378
2379    ## Undocumented edge cases
2380    #> NumberForm[Pi, 20]
2381     = Pi
2382    #> NumberForm[2/3, 10]
2383     = 2 / 3
2384
2385    ## No n or f
2386    #> NumberForm[N[Pi]]
2387     = 3.14159
2388    #> NumberForm[N[Pi, 20]]
2389     = 3.1415926535897932385
2390    #> NumberForm[14310983091809]
2391     = 14310983091809
2392
2393    ## Zero case
2394    #> z0 = 0.0;
2395    #> z1 = 0.0000000000000000000000000000;
2396    #> NumberForm[{z0, z1}, 10]
2397     = {0., 0.×10^-28}
2398    #> NumberForm[{z0, z1}, {10, 4}]
2399     = {0.0000, 0.0000×10^-28}
2400
2401    ## Trailing zeros
2402    #> NumberForm[1.0, 10]
2403     = 1.
2404    #> NumberForm[1.000000000000000000000000, 10]
2405     = 1.000000000
2406    #> NumberForm[1.0, {10, 8}]
2407     = 1.00000000
2408    #> NumberForm[N[Pi, 33], 33]
2409     = 3.14159265358979323846264338327950
2410
2411    ## Correct rounding - see sympy/issues/11472
2412    #> NumberForm[0.645658509, 6]
2413     = 0.645659
2414    #> NumberForm[N[1/7], 30]
2415     = 0.1428571428571428
2416
2417    ## Integer case
2418    #> NumberForm[{0, 2, -415, 83515161451}, 5]
2419     = {0, 2, -415, 83515161451}
2420    #> NumberForm[{2^123, 2^123.}, 4, ExponentFunction -> ((#1) &)]
2421     = {10633823966279326983230456482242756608, 1.063×10^37}
2422    #> NumberForm[{0, 10, -512}, {10, 3}]
2423     = {0.000, 10.000, -512.000}
2424
2425    ## Check arguments
2426    #> NumberForm[1.5, -4]
2427     : Formatting specification -4 should be a positive integer or a pair of positive integers.
2428     = 1.5
2429    #> NumberForm[1.5, {1.5, 2}]
2430     : Formatting specification {1.5, 2} should be a positive integer or a pair of positive integers.
2431     = 1.5
2432    #> NumberForm[1.5, {1, 2.5}]
2433     : Formatting specification {1, 2.5} should be a positive integer or a pair of positive integers.
2434     = 1.5
2435
2436    ## Right padding
2437    #> NumberForm[153., 2]
2438     : In addition to the number of digits requested, one or more zeros will appear as placeholders.
2439     = 150.
2440    #> NumberForm[0.00125, 1]
2441     = 0.001
2442    #> NumberForm[10^5 N[Pi], {5, 3}]
2443     : In addition to the number of digits requested, one or more zeros will appear as placeholders.
2444     = 314160.000
2445    #> NumberForm[10^5 N[Pi], {6, 3}]
2446     = 314159.000
2447    #> NumberForm[10^5 N[Pi], {6, 10}]
2448     = 314159.0000000000
2449    #> NumberForm[1.0000000000000000000, 10, NumberPadding -> {"X", "Y"}]
2450     = X1.000000000
2451
2452    ## Check options
2453
2454    ## DigitBlock
2455    #> NumberForm[12345.123456789, 14, DigitBlock -> 3]
2456     = 12,345.123 456 789
2457    #> NumberForm[12345.12345678, 14, DigitBlock -> 3]
2458     = 12,345.123 456 78
2459    #> NumberForm[N[10^ 5 Pi], 15, DigitBlock -> {4, 2}]
2460     = 31,4159.26 53 58 97 9
2461    #> NumberForm[1.2345, 3, DigitBlock -> -4]
2462     : Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.
2463     = 1.2345
2464    #> NumberForm[1.2345, 3, DigitBlock -> x]
2465     : Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.
2466     = 1.2345
2467    #> NumberForm[1.2345, 3, DigitBlock -> {x, 3}]
2468     : Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.
2469     = 1.2345
2470    #> NumberForm[1.2345, 3, DigitBlock -> {5, -3}]
2471     : Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.
2472     = 1.2345
2473
2474    ## ExponentFunction
2475    #> NumberForm[12345.123456789, 14, ExponentFunction -> ((#) &)]
2476     = 1.2345123456789×10^4
2477    #> NumberForm[12345.123456789, 14, ExponentFunction -> (Null&)]
2478     = 12345.123456789
2479    #> y = N[Pi^Range[-20, 40, 15]];
2480    #> NumberForm[y, 10, ExponentFunction -> (3 Quotient[#, 3] &)]
2481     =  {114.0256472×10^-12, 3.267763643×10^-3, 93.64804748×10^3, 2.683779414×10^12, 76.91214221×10^18}
2482    #> NumberForm[y, 10, ExponentFunction -> (Null &)]
2483     : In addition to the number of digits requested, one or more zeros will appear as placeholders.
2484     : In addition to the number of digits requested, one or more zeros will appear as placeholders.
2485     = {0.0000000001140256472, 0.003267763643, 93648.04748, 2683779414000., 76912142210000000000.}
2486
2487    ## ExponentStep
2488    #> NumberForm[10^8 N[Pi], 10, ExponentStep -> 3]
2489     = 314.1592654×10^6
2490    #> NumberForm[1.2345, 3, ExponentStep -> x]
2491     : Value of option ExponentStep -> x is not a positive integer.
2492     = 1.2345
2493    #> NumberForm[1.2345, 3, ExponentStep -> 0]
2494     : Value of option ExponentStep -> 0 is not a positive integer.
2495     = 1.2345
2496    #> NumberForm[y, 10, ExponentStep -> 6]
2497     = {114.0256472×10^-12, 3267.763643×10^-6, 93648.04748, 2.683779414×10^12, 76.91214221×10^18}
2498
2499    ## NumberFormat
2500    #> NumberForm[y, 10, NumberFormat -> (#1 &)]
2501     = {1.140256472, 0.003267763643, 93648.04748, 2.683779414, 7.691214221}
2502
2503    ## NumberMultiplier
2504    #> NumberForm[1.2345, 3, NumberMultiplier -> 0]
2505     : Value for option NumberMultiplier -> 0 is expected to be a string.
2506     = 1.2345
2507    #> NumberForm[N[10^ 7 Pi], 15, NumberMultiplier -> "*"]
2508     = 3.14159265358979*10^7
2509
2510    ## NumberPoint
2511    #> NumberForm[1.2345, 5, NumberPoint -> ","]
2512     = 1,2345
2513    #> NumberForm[1.2345, 3, NumberPoint -> 0]
2514     : Value for option NumberPoint -> 0 is expected to be a string.
2515     = 1.2345
2516
2517    ## NumberPadding
2518    #> NumberForm[1.41, {10, 5}]
2519     = 1.41000
2520    #> NumberForm[1.41, {10, 5}, NumberPadding -> {"", "X"}]
2521     = 1.41XXX
2522    #> NumberForm[1.41, {10, 5}, NumberPadding -> {"X", "Y"}]
2523     = XXXXX1.41YYY
2524    #> NumberForm[1.41, 10, NumberPadding -> {"X", "Y"}]
2525     = XXXXXXXX1.41
2526    #> NumberForm[1.2345, 3, NumberPadding -> 0]
2527     :  Value for option NumberPadding -> 0 should be a string or a pair of strings.
2528     = 1.2345
2529    #> NumberForm[1.41, 10, NumberPadding -> {"X", "Y"}, NumberSigns -> {"-------------", ""}]
2530     = XXXXXXXXXXXXXXXXXXXX1.41
2531    #> NumberForm[{1., -1., 2.5, -2.5}, {4, 6}, NumberPadding->{"X", "Y"}]
2532     = {X1.YYYYYY, -1.YYYYYY, X2.5YYYYY, -2.5YYYYY}
2533
2534    ## NumberSeparator
2535    #> NumberForm[N[10^ 5 Pi], 15, DigitBlock -> 3, NumberSeparator -> " "]
2536     = 314 159.265 358 979
2537    #> NumberForm[N[10^ 5 Pi], 15, DigitBlock -> 3, NumberSeparator -> {" ", ","}]
2538     = 314 159.265,358,979
2539    #> NumberForm[N[10^ 5 Pi], 15, DigitBlock -> 3, NumberSeparator -> {",", " "}]
2540     = 314,159.265 358 979
2541    #> NumberForm[N[10^ 7 Pi], 15, DigitBlock -> 3, NumberSeparator -> {",", " "}]
2542     = 3.141 592 653 589 79×10^7
2543    #> NumberForm[1.2345, 3, NumberSeparator -> 0]
2544     :  Value for option NumberSeparator -> 0 should be a string or a pair of strings.
2545     = 1.2345
2546
2547    ## NumberSigns
2548    #> NumberForm[1.2345, 5, NumberSigns -> {"-", "+"}]
2549     = +1.2345
2550    #> NumberForm[-1.2345, 5, NumberSigns -> {"- ", ""}]
2551     = - 1.2345
2552    #> NumberForm[1.2345, 3, NumberSigns -> 0]
2553     : Value for option NumberSigns -> 0 should be a pair of strings or two pairs of strings.
2554     = 1.2345
2555
2556    ## SignPadding
2557    #> NumberForm[1.234, 6, SignPadding -> True, NumberPadding -> {"X", "Y"}]
2558     = XXX1.234
2559    #> NumberForm[-1.234, 6, SignPadding -> True, NumberPadding -> {"X", "Y"}]
2560     = -XX1.234
2561    #> NumberForm[-1.234, 6, SignPadding -> False, NumberPadding -> {"X", "Y"}]
2562     = XX-1.234
2563    #> NumberForm[-1.234, {6, 4}, SignPadding -> False, NumberPadding -> {"X", "Y"}]
2564     = X-1.234Y
2565
2566    ## 1-arg, Option case
2567    #> NumberForm[34, ExponentFunction->(Null&)]
2568     = 34
2569
2570    ## zero padding integer x0.0 case
2571    #> NumberForm[50.0, {5, 1}]
2572     = 50.0
2573    #> NumberForm[50, {5, 1}]
2574     = 50.0
2575
2576    ## Rounding correctly
2577    #> NumberForm[43.157, {10, 1}]
2578     = 43.2
2579    #> NumberForm[43.15752525, {10, 5}, NumberSeparator -> ",", DigitBlock -> 1]
2580     = 4,3.1,5,7,5,3
2581    #> NumberForm[80.96, {16, 1}]
2582     = 81.0
2583    #> NumberForm[142.25, {10, 1}]
2584     = 142.3
2585    """
2586
2587    options = {
2588        "DigitBlock": "Infinity",
2589        "ExponentFunction": "Automatic",
2590        "ExponentStep": "1",
2591        "NumberFormat": "Automatic",
2592        "NumberMultiplier": '"×"',
2593        "NumberPadding": '{"", "0"}',
2594        "NumberPoint": '"."',
2595        "NumberSeparator": '{",", " "}',
2596        "NumberSigns": '{"-", ""}',
2597        "SignPadding": "False",
2598    }
2599
2600    @staticmethod
2601    def default_ExponentFunction(value):
2602        n = value.get_int_value()
2603        if -5 <= n <= 5:
2604            return Symbol("Null")
2605        else:
2606            return value
2607
2608    @staticmethod
2609    def default_NumberFormat(man, base, exp, options):
2610        py_exp = exp.get_string_value()
2611        if py_exp:
2612            mul = String(options["NumberMultiplier"])
2613            return Expression(
2614                "RowBox",
2615                Expression(
2616                    SymbolList, man, mul, Expression("SuperscriptBox", base, exp)
2617                ),
2618            )
2619        else:
2620            return man
2621
2622    def apply_list_n(self, expr, n, evaluation, options) -> Expression:
2623        "NumberForm[expr_?ListQ, n_, OptionsPattern[NumberForm]]"
2624        options = [
2625            Expression("RuleDelayed", Symbol(key), value)
2626            for key, value in options.items()
2627        ]
2628        return Expression(
2629            "List",
2630            *[Expression("NumberForm", leaf, n, *options) for leaf in expr.leaves]
2631        )
2632
2633    def apply_list_nf(self, expr, n, f, evaluation, options) -> Expression:
2634        "NumberForm[expr_?ListQ, {n_, f_}, OptionsPattern[NumberForm]]"
2635        options = [
2636            Expression("RuleDelayed", Symbol(key), value)
2637            for key, value in options.items()
2638        ]
2639        return Expression(
2640            "List",
2641            *[
2642                Expression("NumberForm", leaf, Expression(SymbolList, n, f), *options)
2643                for leaf in expr.leaves
2644            ]
2645        )
2646
2647    def apply_makeboxes(self, expr, form, evaluation, options={}):
2648        """MakeBoxes[NumberForm[expr_, OptionsPattern[NumberForm]],
2649            form:StandardForm|TraditionalForm|OutputForm]"""
2650
2651        fallback = Expression(SymbolMakeBoxes, expr, form)
2652
2653        py_options = self.check_options(options, evaluation)
2654        if py_options is None:
2655            return fallback
2656
2657        if isinstance(expr, Integer):
2658            py_n = len(str(abs(expr.get_int_value())))
2659        elif isinstance(expr, Real):
2660            if expr.is_machine_precision():
2661                py_n = 6
2662            else:
2663                py_n = dps(expr.get_precision())
2664        else:
2665            py_n = None
2666
2667        if py_n is not None:
2668            return number_form(expr, py_n, None, evaluation, py_options)
2669        return Expression(SymbolMakeBoxes, expr, form)
2670
2671    def apply_makeboxes_n(self, expr, n, form, evaluation, options={}):
2672        """MakeBoxes[NumberForm[expr_, n_?NotOptionQ, OptionsPattern[NumberForm]],
2673            form:StandardForm|TraditionalForm|OutputForm]"""
2674
2675        fallback = Expression(SymbolMakeBoxes, expr, form)
2676
2677        py_n = n.get_int_value()
2678        if py_n is None or py_n <= 0:
2679            evaluation.message("NumberForm", "iprf", n)
2680            return fallback
2681
2682        py_options = self.check_options(options, evaluation)
2683        if py_options is None:
2684            return fallback
2685
2686        if isinstance(expr, (Integer, Real)):
2687            return number_form(expr, py_n, None, evaluation, py_options)
2688        return Expression(SymbolMakeBoxes, expr, form)
2689
2690    def apply_makeboxes_nf(self, expr, n, f, form, evaluation, options={}):
2691        """MakeBoxes[NumberForm[expr_, {n_, f_}, OptionsPattern[NumberForm]],
2692            form:StandardForm|TraditionalForm|OutputForm]"""
2693
2694        fallback = Expression(SymbolMakeBoxes, expr, form)
2695
2696        nf = Expression(SymbolList, n, f)
2697        py_n = n.get_int_value()
2698        py_f = f.get_int_value()
2699        if py_n is None or py_n <= 0 or py_f is None or py_f < 0:
2700            evaluation.message("NumberForm", "iprf", nf)
2701            return fallback
2702
2703        py_options = self.check_options(options, evaluation)
2704        if py_options is None:
2705            return fallback
2706
2707        if isinstance(expr, (Integer, Real)):
2708            return number_form(expr, py_n, py_f, evaluation, py_options)
2709        return Expression(SymbolMakeBoxes, expr, form)
2710
2711
2712class BaseForm(Builtin):
2713    """
2714    <dl>
2715    <dt>'BaseForm[$expr$, $n$]'
2716        <dd>prints numbers in $expr$ in base $n$.
2717    </dl>
2718
2719    >> BaseForm[33, 2]
2720     = 100001_2
2721
2722    >> BaseForm[234, 16]
2723     = ea_16
2724
2725    >> BaseForm[12.3, 2]
2726     = 1100.01001100110011001_2
2727
2728    >> BaseForm[-42, 16]
2729     = -2a_16
2730
2731    >> BaseForm[x, 2]
2732     = x
2733
2734    >> BaseForm[12, 3] // FullForm
2735     = BaseForm[12, 3]
2736
2737    Bases must be between 2 and 36:
2738    >> BaseForm[12, -3]
2739     : Positive machine-sized integer expected at position 2 in BaseForm[12, -3].
2740     : MakeBoxes[BaseForm[12, -3], OutputForm] is not a valid box structure.
2741    >> BaseForm[12, 100]
2742     : Requested base 100 must be between 2 and 36.
2743     : MakeBoxes[BaseForm[12, 100], OutputForm] is not a valid box structure.
2744
2745    #> BaseForm[0, 2]
2746     = 0_2
2747    #> BaseForm[0.0, 2]
2748     = 0.0_2
2749
2750    #> BaseForm[N[Pi, 30], 16]
2751     = 3.243f6a8885a308d313198a2e_16
2752    """
2753
2754    messages = {
2755        "intpm": (
2756            "Positive machine-sized integer expected at position 2 in "
2757            "BaseForm[`1`, `2`]."
2758        ),
2759        "basf": "Requested base `1` must be between 2 and 36.",
2760    }
2761
2762    def apply_makeboxes(self, expr, n, f, evaluation):
2763        """MakeBoxes[BaseForm[expr_, n_],
2764            f:StandardForm|TraditionalForm|OutputForm]"""
2765
2766        base = n.get_int_value()
2767
2768        if base <= 0:
2769            evaluation.message("BaseForm", "intpm", expr, n)
2770            return
2771
2772        if isinstance(expr, PrecisionReal):
2773            x = expr.to_sympy()
2774            p = reconstruct_digits(expr.get_precision())
2775        elif isinstance(expr, MachineReal):
2776            x = expr.get_float_value()
2777            p = reconstruct_digits(machine_precision)
2778        elif isinstance(expr, Integer):
2779            x = expr.get_int_value()
2780            p = 0
2781        else:
2782            return Expression(SymbolMakeBoxes, expr, f)
2783
2784        try:
2785            val = convert_base(x, base, p)
2786        except ValueError:
2787            return evaluation.message("BaseForm", "basf", n)
2788
2789        if f.get_name() == "System`OutputForm":
2790            return from_python("%s_%d" % (val, base))
2791        else:
2792            return Expression("SubscriptBox", String(val), String(base))
2793