From e02e3a90e3e79b1c6db3194b3b1e1844946ce2bd Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sun, 15 Mar 2026 15:18:41 -0300 Subject: [PATCH 01/10] Improve use of CharacterEncoding --- mathics/builtin/atomic/strings.py | 15 ++++++++++- mathics/eval/strings.py | 4 +-- mathics/format/render/text.py | 41 ++++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/mathics/builtin/atomic/strings.py b/mathics/builtin/atomic/strings.py index 81bd10052..779d1dd8e 100644 --- a/mathics/builtin/atomic/strings.py +++ b/mathics/builtin/atomic/strings.py @@ -899,7 +899,20 @@ def eval_default(self, value, evaluation: Evaluation, options: dict): def eval_form(self, expr, form, evaluation: Evaluation, options: dict): "ToString[expr_, form_Symbol, OptionsPattern[ToString]]" encoding = options["System`CharacterEncoding"] - return eval_ToString(expr, form, encoding.value, evaluation) + if isinstance(encoding, String): + encoding_str = encoding.value + if encoding_str not in _encodings: + evaluation.message("$CharacterEncoding", "charcode", encoding) + encoding_str = evaluation.definitions.get_ownvalue( + "System`$SystemCharacterEncoding" + ).value + else: + evaluation.message("$CharacterEncoding", "charcode", encoding) + encoding_str = evaluation.definitions.get_ownvalue( + "System`$SystemCharacterEncoding" + ).value + + return eval_ToString(expr, form, encoding_str, evaluation) class Transliterate(Builtin): diff --git a/mathics/eval/strings.py b/mathics/eval/strings.py index 4e83e2848..c0a1ba50a 100644 --- a/mathics/eval/strings.py +++ b/mathics/eval/strings.py @@ -23,8 +23,8 @@ def eval_ToString( expr: BaseElement, form: Symbol, encoding: String, evaluation: Evaluation ) -> String: - boxes = format_element(expr, evaluation, form, encoding=encoding) - text = boxes.to_text(evaluation=evaluation) + boxes = format_element(expr, evaluation, form) + text = boxes.to_text(evaluation=evaluation, encoding=encoding) return String(text) diff --git a/mathics/format/render/text.py b/mathics/format/render/text.py index 1b07691e3..fa8c8bb52 100644 --- a/mathics/format/render/text.py +++ b/mathics/format/render/text.py @@ -2,7 +2,6 @@ """ Mathics3 box rendering to plain text. """ - from mathics.builtin.box.graphics import GraphicsBox from mathics.builtin.box.graphics3d import Graphics3DBox from mathics.builtin.box.layout import ( @@ -34,6 +33,43 @@ add_render_function(FormBox, convert_inner_box_field) +# Map WMA encoding names to Python encoding names +ENCODING_WMA_TO_PYTHON = { + "WindowsEastEurope": "cp1250", + "WindowsCyrillic": "cp1251", + "WindowsANSI": "cp1252", + "WindowsGreek": "cp1252", + "WindowsTurkish": "cp1254", +} + + +def encode_string_value(value: str, encoding: str): + """Convert an Unicode string `value` to the required `encoding`""" + if encoding == "ASCII": + # TODO: replace from a table from MathicsScanner + ascii_map = { + "⇒": "=>", + "↔": "<->", + "→": "->", + "⇾": "->", + "⇾": "->", + "⇴": "->", + "∫": r"\[Integral]", + "𝑑": r"\[DifferentialD]", + "⧦": r"\[Equivalent]", + "×": r" x ", + } + result = "" + for ch in value: + ch = ascii_map.get(ch, ch) + result += ch + return result + + encoding = ENCODING_WMA_TO_PYTHON.get(encoding, encoding) + result = value.encode("utf-8").decode(encoding) + return result + + def fractionbox(box: FractionBox, **options) -> str: # Note: values set in `options` take precedence over `box_options` child_options = {**options, **box.box_options} @@ -159,6 +195,9 @@ def string(s: String, **options) -> str: if value.startswith('"') and value.endswith('"'): # nopep8 if not show_string_characters: value = value[1:-1] + + if "encoding" in options and options["encoding"] != "Unicode": + value = encode_string_value(value, options["encoding"]) return value From 1a53c1aad24ac7f4f578cab47015625dd0b5afca Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Tue, 24 Mar 2026 23:42:19 -0300 Subject: [PATCH 02/10] not finished --- mathics/builtin/atomic/strings.py | 4 +- mathics/core/element.py | 1 + mathics/core/evaluation.py | 7 +- mathics/docpipeline.py | 1 + mathics/eval/strings.py | 12 +++- mathics/format/box/numberform.py | 1 + mathics/format/form/outputform.py | 5 +- mathics/format/render/text.py | 81 +++++++++++++++------- test/builtin/files_io/test_files.py | 8 +-- test/builtin/files_io/test_importexport.py | 2 +- test/builtin/numbers/test_trig.py | 4 +- test/builtin/test_binary.py | 16 +++-- test/builtin/test_forms.py | 16 ++--- test/builtin/test_numeric.py | 6 +- test/format/format_tests.yaml | 28 ++++---- test/format/test_format.py | 25 +++++-- test/helper.py | 2 +- 17 files changed, 140 insertions(+), 79 deletions(-) diff --git a/mathics/builtin/atomic/strings.py b/mathics/builtin/atomic/strings.py index 779d1dd8e..d6b9a3b9a 100644 --- a/mathics/builtin/atomic/strings.py +++ b/mathics/builtin/atomic/strings.py @@ -881,7 +881,7 @@ class ToString(Builtin): """ options = { - "CharacterEncoding": '"Unicode"', + "CharacterEncoding": "$CharacterEncoding", "FormatType": "OutputForm", "NumberMarks": "$NumberMarks", "PageHeight": "Infinity", @@ -898,7 +898,7 @@ def eval_default(self, value, evaluation: Evaluation, options: dict): def eval_form(self, expr, form, evaluation: Evaluation, options: dict): "ToString[expr_, form_Symbol, OptionsPattern[ToString]]" - encoding = options["System`CharacterEncoding"] + encoding = options["System`CharacterEncoding"].evaluate(evaluation) if isinstance(encoding, String): encoding_str = encoding.value if encoding_str not in _encodings: diff --git a/mathics/core/element.py b/mathics/core/element.py index dff595127..aa43f9224 100644 --- a/mathics/core/element.py +++ b/mathics/core/element.py @@ -424,6 +424,7 @@ def to_tex(self, **options) -> str: return self.to_format("latex", **options) def to_text(self, **options) -> str: + options.setdefault("encoding", "Unicode") return self.to_format("text", **options) # Deprecated diff --git a/mathics/core/evaluation.py b/mathics/core/evaluation.py index e048c7418..4ea7b93e6 100644 --- a/mathics/core/evaluation.py +++ b/mathics/core/evaluation.py @@ -419,10 +419,15 @@ def format_output(self, expr, format=None): if result is None: return None + try: + encoding = self.definitions.get_ownvalue("System`$CharacterEncoding").value + except AttributeError: + encoding = "Unicode" + try: # With the new implementation, if result is not a ``BoxExpression`` # then we should raise a BoxError here. - boxes = result.to_text(evaluation=self) + boxes = result.to_text(evaluation=self, encoding=encoding) except BoxError: self.message( "General", "notboxes", Expression(SymbolFullForm, result).evaluate(self) diff --git a/mathics/docpipeline.py b/mathics/docpipeline.py index 403f3372f..8251eacfb 100644 --- a/mathics/docpipeline.py +++ b/mathics/docpipeline.py @@ -232,6 +232,7 @@ def test_case( ) if not comparison_result: print("result != wanted") + print(test_pipeline.session.evaluate("$CharacterEncoding")) fail_msg = f"Result: {result}\nWanted: {test.result}" if out: fail_msg += "\nAdditional output:\n" diff --git a/mathics/eval/strings.py b/mathics/eval/strings.py index c0a1ba50a..14e006d63 100644 --- a/mathics/eval/strings.py +++ b/mathics/eval/strings.py @@ -23,9 +23,17 @@ def eval_ToString( expr: BaseElement, form: Symbol, encoding: String, evaluation: Evaluation ) -> String: + from mathics.format.render.text import EncodingNameError + boxes = format_element(expr, evaluation, form) - text = boxes.to_text(evaluation=evaluation, encoding=encoding) - return String(text) + try: + return String(boxes.to_text(evaluation=evaluation, encoding=encoding)) + except EncodingNameError: + # Mimic the WMA behavior. In the future, we can implement the mechanism + # with encodings stored in .m files, and give a chance with it. + evaluation.message("Get", "noopen", String("encodings/" + encoding + "." + "m")) + + return String(boxes.to_text(evaluation=evaluation, encoding="Unicode")) def eval_StringContainsQ(name, string, patt, evaluation, options, matched): diff --git a/mathics/format/box/numberform.py b/mathics/format/box/numberform.py index a6dbd9eda..4b7fffe30 100644 --- a/mathics/format/box/numberform.py +++ b/mathics/format/box/numberform.py @@ -49,6 +49,7 @@ def default_numberformat_outputform(man, base, exp, opts): "ExponentFunction": lambda x: (SymbolNull if abs(x.value) <= 5 else x), "ExponentStep": 1, "NumberFormat": default_numberformat_outputform, + # TODO: Pick from MathicsScanner tables... "NumberMultiplier": "×", "NumberPadding": ["", "0"], "NumberPoint": ".", diff --git a/mathics/format/form/outputform.py b/mathics/format/form/outputform.py index a2e118459..40c41a68e 100644 --- a/mathics/format/form/outputform.py +++ b/mathics/format/form/outputform.py @@ -242,6 +242,7 @@ def render_output_form(expr: BaseElement, evaluation: Evaluation, **kwargs): """ format_expr: Expression = do_format(expr, evaluation, SymbolOutputForm) # type: ignore + kwargs.setdefault("encoding", "Unicode") while format_expr.has_form("HoldForm", 1): # type: ignore format_expr = format_expr.elements[0] @@ -338,7 +339,7 @@ def other_forms(expr, evaluation, **kwargs): raise _WrongFormattedExpression result = format_element(expr, evaluation, SymbolStandardForm, **kwargs) - return result.to_text() + return result.to_text(evaluation=evaluation, **kwargs) @register_outputform("System`Integer") @@ -357,7 +358,7 @@ def integer_outputform(n, evaluation, **kwargs): result = numberform_to_boxes(n, digits, padding, evaluation, py_options) if isinstance(result, String): return result.value - return result.to_text() + return result.to_text(**kwargs) @register_outputform("System`Image") diff --git a/mathics/format/render/text.py b/mathics/format/render/text.py index fa8c8bb52..7003eec2e 100644 --- a/mathics/format/render/text.py +++ b/mathics/format/render/text.py @@ -43,30 +43,62 @@ } +class EncodingNameError(Exception): + pass + + +def get_encoding_table(encoding: str): + """ + Return a dictionary with a map from + character codes in the internal (Unicode) + representation to the request encoding. + """ + if encoding == "Unicode": + return {} + + # In the final implementation, this should load the corresponding + # json table or an encoding file as in WMA + # SystemFiles/CharacterEncodings/*.m + # If the encoding is not available, raise an EncodingError + try: + return { + "ASCII": { + "⧴": ":>", + "⇒": "=>", + "↔": "<->", + "⩵": "==", + "∧": "&&", + "⊻": r"\[Xor]", + "≠": "!=", + "≤": "<=", + "→": "->", + "⇾": "->", + "⇾": "->", + "⇴": "->", + "∫": r"\[Integral]", + "": r"\[DifferentialD]", + "𝑑": r"\[DifferentialD]", + "⧦": r"\[Equivalent]", + "×": r" x ", + }, + "UTF-8": {}, + }[encoding] + except KeyError: + raise EncodingNameError + + def encode_string_value(value: str, encoding: str): """Convert an Unicode string `value` to the required `encoding`""" - if encoding == "ASCII": - # TODO: replace from a table from MathicsScanner - ascii_map = { - "⇒": "=>", - "↔": "<->", - "→": "->", - "⇾": "->", - "⇾": "->", - "⇴": "->", - "∫": r"\[Integral]", - "𝑑": r"\[DifferentialD]", - "⧦": r"\[Equivalent]", - "×": r" x ", - } - result = "" - for ch in value: - ch = ascii_map.get(ch, ch) - result += ch - return result - - encoding = ENCODING_WMA_TO_PYTHON.get(encoding, encoding) - result = value.encode("utf-8").decode(encoding) + + # In WMA, encodings are readed from SystemFiles/CharacterEncodings/*.m + # on the fly. We should load them from Mathics3-Scanner tables. + encoding_table = get_encoding_table(encoding) + if not encoding_table: + return value + result = "" + for ch in value: + ch = encoding_table.get(ch, ch) + result += ch return result @@ -195,10 +227,7 @@ def string(s: String, **options) -> str: if value.startswith('"') and value.endswith('"'): # nopep8 if not show_string_characters: value = value[1:-1] - - if "encoding" in options and options["encoding"] != "Unicode": - value = encode_string_value(value, options["encoding"]) - return value + return encode_string_value(value, options["encoding"]) add_render_function(String, string) diff --git a/test/builtin/files_io/test_files.py b/test/builtin/files_io/test_files.py index 7cf17f603..91a49fa91 100644 --- a/test/builtin/files_io/test_files.py +++ b/test/builtin/files_io/test_files.py @@ -231,28 +231,28 @@ def test_close(): ( 'stream = StringToStream["1.523E-19"]; Read[stream, Real]', None, - "1.523×10^-19", + "1.523 x 10^-19", "", ), ("Close[stream];", None, "Null", ""), ( 'stream = StringToStream["-1.523e19"]; Read[stream, Real]', None, - "-1.523×10^19", + "-1.523 x 10^19", "", ), ("Close[stream];", None, "Null", ""), ( 'stream = StringToStream["3*^10"]; Read[stream, Real]', None, - "3.×10^10", + "3. x 10^10", "", ), ("Close[stream];", None, "Null", ""), ( 'stream = StringToStream["3.*^10"]; Read[stream, Real]', None, - "3.×10^10", + "3. x 10^10", "", ), ("Close[stream];", None, "Null", ""), diff --git a/test/builtin/files_io/test_importexport.py b/test/builtin/files_io/test_importexport.py index 64b09973b..a19b13930 100644 --- a/test/builtin/files_io/test_importexport.py +++ b/test/builtin/files_io/test_importexport.py @@ -310,7 +310,7 @@ def test_inividually(): ( r'System`Convert`B64Dump`B64Decode["4oirIGYg752MIHg="]', None, - r"∫ f  x", + r"\[Integral] f \[DifferentialD] x", None, ), ], diff --git a/test/builtin/numbers/test_trig.py b/test/builtin/numbers/test_trig.py index 89d4d7100..c24226608 100644 --- a/test/builtin/numbers/test_trig.py +++ b/test/builtin/numbers/test_trig.py @@ -42,9 +42,9 @@ def test_ArcCos(): ("ArcTan[-1, 0]", None, "Pi", None), ("ArcTan[0, 1]", None, "Pi / 2", None), ("ArcTan[0, -1]", None, "-Pi / 2", None), - ("Cos[1.5 Pi]", None, "-1.83697×10^-16", None), + ("Cos[1.5 Pi]", None, "-1.83697 x 10^-16", None), ("N[Sin[1], 40]", None, "0.8414709848078965066525023216302989996226", None), - ("Tan[0.5 Pi]", None, "1.63312×10^16", None), + ("Tan[0.5 Pi]", None, "1.63312 x 10^16", None), ], ) def test_trig(str_expr, msgs, str_expected, fail_msg): diff --git a/test/builtin/test_binary.py b/test/builtin/test_binary.py index 0aebbb817..2506b054d 100644 --- a/test/builtin/test_binary.py +++ b/test/builtin/test_binary.py @@ -236,8 +236,12 @@ ('WbR[{1, 0, 0, 0, 0, 0, 240, 255}, "Real64"]', "Indeterminate", None), ## Real128 ## 0x0000 - ('WbR[{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}, "Real128"]', "0.×10^-4965", None), - ('WbR[{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,128}, "Real128"]', "0.×10^-4965", None), + ('WbR[{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}, "Real128"]', "0. x 10^-4965", None), + ( + 'WbR[{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,128}, "Real128"]', + "0. x 10^-4965", + None, + ), ## 0x0001 - 0x7FFE ( 'WbR[{0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,255,63}, "Real128"]', @@ -256,12 +260,12 @@ ), ( 'WbR[{135, 62, 233, 137, 22, 208, 233, 210, 133, 82, 251, 92, 220, 216, 207, 72}, "Real128"]', - "2.45563355727491021879689747166252×10^679", + "2.45563355727491021879689747166252 x 10^679", None, ), ( 'z=WbR[{74, 95, 30, 234, 116, 130, 1, 84, 20, 133, 245, 221, 113, 110, 219, 212}, "Real128"]', - "-4.52840681592341879518366539335138×10^1607", + "-4.52840681592341879518366539335138 x 10^1607", None, ), ("z // Precision", "33.", None), @@ -427,8 +431,8 @@ def test_ByteOrdering(str_expr, str_expected, fail_msg): @pytest.mark.parametrize( ("str_expr", "str_expected"), [ - ("NumericArray[{{1,2},{3,4}}]", ""), - ("ToString[NumericArray[{{1,2},{3,4}}]]", ""), + ("NumericArray[{{1,2},{3,4}}]", ""), + ("ToString[NumericArray[{{1,2},{3,4}}]]", ""), ("Head[NumericArray[{1,2}]]", "NumericArray"), ("AtomQ[NumericArray[{1,2}]]", "True"), ("First[NumericArray[{1,2,3}]]", "1"), diff --git a/test/builtin/test_forms.py b/test/builtin/test_forms.py index 16650ebcb..ff61794ce 100644 --- a/test/builtin/test_forms.py +++ b/test/builtin/test_forms.py @@ -46,8 +46,8 @@ def test_makeboxes_form(expr, form, head, subhead): ("NumberForm[14310983091809]", None, "14310983091809", None), ## Zero case ("z0 = 0.0;z1 = 0.0000000000000000000000000000;", None, "Null", None), - ("NumberForm[{z0, z1}, 10]", None, "{0., 0.×10^-28}", None), - ("NumberForm[{z0, z1}, {10, 4}]", None, "{0.0000, 0.0000×10^-28}", None), + ("NumberForm[{z0, z1}, 10]", None, "{0., 0. x 10^-28}", None), + ("NumberForm[{z0, z1}, {10, 4}]", None, "{0.0000, 0.0000 x 10^-28}", None), ("z0=.;z1=.;", None, "Null", None), ## Trailing zeros ("NumberForm[1.0, 10]", None, "1.", None), @@ -67,7 +67,7 @@ def test_makeboxes_form(expr, form, head, subhead): ( "NumberForm[{2^123, 2^123.}, 4, ExponentFunction -> ((#1) &)]", None, - "{10633823966279326983230456482242756608, 1.063×10^37}", + "{10633823966279326983230456482242756608, 1.063 x 10^37}", None, ), ("NumberForm[{0, 10, -512}, {10, 3}]", None, "{0.000, 10.000, -512.000}", None), @@ -178,7 +178,7 @@ def test_makeboxes_form(expr, form, head, subhead): ( "NumberForm[12345.123456789, 14, ExponentFunction -> ((#) &)]", None, - "1.2345123456789×10^4", + "1.2345123456789 x 10^4", None, ), ( @@ -191,7 +191,7 @@ def test_makeboxes_form(expr, form, head, subhead): ( "NumberForm[y, 10, ExponentFunction -> (3 Quotient[#, 3] &)]", None, - "{114.0256472×10^-12, 3.267763643×10^-3, 93.64804748×10^3, 2.683779414×10^12, 76.91214221×10^18}", + "{114.0256472 x 10^-12, 3.267763643 x 10^-3, 93.64804748 x 10^3, 2.683779414 x 10^12, 76.91214221 x 10^18}", None, ), ( @@ -207,7 +207,7 @@ def test_makeboxes_form(expr, form, head, subhead): ( "NumberForm[10^8 N[Pi], 10, ExponentStep -> 3]", None, - "314.1592654×10^6", + "314.1592654 x 10^6", None, ), ( @@ -225,7 +225,7 @@ def test_makeboxes_form(expr, form, head, subhead): ( "NumberForm[y, 10, ExponentStep -> 6]", None, - "{114.0256472×10^-12, 3267.763643×10^-6, 93648.04748, 2.683779414×10^12, 76.91214221×10^18}", + "{114.0256472 x 10^-12, 3267.763643 x 10^-6, 93648.04748, 2.683779414 x 10^12, 76.91214221 x 10^18}", None, ), ## NumberFormat @@ -318,7 +318,7 @@ def test_makeboxes_form(expr, form, head, subhead): ( 'NumberForm[N[10^ 7 Pi], 15, DigitBlock -> 3, NumberSeparator -> {",", " "}]', None, - "3.141 592 653 589 79×10^7", + "3.141 592 653 589 79 x 10^7", None, ), ( diff --git a/test/builtin/test_numeric.py b/test/builtin/test_numeric.py index 78d3a25a4..1ca38e27c 100644 --- a/test/builtin/test_numeric.py +++ b/test/builtin/test_numeric.py @@ -76,19 +76,19 @@ def test_realvalued(): ( "N[3^200]", None, - "2.65614×10^95", + "2.65614 x 10^95", "Numeric converts a large integer to a MachineReal without losing precision", ), ( "N[2^1023]", None, - "8.98847×10^307", + "8.98847 x 10^307", "Numeric display digits for value under 2^1024 is DefaultPrintDisplay value 6", ), ( "N[2^1024]", None, - "1.797693134862316×10^308", + "1.797693134862316 x 10^308", "Numeric display digits for value on or over= 2^1024 is $MachineDigits", ), ( diff --git a/test/format/format_tests.yaml b/test/format/format_tests.yaml index 569bb769b..45a832f86 100644 --- a/test/format/format_tests.yaml +++ b/test/format/format_tests.yaml @@ -100,7 +100,7 @@ mathml: {} text: System`InputForm: -1.*^-6 - System`OutputForm: "-1.\xD710^-6" + System`OutputForm: "-1. x 10^-6" -1. 10^5: @@ -124,9 +124,9 @@ mathml: {} text: System`InputForm: -1.*^6 - System`OutputForm: "-1.\xD710^6" + System`OutputForm: "-1. x 10^6" System`StandardForm: -1.`*^6 - System`TraditionalForm: "-1.`\xD710^6" + System`TraditionalForm: "-1.` x 10^6" '-4': @@ -224,7 +224,7 @@ mathml: {} text: System`InputForm: 1.*^-6 - System`OutputForm: "1.\xD710^-6" + System`OutputForm: "1. x 10^-6" 1. 10^5: @@ -252,9 +252,9 @@ mathml: {} text: System`InputForm: 1.*^6 - System`OutputForm: "1.\xD710^6" + System`OutputForm: "1. x 10^6" System`StandardForm: 1.`*^6 - System`TraditionalForm: "1.`\xD710^6" + System`TraditionalForm: "1.` x 10^6" 1/(1+1/(1+1/a)): @@ -287,8 +287,8 @@ <|a -> x, b -> y, c -> <|d -> t|>|>: msg: Association latex: - System`InputForm: '\text{$<$$\vert$a -$>$ x, b -$>$ y, c -$>$ $<$$\vert$d -$>$ t$\vert$$>$$\vert$$>$}' - System`OutputForm: '\text{$<$$\vert$a -$>$ x, b -$>$ y, c -$>$ $<$$\vert$d -$>$ t$\vert$$>$$\vert$$>$}' + System`InputForm: '\text{$<$$\vert$a $\to$ x, b $\to$ y, c $\to$ $<$$\vert$d $\to$ t$\vert$$>$$\vert$$>$}' + System`OutputForm: '\text{$<$$\vert$a $\to$ x, b $\to$ y, c $\to$ $<$$\vert$d $\to$ t$\vert$$>$$\vert$$>$}' System`StandardForm: '\langle\vert a->x, b->y, c->\langle\vert d->t\vert\rangle \vert\rangle' System`TraditionalForm: '\langle\vert a->x, b->y, c->\langle\vert d->t\vert\rangle \vert\rangle' mathml: @@ -306,8 +306,8 @@ Association[a -> x, b -> y, c -> Association[d -> t, Association[e -> u]]]: msg: Nested Association latex: - System`InputForm: '\text{$<$$\vert$a -$>$ x, b -$>$ y, c -$>$ $<$$\vert$d -$>$ t, e -$>$ u$\vert$$>$$\vert$$>$}' - System`OutputForm: '\text{$<$$\vert$a -$>$ x, b -$>$ y, c -$>$ $<$$\vert$d -$>$ t, e -$>$ u$\vert$$>$$\vert$$>$}' + System`InputForm: '\text{$<$$\vert$a $\to$ x, b $\to$ y, c $\to$ $<$$\vert$d $\to$ t, e $\to$ u$\vert$$>$$\vert$$>$}' + System`OutputForm: '\text{$<$$\vert$a $\to$ x, b $\to$ y, c $\to$ $<$$\vert$d $\to$ t, e $\to$ u$\vert$$>$$\vert$$>$}' System`StandardForm: '\langle\vert a->x, b->y, c->\langle\vert d->t, e->u\vert\rangle \vert\rangle' System`TraditionalForm: '\langle\vert a->x, b->y, c->\langle\vert d->t, e->u\vert\rangle \vert\rangle' mathml: @@ -336,9 +336,9 @@ Complex[1.09*^12, 3.]: System`TraditionalForm: "\n \n 1.09\n ×\n \n 10\n 12\n \n \n +\n \n 3.\n \n I\n \n" text: System`InputForm: 1.09*^12 + 3.*I - System`OutputForm: "1.09\xD710^12 + 3. I" + System`OutputForm: "1.09 x 10^12 + 3. I" System`StandardForm: 1.09`*^12+3.` I - System`TraditionalForm: "1.09`\xD710^12+3.`\u2062I" + System`TraditionalForm: "1.09` x 10^12+3.`\u2062I" Graphics[{Text[a^b,{0,0}]}]: @@ -528,8 +528,8 @@ Integrate[F[x], {x, a, g[b]}]: text: System`InputForm: Integrate[F[x], {x, a, g[b]}] System`OutputForm: Integrate[F[x], {x, a, g[b]}] - System`StandardForm: 'Subsuperscript[∫, a, g[b]]⁢F[x]⁢𝑑x' - System`TraditionalForm: 'Subsuperscript[∫, a, g(b)]⁢F(x)⁢𝑑x' + System`StandardForm: 'Subsuperscript[\[Integral], a, g[b]]⁢F[x]⁢\[DifferentialD]x' + System`TraditionalForm: 'Subsuperscript[\[Integral], a, g(b)]⁢F(x)⁢\[DifferentialD]x' MatrixForm[{{a,b},{c,d}}]: msg: GridBox in a matrix diff --git a/test/format/test_format.py b/test/format/test_format.py index f6aeea269..bd824201f 100644 --- a/test/format/test_format.py +++ b/test/format/test_format.py @@ -115,10 +115,13 @@ def test_makeboxes_text_fragile(str_expr, str_expected, form, msg): format_result = result.format(session.evaluation, form) if msg: assert ( - format_result.to_text(evaluation=session.evaluation) == str_expected + format_result.to_text(evaluation=session.evaluation, encoding="ASCII") + == str_expected ), msg else: - strresult = format_result.to_text(evaluation=session.evaluation) + strresult = format_result.to_text( + evaluation=session.evaluation, encoding="ASCII" + ) assert strresult == str_expected @@ -130,9 +133,14 @@ def test_makeboxes_text(str_expr, str_expected, form, msg): result = session.evaluate(str_expr) format_result = result.format(session.evaluation, form) if msg: - assert format_result.to_text(evaluation=session.evaluation) == str_expected, msg + assert ( + format_result.to_text(evaluation=session.evaluation, encoding="ASCII") + == str_expected + ), msg else: - strresult = format_result.to_text(evaluation=session.evaluation) + strresult = format_result.to_text( + evaluation=session.evaluation, encoding="ASCII" + ) assert strresult == str_expected @@ -175,7 +183,9 @@ def test_makeboxes_tex(str_expr, str_expected, form, msg): == str_expected.strip() ), msg else: - strresult = format_result.to_text(evaluation=session.evaluation).strip() + strresult = format_result.to_text( + evaluation=session.evaluation, encoding="ASCII" + ).strip() assert strresult == str_expected @@ -209,7 +219,8 @@ def test_makeboxes_mathml(str_expr, str_expected, form, msg): format_result = result.format(session.evaluation, form) if msg: assert ( - format_result.to_mathml(evaluation=session.evaluation) == str_expected + format_result.to_mathml(evaluation=session.evaluation, encoding="ASCII") + == str_expected ), msg else: strresult = format_result.to_mathml(evaluation=session.evaluation) @@ -221,7 +232,7 @@ def test_makeboxes_mathml(str_expr, str_expected, form, msg): [ ( "OutputForm[Complex[2.0 ^ 40, 3]]", - "1.09951×10^12 + 3. I", + "1.09951 x 10^12 + 3. I", "OutputForm Complex", ), ( diff --git a/test/helper.py b/test/helper.py index dc0900eb4..7407920da 100644 --- a/test/helper.py +++ b/test/helper.py @@ -189,7 +189,7 @@ def check_evaluation_as_in_cli( if failure_message: assert res.result == str_expected, failure_message - assert res.result == str_expected + assert res.result == str_expected, f"'{res.result}'!='{str_expected}'" # List below could be a Tuple, but List looks better in the tests From 3d4b0a59a1ed37bf191419e46606afd808adc14f Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Wed, 25 Mar 2026 12:05:58 -0300 Subject: [PATCH 03/10] hangle encoding in doctests --- mathics/builtin/testing_expressions/logic.py | 2 +- mathics/doc/doc_entries.py | 20 ++++-- mathics/docpipeline.py | 5 +- mathics/eval/strings.py | 2 +- mathics/format/render/text.py | 70 +------------------- 5 files changed, 21 insertions(+), 78 deletions(-) diff --git a/mathics/builtin/testing_expressions/logic.py b/mathics/builtin/testing_expressions/logic.py index 841f83359..42e8c6490 100644 --- a/mathics/builtin/testing_expressions/logic.py +++ b/mathics/builtin/testing_expressions/logic.py @@ -272,7 +272,7 @@ class Implies(InfixOperator): If an expression does not evaluate to 'True' or 'False', 'Implies' returns a result in symbolic form: >> Implies[a, Implies[b, Implies[True, c]]] - = a \[Implies] b \[Implies] c + = a => b => c """ grouping = "Right" diff --git a/mathics/doc/doc_entries.py b/mathics/doc/doc_entries.py index 3d50a5736..7678a754d 100644 --- a/mathics/doc/doc_entries.py +++ b/mathics/doc/doc_entries.py @@ -13,6 +13,7 @@ from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Sequence, Tuple from mathics.core.evaluation import Message, Print, _Out +from mathics.format.render.encoding import encode_string_value if TYPE_CHECKING: from mathics.doc.structure import DocSection @@ -406,14 +407,18 @@ def strip_sentinal(line: str): def __str__(self) -> str: return self.test - def compare(self, result: Optional[str], out: tuple = tuple()) -> bool: + def compare( + self, result: Optional[str], out: tuple = tuple(), encoding="ASCII" + ) -> bool: """ Performs a doctest comparison between ``result`` and ``wanted`` and returns True if the test should be considered a success. """ - return self.compare_result(result) and self.compare_out(out) + return self.compare_result(result, encoding=encoding) and self.compare_out( + out, encoding=encoding + ) - def compare_out(self, outs: tuple = tuple()) -> bool: + def compare_out(self, outs: tuple = tuple(), encoding="ASCII") -> bool: """Compare messages and warnings produced during the evaluation of the test with the expected messages and warnings.""" # Check out @@ -434,12 +439,14 @@ def tabs_to_spaces(val): for got, wanted in zip(outs, wanted_outs): if wanted.text == "...": return True - if not tabs_to_spaces(got) == tabs_to_spaces(wanted): + if not tabs_to_spaces(got) == encode_string_value( + tabs_to_spaces(wanted), encoding=encoding + ): return False return True - def compare_result(self, result: Optional[str]): + def compare_result(self, result: Optional[str], encoding="ASCII"): """Compare a result with the expected result""" wanted = self.result # Check result @@ -458,6 +465,9 @@ def compare_result(self, result: Optional[str]): return False for res, want in zip(result_list, wanted_list): + # TODO_ Be more careful with special characters used in + # pattern matching. + want = encode_string_value(want, encoding=encoding) wanted_re = re.escape(want.strip()) wanted_re = wanted_re.replace("\\.\\.\\.", ".*?") wanted_re = f"^{wanted_re}$" diff --git a/mathics/docpipeline.py b/mathics/docpipeline.py index 8251eacfb..1e068635d 100644 --- a/mathics/docpipeline.py +++ b/mathics/docpipeline.py @@ -224,7 +224,9 @@ def test_case( return False time_start = datetime.now() - comparison_result = test.compare_result(result) + comparison_result = test.compare_result( + result, encoding=settings.SYSTEM_CHARACTER_ENCODING + ) if test_parameters.check_partial_elapsed_time: test_pipeline.print_and_log( @@ -232,7 +234,6 @@ def test_case( ) if not comparison_result: print("result != wanted") - print(test_pipeline.session.evaluate("$CharacterEncoding")) fail_msg = f"Result: {result}\nWanted: {test.result}" if out: fail_msg += "\nAdditional output:\n" diff --git a/mathics/eval/strings.py b/mathics/eval/strings.py index 14e006d63..73fcfc925 100644 --- a/mathics/eval/strings.py +++ b/mathics/eval/strings.py @@ -18,12 +18,12 @@ from mathics.core.list import ListExpression from mathics.core.symbols import Symbol, SymbolTrue from mathics.format.box import format_element +from mathics.format.render.encoding import EncodingNameError def eval_ToString( expr: BaseElement, form: Symbol, encoding: String, evaluation: Evaluation ) -> String: - from mathics.format.render.text import EncodingNameError boxes = format_element(expr, evaluation, form) try: diff --git a/mathics/format/render/text.py b/mathics/format/render/text.py index 7003eec2e..646def761 100644 --- a/mathics/format/render/text.py +++ b/mathics/format/render/text.py @@ -29,79 +29,11 @@ from mathics.format.box.graphics import prepare_elements as prepare_elements2d from mathics.format.box.graphics3d import prepare_elements as prepare_elements3d from mathics.format.form.util import _WrongFormattedExpression, text_cells_to_grid +from mathics.format.render.encoding import encode_string_value add_render_function(FormBox, convert_inner_box_field) -# Map WMA encoding names to Python encoding names -ENCODING_WMA_TO_PYTHON = { - "WindowsEastEurope": "cp1250", - "WindowsCyrillic": "cp1251", - "WindowsANSI": "cp1252", - "WindowsGreek": "cp1252", - "WindowsTurkish": "cp1254", -} - - -class EncodingNameError(Exception): - pass - - -def get_encoding_table(encoding: str): - """ - Return a dictionary with a map from - character codes in the internal (Unicode) - representation to the request encoding. - """ - if encoding == "Unicode": - return {} - - # In the final implementation, this should load the corresponding - # json table or an encoding file as in WMA - # SystemFiles/CharacterEncodings/*.m - # If the encoding is not available, raise an EncodingError - try: - return { - "ASCII": { - "⧴": ":>", - "⇒": "=>", - "↔": "<->", - "⩵": "==", - "∧": "&&", - "⊻": r"\[Xor]", - "≠": "!=", - "≤": "<=", - "→": "->", - "⇾": "->", - "⇾": "->", - "⇴": "->", - "∫": r"\[Integral]", - "": r"\[DifferentialD]", - "𝑑": r"\[DifferentialD]", - "⧦": r"\[Equivalent]", - "×": r" x ", - }, - "UTF-8": {}, - }[encoding] - except KeyError: - raise EncodingNameError - - -def encode_string_value(value: str, encoding: str): - """Convert an Unicode string `value` to the required `encoding`""" - - # In WMA, encodings are readed from SystemFiles/CharacterEncodings/*.m - # on the fly. We should load them from Mathics3-Scanner tables. - encoding_table = get_encoding_table(encoding) - if not encoding_table: - return value - result = "" - for ch in value: - ch = encoding_table.get(ch, ch) - result += ch - return result - - def fractionbox(box: FractionBox, **options) -> str: # Note: values set in `options` take precedence over `box_options` child_options = {**options, **box.box_options} From 0218bd99313b06244555649c39498c4442c5f586 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Wed, 25 Mar 2026 12:15:26 -0300 Subject: [PATCH 04/10] adjust tests --- test/format/format_tests.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/format/format_tests.yaml b/test/format/format_tests.yaml index 45a832f86..91f7146f2 100644 --- a/test/format/format_tests.yaml +++ b/test/format/format_tests.yaml @@ -287,13 +287,13 @@ <|a -> x, b -> y, c -> <|d -> t|>|>: msg: Association latex: - System`InputForm: '\text{$<$$\vert$a $\to$ x, b $\to$ y, c $\to$ $<$$\vert$d $\to$ t$\vert$$>$$\vert$$>$}' + System`InputForm: '\text{$<$$\vert$a -$>$ x, b -$>$ y, c -$>$ $<$$\vert$d -$>$ t$\vert$$>$$\vert$$>$}' System`OutputForm: '\text{$<$$\vert$a $\to$ x, b $\to$ y, c $\to$ $<$$\vert$d $\to$ t$\vert$$>$$\vert$$>$}' System`StandardForm: '\langle\vert a->x, b->y, c->\langle\vert d->t\vert\rangle \vert\rangle' System`TraditionalForm: '\langle\vert a->x, b->y, c->\langle\vert d->t\vert\rangle \vert\rangle' mathml: System`InputForm: <|a -> x, b -> y, c -> <|d -> t|>|> - System`OutputForm: '<|a -> x, b -> y, c -> <|d -> t|>|>' + System`OutputForm: '<|a ⇾ x, b ⇾ y, c ⇾ <|d ⇾ t|>|>' System`StandardForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n d\n ->\n t\n \n |>\n \n \n \n |>\n" System`TraditionalForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n d\n ->\n t\n \n |>\n \n \n \n |>\n" text: @@ -306,13 +306,13 @@ Association[a -> x, b -> y, c -> Association[d -> t, Association[e -> u]]]: msg: Nested Association latex: - System`InputForm: '\text{$<$$\vert$a $\to$ x, b $\to$ y, c $\to$ $<$$\vert$d $\to$ t, e $\to$ u$\vert$$>$$\vert$$>$}' + System`InputForm: '\text{$<$$\vert$a -$>$ x, b -$>$ y, c -$>$ $<$$\vert$d -$>$ t, e -$>$ u$\vert$$>$$\vert$$>$}' System`OutputForm: '\text{$<$$\vert$a $\to$ x, b $\to$ y, c $\to$ $<$$\vert$d $\to$ t, e $\to$ u$\vert$$>$$\vert$$>$}' System`StandardForm: '\langle\vert a->x, b->y, c->\langle\vert d->t, e->u\vert\rangle \vert\rangle' System`TraditionalForm: '\langle\vert a->x, b->y, c->\langle\vert d->t, e->u\vert\rangle \vert\rangle' mathml: System`InputForm: "<|a -> x, b -> y, c -> <|d -> t, e -> u|>|>" - System`OutputForm: '<|a -> x, b -> y, c -> <|d -> t, e -> u|>|>' + System`OutputForm: '<|a ⇾ x, b ⇾ y, c ⇾ <|d ⇾ t, e ⇾ u|>|>' System`StandardForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n \n d\n ->\n t\n \n ,\n \n e\n ->\n u\n \n \n |>\n \n \n \n |>\n" System`TraditionalForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n \n d\n ->\n t\n \n ,\n \n e\n ->\n u\n \n \n |>\n \n \n \n |>\n" text: From 1324c41be5ca096572f7ab479ecb942c5867b1a5 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Wed, 25 Mar 2026 12:32:54 -0300 Subject: [PATCH 05/10] commenting out the Mathml tests --- test/format/format_tests.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/format/format_tests.yaml b/test/format/format_tests.yaml index 91f7146f2..07760f9c8 100644 --- a/test/format/format_tests.yaml +++ b/test/format/format_tests.yaml @@ -293,7 +293,7 @@ System`TraditionalForm: '\langle\vert a->x, b->y, c->\langle\vert d->t\vert\rangle \vert\rangle' mathml: System`InputForm: <|a -> x, b -> y, c -> <|d -> t|>|> - System`OutputForm: '<|a ⇾ x, b ⇾ y, c ⇾ <|d ⇾ t|>|>' + # System`OutputForm: '<|a ⇾ x, b ⇾ y, c ⇾ <|d ⇾ t|>|>' System`StandardForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n d\n ->\n t\n \n |>\n \n \n \n |>\n" System`TraditionalForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n d\n ->\n t\n \n |>\n \n \n \n |>\n" text: @@ -312,7 +312,7 @@ Association[a -> x, b -> y, c -> Association[d -> t, Association[e -> u]]]: System`TraditionalForm: '\langle\vert a->x, b->y, c->\langle\vert d->t, e->u\vert\rangle \vert\rangle' mathml: System`InputForm: "<|a -> x, b -> y, c -> <|d -> t, e -> u|>|>" - System`OutputForm: '<|a ⇾ x, b ⇾ y, c ⇾ <|d ⇾ t, e ⇾ u|>|>' + # System`OutputForm: '<|a ⇾ x, b ⇾ y, c ⇾ <|d ⇾ t, e ⇾ u|>|>' System`StandardForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n \n d\n ->\n t\n \n ,\n \n e\n ->\n u\n \n \n |>\n \n \n \n |>\n" System`TraditionalForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n \n d\n ->\n t\n \n ,\n \n e\n ->\n u\n \n \n |>\n \n \n \n |>\n" text: From 79dcf9d8d57be688386f22029804c145c8efc76c Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Wed, 25 Mar 2026 12:36:27 -0300 Subject: [PATCH 06/10] adding missing module --- mathics/format/render/encoding.py | 74 +++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 mathics/format/render/encoding.py diff --git a/mathics/format/render/encoding.py b/mathics/format/render/encoding.py new file mode 100644 index 000000000..939526035 --- /dev/null +++ b/mathics/format/render/encoding.py @@ -0,0 +1,74 @@ +""" +Functions to format strings in a given encoding. +""" + +from typing import Dict + +# Map WMA encoding names to Python encoding names +ENCODING_WMA_TO_PYTHON = { + "WindowsEastEurope": "cp1250", + "WindowsCyrillic": "cp1251", + "WindowsANSI": "cp1252", + "WindowsGreek": "cp1252", + "WindowsTurkish": "cp1254", +} + + +class EncodingNameError(Exception): + pass + + +def get_encoding_table(encoding: str) -> Dict[str, str]: + """ + Return a dictionary with a map from + character codes in the internal (Unicode) + representation to the request encoding. + """ + if encoding == "Unicode": + return {} + + # In the final implementation, this should load the corresponding + # json table or an encoding file as in WMA + # SystemFiles/CharacterEncodings/*.m + # If the encoding is not available, raise an EncodingError + try: + return { + "ASCII": { + "⧴": ":>", + "⇒": "=>", + "↔": "<->", + "⩵": "==", + "∧": "&&", + "∨": "||", + "⊻": r"\[Xor]", + "≠": "!=", + "≤": "<=", + "→": "->", + "⇾": "->", + "⇾": "->", + "⇴": "->", + "∫": r"\[Integral]", + "": r"\[DifferentialD]", + "𝑑": r"\[DifferentialD]", + "⧦": r"\[Equivalent]", + "×": r" x ", + }, + "UTF-8": {}, + }[encoding] + except KeyError: + raise EncodingNameError + + +def encode_string_value(value: str, encoding: str) -> str: + """Convert an Unicode string `value` to the required `encoding`""" + + # In WMA, encodings are readed from SystemFiles/CharacterEncodings/*.m + # on the fly. We should load them from Mathics3-Scanner tables. + encoding_table = get_encoding_table(encoding) + if not encoding_table: + return value + result = "" + for ch in value: + ch = encoding_table.get(ch, ch) + result += ch + return result From e7f88e5674da95aa6dd5e15c5146e2bc1e9ce32d Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Wed, 25 Mar 2026 12:50:12 -0300 Subject: [PATCH 07/10] avoid circular import --- mathics/eval/strings.py | 2 +- mathics/format/render/encoding.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/mathics/eval/strings.py b/mathics/eval/strings.py index 73fcfc925..19972b9a7 100644 --- a/mathics/eval/strings.py +++ b/mathics/eval/strings.py @@ -18,12 +18,12 @@ from mathics.core.list import ListExpression from mathics.core.symbols import Symbol, SymbolTrue from mathics.format.box import format_element -from mathics.format.render.encoding import EncodingNameError def eval_ToString( expr: BaseElement, form: Symbol, encoding: String, evaluation: Evaluation ) -> String: + from mathics.format.render.encoding import EncodingNameError boxes = format_element(expr, evaluation, form) try: diff --git a/mathics/format/render/encoding.py b/mathics/format/render/encoding.py index 939526035..4e6ee5c48 100644 --- a/mathics/format/render/encoding.py +++ b/mathics/format/render/encoding.py @@ -45,7 +45,6 @@ def get_encoding_table(encoding: str) -> Dict[str, str]: "≤": "<=", "→": "->", "⇾": "->", - "⇾": "->", "⇴": "->", "∫": r"\[Integral]", "": r"\[DifferentialD]", From 0595532d47af4428a810a80897e37205cc1e3970 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Wed, 25 Mar 2026 16:04:33 -0300 Subject: [PATCH 08/10] using Mathics3-scanner tables. Moving encoding.py to mathics.eval --- mathics/builtin/testing_expressions/logic.py | 2 +- mathics/doc/doc_entries.py | 2 +- mathics/{format/render => eval}/encoding.py | 44 +++++++++++++------- mathics/eval/strings.py | 2 +- mathics/format/render/text.py | 2 +- 5 files changed, 32 insertions(+), 20 deletions(-) rename mathics/{format/render => eval}/encoding.py (87%) diff --git a/mathics/builtin/testing_expressions/logic.py b/mathics/builtin/testing_expressions/logic.py index 42e8c6490..841f83359 100644 --- a/mathics/builtin/testing_expressions/logic.py +++ b/mathics/builtin/testing_expressions/logic.py @@ -272,7 +272,7 @@ class Implies(InfixOperator): If an expression does not evaluate to 'True' or 'False', 'Implies' returns a result in symbolic form: >> Implies[a, Implies[b, Implies[True, c]]] - = a => b => c + = a \[Implies] b \[Implies] c """ grouping = "Right" diff --git a/mathics/doc/doc_entries.py b/mathics/doc/doc_entries.py index 7678a754d..c6eff64ad 100644 --- a/mathics/doc/doc_entries.py +++ b/mathics/doc/doc_entries.py @@ -13,7 +13,7 @@ from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Sequence, Tuple from mathics.core.evaluation import Message, Print, _Out -from mathics.format.render.encoding import encode_string_value +from mathics.eval.encoding import encode_string_value if TYPE_CHECKING: from mathics.doc.structure import DocSection diff --git a/mathics/format/render/encoding.py b/mathics/eval/encoding.py similarity index 87% rename from mathics/format/render/encoding.py rename to mathics/eval/encoding.py index 4e6ee5c48..cb4646b19 100644 --- a/mathics/format/render/encoding.py +++ b/mathics/eval/encoding.py @@ -4,6 +4,8 @@ from typing import Dict +from mathics.core.convert.op import operator_to_ascii, operator_to_unicode + # Map WMA encoding names to Python encoding names ENCODING_WMA_TO_PYTHON = { "WindowsEastEurope": "cp1250", @@ -13,27 +15,18 @@ "WindowsTurkish": "cp1254", } +UNICODE_CHARACTER_TO_ASCII = { + ch: operator_to_ascii.get(name, rf"\[{name}]") + for name, ch in operator_to_unicode.items() +} + class EncodingNameError(Exception): pass -def get_encoding_table(encoding: str) -> Dict[str, str]: - """ - Return a dictionary with a map from - character codes in the internal (Unicode) - representation to the request encoding. - """ - if encoding == "Unicode": - return {} - - # In the final implementation, this should load the corresponding - # json table or an encoding file as in WMA - # SystemFiles/CharacterEncodings/*.m - # If the encoding is not available, raise an EncodingError - try: - return { - "ASCII": { +r""" + { "⧴": ":>", "⇒": "=>", "↔": "<->", @@ -52,6 +45,25 @@ def get_encoding_table(encoding: str) -> Dict[str, str]: "⧦": r"\[Equivalent]", "×": r" x ", }, +""" + + +def get_encoding_table(encoding: str) -> Dict[str, str]: + """ + Return a dictionary with a map from + character codes in the internal (Unicode) + representation to the request encoding. + """ + if encoding == "Unicode": + return {} + + # In the final implementation, this should load the corresponding + # json table or an encoding file as in WMA + # SystemFiles/CharacterEncodings/*.m + # If the encoding is not available, raise an EncodingError + try: + return { + "ASCII": UNICODE_CHARACTER_TO_ASCII, "UTF-8": {}, }[encoding] except KeyError: diff --git a/mathics/eval/strings.py b/mathics/eval/strings.py index 19972b9a7..56f27815a 100644 --- a/mathics/eval/strings.py +++ b/mathics/eval/strings.py @@ -17,13 +17,13 @@ from mathics.core.expression_predefined import MATHICS3_INFINITY from mathics.core.list import ListExpression from mathics.core.symbols import Symbol, SymbolTrue +from mathics.eval.encoding import EncodingNameError from mathics.format.box import format_element def eval_ToString( expr: BaseElement, form: Symbol, encoding: String, evaluation: Evaluation ) -> String: - from mathics.format.render.encoding import EncodingNameError boxes = format_element(expr, evaluation, form) try: diff --git a/mathics/format/render/text.py b/mathics/format/render/text.py index 646def761..91f05c4c5 100644 --- a/mathics/format/render/text.py +++ b/mathics/format/render/text.py @@ -26,10 +26,10 @@ convert_inner_box_field, ) from mathics.core.symbols import Atom, SymbolTrue +from mathics.eval.encoding import encode_string_value from mathics.format.box.graphics import prepare_elements as prepare_elements2d from mathics.format.box.graphics3d import prepare_elements as prepare_elements3d from mathics.format.form.util import _WrongFormattedExpression, text_cells_to_grid -from mathics.format.render.encoding import encode_string_value add_render_function(FormBox, convert_inner_box_field) From 1670da6d26f7f3cff08d6f5c50f0bafce045271b Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Wed, 25 Mar 2026 16:25:35 -0300 Subject: [PATCH 09/10] remove hard coded table --- mathics/eval/encoding.py | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/mathics/eval/encoding.py b/mathics/eval/encoding.py index cb4646b19..3daa0380c 100644 --- a/mathics/eval/encoding.py +++ b/mathics/eval/encoding.py @@ -20,34 +20,21 @@ for name, ch in operator_to_unicode.items() } +# These characters are used in encoding +# in WMA, and differs from what we have +# in Mathics3-scanner tables: +UNICODE_CHARACTER_TO_ASCII.update( + { + "×": r" x ", + "": r"\[DifferentialD]", + } +) + class EncodingNameError(Exception): pass -r""" - { - "⧴": ":>", - "⇒": "=>", - "↔": "<->", - "⩵": "==", - "∧": "&&", - "∨": "||", - "⊻": r"\[Xor]", - "≠": "!=", - "≤": "<=", - "→": "->", - "⇾": "->", - "⇴": "->", - "∫": r"\[Integral]", - "": r"\[DifferentialD]", - "𝑑": r"\[DifferentialD]", - "⧦": r"\[Equivalent]", - "×": r" x ", - }, -""" - - def get_encoding_table(encoding: str) -> Dict[str, str]: """ Return a dictionary with a map from From d9e7eb5cce4dec05de5a9632cbc76ba1859ff5d1 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Fri, 27 Mar 2026 10:01:00 -0300 Subject: [PATCH 10/10] last tweaks --- mathics/doc/documentation/1-Manual.mdoc | 4 +-- mathics/docpipeline.py | 13 ++----- mathics/eval/encoding.py | 2 +- mathics/format/box/numberform.py | 4 +-- mathics/format/form/inputform.py | 6 ---- mathics/format/form/outputform.py | 7 ---- mathics/format/form/util.py | 6 +--- mathics/format/render/mathml.py | 17 ++++++++- test/format/format_tests.yaml | 48 ++++++++++++------------- 9 files changed, 49 insertions(+), 58 deletions(-) diff --git a/mathics/doc/documentation/1-Manual.mdoc b/mathics/doc/documentation/1-Manual.mdoc index e4116375f..c0ce24a43 100644 --- a/mathics/doc/documentation/1-Manual.mdoc +++ b/mathics/doc/documentation/1-Manual.mdoc @@ -656,10 +656,10 @@ You can also specify a list of rules: There is a "delayed" version of 'Rule' which can be specified by ':>' (similar to the relation of ':=' to '='): >> a :> 1 + 2 - = a :> 1 + 2 + = a ⧴ 1 + 2 >> a -> 1 + 2 - = a -> 3 + = a ⇾ 3 This is useful when the right side of a rule should not be evaluated immediately (before matching): >> {1, 2} /. x_Integer -> N[x] diff --git a/mathics/docpipeline.py b/mathics/docpipeline.py index 1e068635d..c46ce0ce6 100644 --- a/mathics/docpipeline.py +++ b/mathics/docpipeline.py @@ -45,6 +45,8 @@ # When 3.8 is base, the below can be a Literal type. INVALID_TEST_GROUP_SETUP = (None, None) +CHARACTER_ENCODING = settings.SYSTEM_CHARACTER_ENCODING + TestParameters = namedtuple( "TestParameters", [ @@ -138,10 +140,6 @@ def validate_group_setup( else: self.output_data = {} - # For consistency set the character encoding ASCII which is - # the lowest common denominator available on all systems. - settings.SYSTEM_CHARACTER_ENCODING = "ASCII" - if self.session.definitions is None: self.print_and_log("Definitions are not initialized.") return INVALID_TEST_GROUP_SETUP @@ -224,9 +222,7 @@ def test_case( return False time_start = datetime.now() - comparison_result = test.compare_result( - result, encoding=settings.SYSTEM_CHARACTER_ENCODING - ) + comparison_result = test.compare_result(result, encoding=CHARACTER_ENCODING) if test_parameters.check_partial_elapsed_time: test_pipeline.print_and_log( @@ -500,10 +496,7 @@ def test_tests( """ test_status: TestStatus = test_pipeline.status test_parameters: TestParameters = test_pipeline.parameters - # For consistency set the character encoding ASCII which is - # the lowest common denominator available on all systems. - settings.SYSTEM_CHARACTER_ENCODING = "ASCII" test_pipeline.reset_user_definitions() output_data, names = test_pipeline.validate_group_setup( diff --git a/mathics/eval/encoding.py b/mathics/eval/encoding.py index 3daa0380c..5e90239f5 100644 --- a/mathics/eval/encoding.py +++ b/mathics/eval/encoding.py @@ -25,7 +25,7 @@ # in Mathics3-scanner tables: UNICODE_CHARACTER_TO_ASCII.update( { - "×": r" x ", + operator_to_unicode["Times"]: r" x ", "": r"\[DifferentialD]", } ) diff --git a/mathics/format/box/numberform.py b/mathics/format/box/numberform.py index 4b7fffe30..81a040bf3 100644 --- a/mathics/format/box/numberform.py +++ b/mathics/format/box/numberform.py @@ -17,6 +17,7 @@ Real, String, ) +from mathics.core.convert.op import operator_to_unicode from mathics.core.element import BaseElement, BoxElementMixin from mathics.core.evaluation import Evaluation from mathics.core.expression import Expression @@ -49,8 +50,7 @@ def default_numberformat_outputform(man, base, exp, opts): "ExponentFunction": lambda x: (SymbolNull if abs(x.value) <= 5 else x), "ExponentStep": 1, "NumberFormat": default_numberformat_outputform, - # TODO: Pick from MathicsScanner tables... - "NumberMultiplier": "×", + "NumberMultiplier": operator_to_unicode["Times"], "NumberPadding": ["", "0"], "NumberPoint": ".", "NumberSeparator": [",", ""], diff --git a/mathics/format/form/inputform.py b/mathics/format/form/inputform.py index 5fd1b2564..233198f97 100644 --- a/mathics/format/form/inputform.py +++ b/mathics/format/form/inputform.py @@ -39,7 +39,6 @@ SymbolRight, ) from mathics.format.box.formatvalues import do_format # , format_element -from mathics.settings import SYSTEM_CHARACTER_ENCODING from .util import ( ARITHMETIC_OPERATOR_STRINGS, @@ -154,7 +153,6 @@ def _infix_expression_to_inputform_text( # has a head that matches with a symbol associated to an infix # operator, WMA builds its inputform without passing through # its "Infix" form. - kwargs["encoding"] = kwargs.get("encoding", SYSTEM_CHARACTER_ENCODING) operands, ops_lst, precedence, group = collect_in_pre_post_arguments( expr, evaluation, **kwargs ) @@ -198,7 +196,6 @@ def _prefix_expression_to_inputform_text( """ Convert Prefix[...] into a OutputForm string. """ - kwargs["encoding"] = kwargs.get("encoding", SYSTEM_CHARACTER_ENCODING) operands, op_head, precedence, group = collect_in_pre_post_arguments( expr, evaluation, **kwargs ) @@ -206,7 +203,6 @@ def _prefix_expression_to_inputform_text( if len(operands) != 1: raise _WrongFormattedExpression operand = operands[0] - kwargs["encoding"] = kwargs.get("encoding", SYSTEM_CHARACTER_ENCODING) target_txt = render_input_form(operand, evaluation, **kwargs) parenthesized = group in (None, SymbolRight, SymbolNonAssociative) target_txt = parenthesize(precedence, operand, target_txt, parenthesized) @@ -220,7 +216,6 @@ def _postfix_expression_to_inputform_text( """ Convert Postfix[...] into a OutputForm string. """ - kwargs["encoding"] = kwargs.get("encoding", SYSTEM_CHARACTER_ENCODING) operands, op_head, precedence, group = collect_in_pre_post_arguments( expr, evaluation, **kwargs ) @@ -295,7 +290,6 @@ def _rule_to_inputform_text(expr, evaluation: Evaluation, **kwargs) -> str: """Rule|RuleDelayed[{...}]""" head = expr.head elements = expr.elements - kwargs["encoding"] = kwargs.get("encoding", SYSTEM_CHARACTER_ENCODING) if len(elements) != 2: return _generic_to_inputform_text(expr, evaluation, **kwargs) pat, rule = (render_input_form(elem, evaluation, **kwargs) for elem in elements) diff --git a/mathics/format/form/outputform.py b/mathics/format/form/outputform.py index 40c41a68e..83f1b0b1f 100644 --- a/mathics/format/form/outputform.py +++ b/mathics/format/form/outputform.py @@ -47,7 +47,6 @@ get_numberform_parameters, numberform_to_boxes, ) -from mathics.settings import SYSTEM_CHARACTER_ENCODING from .inputform import render_input_form from .util import ( @@ -242,7 +241,6 @@ def render_output_form(expr: BaseElement, evaluation: Evaluation, **kwargs): """ format_expr: Expression = do_format(expr, evaluation, SymbolOutputForm) # type: ignore - kwargs.setdefault("encoding", "Unicode") while format_expr.has_form("HoldForm", 1): # type: ignore format_expr = format_expr.elements[0] @@ -382,7 +380,6 @@ def _infix_outputform_text(expr: Expression, evaluation: Evaluation, **kwargs) - # has a head that matches with a symbol associated to an infix # operator, WMA builds its inputform without passing through # its "Infix" form. - kwargs["encoding"] = kwargs.get("encoding", SYSTEM_CHARACTER_ENCODING) operands, ops_lst, precedence, group = collect_in_pre_post_arguments( expr, evaluation, **kwargs ) @@ -682,7 +679,6 @@ def _prefix_output_text(expr: Expression, evaluation: Evaluation, **kwargs) -> s if not isinstance(expr.head, Symbol): raise _WrongFormattedExpression - kwargs["encoding"] = kwargs.get("encoding", SYSTEM_CHARACTER_ENCODING) operands, op_head, precedence, group = collect_in_pre_post_arguments( expr, evaluation, **kwargs ) @@ -692,7 +688,6 @@ def _prefix_output_text(expr: Expression, evaluation: Evaluation, **kwargs) -> s if not isinstance(op_head, str): raise _WrongFormattedExpression operand = operands[0] - kwargs["encoding"] = kwargs.get("encoding", SYSTEM_CHARACTER_ENCODING) target_txt = render_output_form(operand, evaluation, **kwargs) parenthesized = group in (None, SymbolRight, SymbolNonAssociative) target_txt = parenthesize(precedence, operand, target_txt, parenthesized) @@ -707,7 +702,6 @@ def _postfix_output_text(expr: Expression, evaluation: Evaluation, **kwargs) -> if not isinstance(expr.head, Symbol): raise _WrongFormattedExpression - kwargs["encoding"] = kwargs.get("encoding", SYSTEM_CHARACTER_ENCODING) operands, op_head, precedence, group = collect_in_pre_post_arguments( expr, evaluation, **kwargs ) @@ -777,7 +771,6 @@ def rule_to_outputform_text(expr, evaluation: Evaluation, **kwargs): raise _WrongFormattedExpression elements = expr.elements - kwargs["encoding"] = kwargs.get("encoding", SYSTEM_CHARACTER_ENCODING) if len(elements) != 2: return _default_render_output_form(expr, evaluation, **kwargs) pat, rule = (render_output_form(elem, evaluation, **kwargs) for elem in elements) diff --git a/mathics/format/form/util.py b/mathics/format/form/util.py index a3a7a7584..1160396ac 100644 --- a/mathics/format/form/util.py +++ b/mathics/format/form/util.py @@ -145,7 +145,6 @@ def collect_in_pre_post_arguments( def get_operator_str(head, evaluation, **kwargs) -> str: - encoding = kwargs["encoding"] if isinstance(head, String): op_str = head.value elif isinstance(head, Symbol): @@ -154,10 +153,7 @@ def get_operator_str(head, evaluation, **kwargs) -> str: render_function = kwargs["_render_function"] return render_function(head, evaluation, **kwargs) - if encoding == "ASCII": - operator = operator_to_ascii.get(op_str, op_str) - else: - operator = operator_to_unicode.get(op_str, op_str) + operator = operator_to_unicode.get(op_str, op_str) return operator diff --git a/mathics/format/render/mathml.py b/mathics/format/render/mathml.py index 0594a2f8b..f71dfb6a6 100644 --- a/mathics/format/render/mathml.py +++ b/mathics/format/render/mathml.py @@ -69,10 +69,25 @@ def convert_inner_box(box, **options): def encode_mathml(text: str) -> str: + """ + Convert a string into a MathML code. + * Escape special characters + * Replace non-ANSI characters by character codes. + """ text = text.replace("&", "&").replace("<", "<").replace(">", ">") text = text.replace('"', """).replace(" ", " ") text = text.replace("\n", '') - return text + # Now, handle non-ansi characters using the + # the form '&#{code};' + # Notice that this should happend after escaping special characters: + result = "" + for c in text: + code = ord(c) + if code > 127: + result += f"&#{code};" + else: + result += c + return result extra_operators = { diff --git a/test/format/format_tests.yaml b/test/format/format_tests.yaml index 07760f9c8..71e70a839 100644 --- a/test/format/format_tests.yaml +++ b/test/format/format_tests.yaml @@ -70,10 +70,10 @@ System`StandardForm: "\\text{$\\pi$ is a trascendental number}" System`TraditionalForm: "\\text{$\\pi$ is a trascendental number}" mathml: - System`InputForm: "\u03C0 is a trascendental number" - System`OutputForm: "\u03C0 is a trascendental number" - System`StandardForm: "\u03C0 is a trascendental number" - System`TraditionalForm: "\u03C0 is a trascendental number" + System`InputForm: "π is a trascendental number" + System`OutputForm: "π is a trascendental number" + System`StandardForm: "π is a trascendental number" + System`TraditionalForm: "π is a trascendental number" text: System`InputForm: "\"\u03C0 is a trascendental number\"" System`OutputForm: "\u03C0 is a trascendental number" @@ -287,13 +287,13 @@ <|a -> x, b -> y, c -> <|d -> t|>|>: msg: Association latex: - System`InputForm: '\text{$<$$\vert$a -$>$ x, b -$>$ y, c -$>$ $<$$\vert$d -$>$ t$\vert$$>$$\vert$$>$}' + System`InputForm: '\text{$<$$\vert$a $\to$ x, b $\to$ y, c $\to$ $<$$\vert$d $\to$ t$\vert$$>$$\vert$$>$}' System`OutputForm: '\text{$<$$\vert$a $\to$ x, b $\to$ y, c $\to$ $<$$\vert$d $\to$ t$\vert$$>$$\vert$$>$}' System`StandardForm: '\langle\vert a->x, b->y, c->\langle\vert d->t\vert\rangle \vert\rangle' System`TraditionalForm: '\langle\vert a->x, b->y, c->\langle\vert d->t\vert\rangle \vert\rangle' mathml: - System`InputForm: <|a -> x, b -> y, c -> <|d -> t|>|> - # System`OutputForm: '<|a ⇾ x, b ⇾ y, c ⇾ <|d ⇾ t|>|>' + System`InputForm: <|a ⇾ x, b ⇾ y, c ⇾ <|d ⇾ t|>|> + System`OutputForm: '<|a ⇾ x, b ⇾ y, c ⇾ <|d ⇾ t|>|>' System`StandardForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n d\n ->\n t\n \n |>\n \n \n \n |>\n" System`TraditionalForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n d\n ->\n t\n \n |>\n \n \n \n |>\n" text: @@ -306,13 +306,13 @@ Association[a -> x, b -> y, c -> Association[d -> t, Association[e -> u]]]: msg: Nested Association latex: - System`InputForm: '\text{$<$$\vert$a -$>$ x, b -$>$ y, c -$>$ $<$$\vert$d -$>$ t, e -$>$ u$\vert$$>$$\vert$$>$}' + System`InputForm: '\text{$<$$\vert$a $\to$ x, b $\to$ y, c $\to$ $<$$\vert$d $\to$ t, e $\to$ u$\vert$$>$$\vert$$>$}' System`OutputForm: '\text{$<$$\vert$a $\to$ x, b $\to$ y, c $\to$ $<$$\vert$d $\to$ t, e $\to$ u$\vert$$>$$\vert$$>$}' System`StandardForm: '\langle\vert a->x, b->y, c->\langle\vert d->t, e->u\vert\rangle \vert\rangle' System`TraditionalForm: '\langle\vert a->x, b->y, c->\langle\vert d->t, e->u\vert\rangle \vert\rangle' mathml: - System`InputForm: "<|a -> x, b -> y, c -> <|d -> t, e -> u|>|>" - # System`OutputForm: '<|a ⇾ x, b ⇾ y, c ⇾ <|d ⇾ t, e ⇾ u|>|>' + System`InputForm: "<|a ⇾ x, b ⇾ y, c ⇾ <|d ⇾ t, e ⇾ u|>|>" + System`OutputForm: '<|a ⇾ x, b ⇾ y, c ⇾ <|d ⇾ t, e ⇾ u|>|>' System`StandardForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n \n d\n ->\n t\n \n ,\n \n e\n ->\n u\n \n \n |>\n \n \n \n |>\n" System`TraditionalForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n \n d\n ->\n t\n \n ,\n \n e\n ->\n u\n \n \n |>\n \n \n \n |>\n" text: @@ -331,9 +331,9 @@ Complex[1.09*^12, 3.]: System`TraditionalForm: 1.09\times 10^{12}+3. I mathml: System`InputForm: 1.09*^12 + 3.*I - System`OutputForm: '1.09×10^12 + 3. I' + System`OutputForm: '1.09×10^12 + 3. I' System`StandardForm: "\n \n 1.09\n *^\n 12\n \n +\n \n 3.\n  \n I\n \n" - System`TraditionalForm: "\n \n 1.09\n ×\n \n 10\n 12\n \n \n +\n \n 3.\n \n I\n \n" + System`TraditionalForm: "\n \n 1.09\n ×\n \n 10\n 12\n \n \n +\n \n 3.\n \n I\n \n" text: System`InputForm: 1.09*^12 + 3.*I System`OutputForm: "1.09 x 10^12 + 3. I" @@ -458,10 +458,10 @@ Graphics[{}]: System`TraditionalForm: "\\begin{array}{cc} \\text{Spanish} & \\text{Hola!}\\\\\ \ \\text{Portuguese} & \\text{Ol\\`{a}!}\\\\ \\text{English} & \\text{Hi!}\\end{array}" mathml: - System`InputForm: "Grid[{{"Spanish", "Hola!"}, {"Portuguese", "Olà!"}, {"English", "Hi!"}}]" - System`OutputForm: 'Spanish      Hola!Portuguese   Olà!English      Hi!' - System`StandardForm: "\n \n \n Spanish\n \n \n Hola!\n \n \n \n \n Portuguese\n \n \n Olà!\n \n \n \n \n English\n \n \n Hi!\n \n \n" - System`TraditionalForm: "\n \n \n Spanish\n \n \n Hola!\n \n \n \n \n Portuguese\n \n \n Olà!\n \n \n \n \n English\n \n \n Hi!\n \n \n" + System`InputForm: "Grid[{{"Spanish", "Hola!"}, {"Portuguese", "Olà!"}, {"English", "Hi!"}}]" + System`OutputForm: 'Spanish      Hola!Portuguese   Olà!English      Hi!' + System`StandardForm: "\n \n \n Spanish\n \n \n Hola!\n \n \n \n \n Portuguese\n \n \n Olà!\n \n \n \n \n English\n \n \n Hi!\n \n \n" + System`TraditionalForm: "\n \n \n Spanish\n \n \n Hola!\n \n \n \n \n Portuguese\n \n \n Olà!\n \n \n \n \n English\n \n \n Hi!\n \n \n" text: System`InputForm: "Grid[{{\"Spanish\", \"Hola!\"}, {\"Portuguese\", \"Ol\xE0!\"\ }, {\"English\", \"Hi!\"}}]" @@ -523,8 +523,8 @@ Integrate[F[x], {x, a, g[b]}]: mathml: System`InputForm: 'Integrate[F[x], {x, a, g[b]}]' System`OutputForm: 'Integrate[F[x], {x, a, g[b]}]' - System`StandardForm: "\n \n \n a\n \n g\n [\n b\n ]\n \n \n \u2062\n \n F\n [\n x\n ]\n \n \u2062\n \n 𝑑\n x\n \n" - System`TraditionalForm: "\n \n \n a\n \n g\n (\n b\n )\n \n \n \u2062\n \n F\n (\n x\n )\n \n \u2062\n \n 𝑑\n x\n \n" + System`StandardForm: "\n \n \n a\n \n g\n [\n b\n ]\n \n \n \n \n F\n [\n x\n ]\n \n \n \n 𝑑\n x\n \n" + System`TraditionalForm: "\n \n \n a\n \n g\n (\n b\n )\n \n \n \n \n F\n (\n x\n )\n \n \n \n 𝑑\n x\n \n" text: System`InputForm: Integrate[F[x], {x, a, g[b]}] System`OutputForm: Integrate[F[x], {x, a, g[b]}] @@ -730,8 +730,8 @@ TableForm[{{a,b},{c,d}}]: mathml: System`InputForm: Pi System`OutputForm: 'Pi' - System`StandardForm: "\u03C0" - System`TraditionalForm: "\u03C0" + System`StandardForm: "π" + System`TraditionalForm: "π" text: System`InputForm: Pi System`OutputForm: Pi @@ -747,10 +747,10 @@ TableForm[{{a,b},{c,d}}]: System`StandardForm: \alpha System`TraditionalForm: \alpha mathml: - System`InputForm: 'α' - System`OutputForm: 'α' - System`StandardForm: 'α' - System`TraditionalForm: 'α' + System`InputForm: 'α' + System`OutputForm: 'α' + System`StandardForm: 'α' + System`TraditionalForm: 'α' text: System`InputForm: 'α' System`OutputForm: 'α'