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