From 40740d8e1eade3470e8dbd409ef66f4d00c88d86 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sun, 29 Mar 2026 04:49:07 -0300 Subject: [PATCH 01/11] partial --- mathics/builtin/box/layout.py | 18 +++++++++ mathics/builtin/layout.py | 5 ++- mathics/core/convert/op.py | 62 ++++++++++++++++++++++++++++- mathics/eval/encoding.py | 53 ++++++++++++++++++++++++ mathics/format/form/outputform.py | 29 +++++++++++--- mathics/format/render/text.py | 10 ++++- test/builtin/atomic/test_strings.py | 2 +- 7 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 mathics/eval/encoding.py diff --git a/mathics/builtin/box/layout.py b/mathics/builtin/box/layout.py index 706cd777f..3aeda280f 100644 --- a/mathics/builtin/box/layout.py +++ b/mathics/builtin/box/layout.py @@ -459,6 +459,24 @@ def is_multiline(self) -> bool: return any(item.is_multiline for item in self.items) +class ShowSpecialCharacters(Builtin): + """ + + :WMA link: + https://reference.wolfram.com/language/ref/ShowSpecialCharacters.html +
+
'ShowSpecialCharacters' +
is an option for 'Style' and 'Cell' that directs whether non-ANSI characters must be shown as special characters or by escaped sequences. +
+ + + """ + + summary_text = "cell option directing whether show special characters in a reversible ANSI format." + + class ShowStringCharacters(Builtin): """ diff --git a/mathics/builtin/layout.py b/mathics/builtin/layout.py index 6caa1170f..09b81588e 100644 --- a/mathics/builtin/layout.py +++ b/mathics/builtin/layout.py @@ -547,7 +547,10 @@ class Style(Builtin): """ summary_text = "wrapper for styles and style options to apply" - options = {"ImageSizeMultipliers": "Automatic"} + options = { + "ImageSizeMultipliers": "Automatic", + "$OptionSyntax": "Ignore", + } rules = { "MakeBoxes[Style[expr_, OptionsPattern[Style]], f_]": ( "StyleBox[MakeBoxes[expr, f], " diff --git a/mathics/core/convert/op.py b/mathics/core/convert/op.py index 386f3bdf1..1f5c8c9df 100644 --- a/mathics/core/convert/op.py +++ b/mathics/core/convert/op.py @@ -12,6 +12,21 @@ ) ascii_operator_to_symbol = NAMED_CHARACTERS_COLLECTION["ascii-operator-to-symbol"] +CHARACTER_TO_NAME = { + char: rf"\[{name}]" + for name, char in NAMED_CHARACTERS_COLLECTION["named-characters"].items() +} + + +ESCAPE_CODE_BY_DIGITS = { + 1: r"\.0", + 2: r"\.", + 3: r"\:0", + 4: r"\:", + 5: r"\|0", + 6: r"\|", +} + builtin_constants = NAMED_CHARACTERS_COLLECTION["builtin-constants"] operator_to_unicode = NAMED_CHARACTERS_COLLECTION["operator-to-unicode"] operator_to_ascii = NAMED_CHARACTERS_COLLECTION["operator-to-ascii"] @@ -19,10 +34,31 @@ val: operator_to_ascii[key] for key, val in operator_to_unicode.items() } + +# This dictionary is used for the default encoding from Unicode/UTF-8 to ASCII + +UNICODE_CHARACTER_TO_ASCII = CHARACTER_TO_NAME.copy() +UNICODE_CHARACTER_TO_ASCII.update( + { + ch: operator_to_ascii[name] + for name, ch in operator_to_unicode.items() + if name in operator_to_ascii + } +) +# These characters are used in encoding +# in WMA, and differs from what we have +# in Mathics3-scanner tables: +UNICODE_CHARACTER_TO_ASCII.update( + { + operator_to_unicode["Times"]: r" x ", + "": r"\[DifferentialD]", + } +) + + UNICODE_TO_AMSLATEX = NAMED_CHARACTERS_COLLECTION.get("unicode-to-amslatex", {}) UNICODE_TO_LATEX = NAMED_CHARACTERS_COLLECTION.get("unicode-to-latex", {}) - AMSTEX_OPERATORS = { NAMED_CHARACTERS["Prime"]: "'", NAMED_CHARACTERS["Prime"] * 2: "''", @@ -48,6 +84,30 @@ } +def string_to_invertible_ansi(string: str): + """ + Replace non-ANSI characters by their names. If the character + does not have a name, use the WMA hex character code form. + Passing the string through `evaluation.parse` brings back + the original string. + This is used in particular for rendering `FullForm` expressions, + and when `Style` is called with both the options + `ShowStringCharacters->True` and `ShowSpecialCharacters->False`. + """ + result = "" + for c in string: + ord_c = ord(c) + if ord_c < 128: + result += c + else: + named = CHARACTER_TO_NAME.get(c, None) + if named is None: + named = hex(ord_c)[2:] + named = ESCAPE_CODE_BY_DIGITS[len(named)] + named + result += named + return result + + def is_named_operator(str_op): if len(str_op) < 3: return False diff --git a/mathics/eval/encoding.py b/mathics/eval/encoding.py new file mode 100644 index 000000000..8930a74d5 --- /dev/null +++ b/mathics/eval/encoding.py @@ -0,0 +1,53 @@ +""" +Functions to format strings in a given encoding. +""" + +from typing import Dict + +from mathics.core.convert.op import UNICODE_CHARACTER_TO_ASCII + + +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 + # TODO: implement the load of .m encodings. + # Format: + # (*name*) + # {"7Bit"|"8Bit"|"16Bit",{{target_Integer,src_String}|{target_Integer, src_String, False},...}} + # + # If the encoding is not available, raise an EncodingError + try: + return { + "ASCII": UNICODE_CHARACTER_TO_ASCII, + "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 diff --git a/mathics/format/form/outputform.py b/mathics/format/form/outputform.py index a2e118459..678c01ac8 100644 --- a/mathics/format/form/outputform.py +++ b/mathics/format/form/outputform.py @@ -88,8 +88,8 @@ def _default_render_output_form( if isinstance(expr, Atom): result = expr.atom_to_boxes(SymbolOutputForm, evaluation) if isinstance(result, String): - return result.value - return result.to_text() + return result.to_text(**kwargs) + return result.to_text(**kwargs) expr_head = expr.head head = render_output_form(expr_head, evaluation, **kwargs) @@ -835,12 +835,15 @@ def _slotsequence_outputform_text(expr: Expression, evaluation: Evaluation, **kw @register_outputform("System`String") def string_render_output_form(expr: String, evaluation: Evaluation, **kwargs) -> str: + from mathics.format.render.text import string as render_string + # lines = expr.value.split("\n") # max_len = max([len(line) for line in lines]) # lines = [line + (max_len - len(line)) * " " for line in lines] # return "\n".join(lines) - value = expr.value - return value + # value = expr.value + # return value + return render_string(expr, **kwargs) @register_outputform("System`StringForm") @@ -940,7 +943,23 @@ def style_to_outputform_text(expr: Expression, evaluation: Evaluation, **kwargs) elements = expr.elements if not elements: raise _WrongFormattedExpression - return render_output_form(elements[0], evaluation, **kwargs) + elem, *style_and_options = elements + options = {} + if style_and_options: + style, *style_and_options = style_and_options + option = style.get_option_values(evaluation) + if option is not None: + options.update(option) + + for opt_arg in style_and_options: + option = opt_arg.get_option_values(evaluation) + if option is None: + raise _WrongFormattedExpression + for opt, val in option.items(): + options[opt] = val + + kwargs.update(options) + return render_output_form(elem, evaluation, **kwargs) @register_outputform("System`Symbol") diff --git a/mathics/format/render/text.py b/mathics/format/render/text.py index 1b07691e3..e3a917fe0 100644 --- a/mathics/format/render/text.py +++ b/mathics/format/render/text.py @@ -20,13 +20,14 @@ TagBox, ) from mathics.core.atoms import String +from mathics.core.convert.op import string_to_invertible_ansi from mathics.core.exceptions import BoxConstructError from mathics.core.formatter import ( add_render_function, convert_box_to_format, convert_inner_box_field, ) -from mathics.core.symbols import Atom, SymbolTrue +from mathics.core.symbols import Atom, SymbolFalse, SymbolTrue from mathics.format.box.graphics import prepare_elements as prepare_elements2d from mathics.format.box.graphics3d import prepare_elements as prepare_elements3d from mathics.format.form.util import _WrongFormattedExpression, text_cells_to_grid @@ -156,6 +157,13 @@ def string(s: String, **options) -> str: show_string_characters = ( options.get("System`ShowStringCharacters", None) is SymbolTrue ) + show_special_characters = (not show_string_characters) or ( + not (options.get("System`ShowSpecialCharacters", None) is SymbolFalse) + ) + + if not show_special_characters: + value = string_to_invertible_ansi(value) + if value.startswith('"') and value.endswith('"'): # nopep8 if not show_string_characters: value = value[1:-1] diff --git a/test/builtin/atomic/test_strings.py b/test/builtin/atomic/test_strings.py index eb509e366..61c0c7fec 100644 --- a/test/builtin/atomic/test_strings.py +++ b/test/builtin/atomic/test_strings.py @@ -103,7 +103,7 @@ def test_alphabet(str_expr, str_expected, fail_msg, warnings): ( 'ToExpression["1+"]', ( - "Incomplete expression; more input is needed (line 1 of \"ToExpression['1+']\").", + "Incomplete expression; more input is needed (line 1 of ToExpression['1+']).", ), "$Failed", ), From 5d7b713de049776b6d763c4e9a8104dd03ae953b Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sun, 29 Mar 2026 10:10:50 -0300 Subject: [PATCH 02/11] adjust render OutputForm. adjust tests. --- mathics/builtin/files_io/files.py | 1 + mathics/core/evaluation.py | 7 ++++++- mathics/format/form/outputform.py | 10 +++++++++- test/builtin/atomic/test_strings.py | 2 +- test/format/format_tests.yaml | 16 ++++++++++------ 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/mathics/builtin/files_io/files.py b/mathics/builtin/files_io/files.py index 6cff9887a..3407a0195 100644 --- a/mathics/builtin/files_io/files.py +++ b/mathics/builtin/files_io/files.py @@ -327,6 +327,7 @@ def eval(self, path, evaluation: Evaluation, options: dict): result = result[:-1] for res in result: + print("show", String(res).value) evaluation.print_out(String(res)) return SymbolNull diff --git a/mathics/core/evaluation.py b/mathics/core/evaluation.py index e048c7418..ae8c162ba 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 = "UTF-8" + 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/format/form/outputform.py b/mathics/format/form/outputform.py index 678c01ac8..86f4e411f 100644 --- a/mathics/format/form/outputform.py +++ b/mathics/format/form/outputform.py @@ -25,7 +25,14 @@ from mathics.core.expression import BoxError, Expression from mathics.core.list import ListExpression from mathics.core.number import dps -from mathics.core.symbols import Atom, Symbol, SymbolFullForm, SymbolList, SymbolTimes +from mathics.core.symbols import ( + Atom, + Symbol, + SymbolFullForm, + SymbolList, + SymbolTimes, + SymbolTrue, +) from mathics.core.systemsymbols import ( SymbolDerivative, SymbolInfinity, @@ -843,6 +850,7 @@ def string_render_output_form(expr: String, evaluation: Evaluation, **kwargs) -> # return "\n".join(lines) # value = expr.value # return value + kwargs["System`ShowStringCharacters"] = SymbolTrue return render_string(expr, **kwargs) diff --git a/test/builtin/atomic/test_strings.py b/test/builtin/atomic/test_strings.py index 61c0c7fec..eb509e366 100644 --- a/test/builtin/atomic/test_strings.py +++ b/test/builtin/atomic/test_strings.py @@ -103,7 +103,7 @@ def test_alphabet(str_expr, str_expected, fail_msg, warnings): ( 'ToExpression["1+"]', ( - "Incomplete expression; more input is needed (line 1 of ToExpression['1+']).", + "Incomplete expression; more input is needed (line 1 of \"ToExpression['1+']\").", ), "$Failed", ), diff --git a/test/format/format_tests.yaml b/test/format/format_tests.yaml index 569bb769b..4205235e2 100644 --- a/test/format/format_tests.yaml +++ b/test/format/format_tests.yaml @@ -65,17 +65,19 @@ '"\[Pi] is a trascendental number"': msg: A String latex: - System`InputForm: "\\text{``$\\pi$ is a trascendental number''}" + # TODO: This should be fixed later + # System`InputForm: "\\text{``$\\pi$ is a trascendental number''}" System`OutputForm: "\\text{$\\pi$ is a trascendental number}" System`StandardForm: "\\text{$\\pi$ is a trascendental number}" System`TraditionalForm: "\\text{$\\pi$ is a trascendental number}" mathml: - System`InputForm: "\u03C0 is a trascendental number" + # TODO: THis should be fixed later + # 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" text: - System`InputForm: "\"\u03C0 is a trascendental number\"" + System`InputForm: '"\[Pi] 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" @@ -440,7 +442,8 @@ Graphics[{}]: "Grid[{{\"Spanish\", \"Hola!\"},{\"Portuguese\", \"Ol\xE0!\"},{\"English\", \"Hi!\"}}]": msg: Strings in a GridBox latex: - System`InputForm: \text{Grid[\{\{"Spanish", "Hola!"\}, \{"Portuguese", "Ol\`{a}!"\}, \{"English", "Hi!"\}\}]} + # TODO: This should be fixed later + # System`InputForm: \text{Grid[\{\{"Spanish", "Hola!"\}, \{"Portuguese", "Ol\`{a}!"\}, \{"English", "Hi!"\}\}]} System`OutputForm: '\text{Spanish Hola!\newline \newline @@ -458,12 +461,13 @@ 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!"}}]" + # TODO: This should be adjusted later + # 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!\"\ + System`InputForm: "Grid[{{\"Spanish\", \"Hola!\"}, {\"Portuguese\", \"Ol\\[AGrave]!\"\ }, {\"English\", \"Hi!\"}}]" System`OutputForm: "Spanish Hola!\n\nPortuguese Ol\xE0!\n\nEnglish \ \ Hi!\n" From 38e3a5d4a7f47f66886f0d95e10350b3f23b9d4b Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sun, 29 Mar 2026 10:42:28 -0300 Subject: [PATCH 03/11] Remove print statement from files.py --- mathics/builtin/files_io/files.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mathics/builtin/files_io/files.py b/mathics/builtin/files_io/files.py index 3407a0195..6cff9887a 100644 --- a/mathics/builtin/files_io/files.py +++ b/mathics/builtin/files_io/files.py @@ -327,7 +327,6 @@ def eval(self, path, evaluation: Evaluation, options: dict): result = result[:-1] for res in result: - print("show", String(res).value) evaluation.print_out(String(res)) return SymbolNull From 3c36644c98af308a3d822183bad6996064bcbf14 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sun, 29 Mar 2026 10:48:34 -0300 Subject: [PATCH 04/11] Update layout.py --- mathics/builtin/box/layout.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mathics/builtin/box/layout.py b/mathics/builtin/box/layout.py index 3aeda280f..806361de6 100644 --- a/mathics/builtin/box/layout.py +++ b/mathics/builtin/box/layout.py @@ -466,7 +466,7 @@ class ShowSpecialCharacters(Builtin): https://reference.wolfram.com/language/ref/ShowSpecialCharacters.html
'ShowSpecialCharacters' -
is an option for 'Style' and 'Cell' that directs whether non-ANSI characters must be shown as special characters or by escaped sequences. +
is an option for 'Style' and 'Cell' that directs whether non-ASCII characters must be shown as special characters or by escaped sequences.
""" - summary_text = "cell option directing whether show special characters in a reversible ANSI format." + summary_text = "cell option directing whether show special characters in a reversible ASCII format." class ShowStringCharacters(Builtin): From 36e89086e9d0c47fc45ac96a21fad42c5b088c95 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sun, 29 Mar 2026 10:49:32 -0300 Subject: [PATCH 05/11] Rename function to string_to_invertible_ascii Renamed function to reflect ASCII handling and updated docstring for clarity. --- mathics/core/convert/op.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mathics/core/convert/op.py b/mathics/core/convert/op.py index 1f5c8c9df..057f59366 100644 --- a/mathics/core/convert/op.py +++ b/mathics/core/convert/op.py @@ -84,9 +84,9 @@ } -def string_to_invertible_ansi(string: str): +def string_to_invertible_ascii(string: str): """ - Replace non-ANSI characters by their names. If the character + Replace non-ANSI characters with their names. If the character does not have a name, use the WMA hex character code form. Passing the string through `evaluation.parse` brings back the original string. From 25504f5384044407e93f50c8eefa75c48d8bfc74 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sun, 29 Mar 2026 10:50:23 -0300 Subject: [PATCH 06/11] Change import from string_to_invertible_ansi to ascii --- mathics/format/render/text.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mathics/format/render/text.py b/mathics/format/render/text.py index e3a917fe0..11ea8045a 100644 --- a/mathics/format/render/text.py +++ b/mathics/format/render/text.py @@ -20,7 +20,7 @@ TagBox, ) from mathics.core.atoms import String -from mathics.core.convert.op import string_to_invertible_ansi +from mathics.core.convert.op import string_to_invertible_ascii from mathics.core.exceptions import BoxConstructError from mathics.core.formatter import ( add_render_function, @@ -162,7 +162,7 @@ def string(s: String, **options) -> str: ) if not show_special_characters: - value = string_to_invertible_ansi(value) + value = string_to_invertible_ascii(value) if value.startswith('"') and value.endswith('"'): # nopep8 if not show_string_characters: From 848d87bd4af3f48bd0d512e7bef9296c91b436ca Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sun, 29 Mar 2026 11:27:59 -0300 Subject: [PATCH 07/11] Update layout.py --- mathics/builtin/box/layout.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mathics/builtin/box/layout.py b/mathics/builtin/box/layout.py index 806361de6..fb2e960ac 100644 --- a/mathics/builtin/box/layout.py +++ b/mathics/builtin/box/layout.py @@ -470,11 +470,11 @@ class ShowSpecialCharacters(Builtin):
    -
  • With 'ShowSpecialCharacters' is set to 'False', special characters are always displayed by name when possible. +
  • With 'ShowSpecialCharacters' set to 'False', special characters are always displayed by name when possible.
""" - summary_text = "cell option directing whether show special characters in a reversible ASCII format." + summary_text = "cell and style option directing whether show special characters in a reversible ASCII format." class ShowStringCharacters(Builtin): From bb738c6391e35139c6402a516be11473537df640 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sun, 29 Mar 2026 12:08:16 -0300 Subject: [PATCH 08/11] remove not strictly needed changes --- mathics/core/convert/op.py | 22 ---------------- mathics/eval/encoding.py | 53 -------------------------------------- 2 files changed, 75 deletions(-) delete mode 100644 mathics/eval/encoding.py diff --git a/mathics/core/convert/op.py b/mathics/core/convert/op.py index 057f59366..8e5689bba 100644 --- a/mathics/core/convert/op.py +++ b/mathics/core/convert/op.py @@ -34,28 +34,6 @@ val: operator_to_ascii[key] for key, val in operator_to_unicode.items() } - -# This dictionary is used for the default encoding from Unicode/UTF-8 to ASCII - -UNICODE_CHARACTER_TO_ASCII = CHARACTER_TO_NAME.copy() -UNICODE_CHARACTER_TO_ASCII.update( - { - ch: operator_to_ascii[name] - for name, ch in operator_to_unicode.items() - if name in operator_to_ascii - } -) -# These characters are used in encoding -# in WMA, and differs from what we have -# in Mathics3-scanner tables: -UNICODE_CHARACTER_TO_ASCII.update( - { - operator_to_unicode["Times"]: r" x ", - "": r"\[DifferentialD]", - } -) - - UNICODE_TO_AMSLATEX = NAMED_CHARACTERS_COLLECTION.get("unicode-to-amslatex", {}) UNICODE_TO_LATEX = NAMED_CHARACTERS_COLLECTION.get("unicode-to-latex", {}) diff --git a/mathics/eval/encoding.py b/mathics/eval/encoding.py deleted file mode 100644 index 8930a74d5..000000000 --- a/mathics/eval/encoding.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -Functions to format strings in a given encoding. -""" - -from typing import Dict - -from mathics.core.convert.op import UNICODE_CHARACTER_TO_ASCII - - -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 - # TODO: implement the load of .m encodings. - # Format: - # (*name*) - # {"7Bit"|"8Bit"|"16Bit",{{target_Integer,src_String}|{target_Integer, src_String, False},...}} - # - # If the encoding is not available, raise an EncodingError - try: - return { - "ASCII": UNICODE_CHARACTER_TO_ASCII, - "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 2dc1b947f3522208899fc9451541c8d4e29a64e4 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sun, 29 Mar 2026 12:20:03 -0300 Subject: [PATCH 09/11] adjust ShowStringCharacters in OutputForm --- mathics/format/form/outputform.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/mathics/format/form/outputform.py b/mathics/format/form/outputform.py index 86f4e411f..8f67b34ce 100644 --- a/mathics/format/form/outputform.py +++ b/mathics/format/form/outputform.py @@ -844,13 +844,21 @@ def _slotsequence_outputform_text(expr: Expression, evaluation: Evaluation, **kw def string_render_output_form(expr: String, evaluation: Evaluation, **kwargs) -> str: from mathics.format.render.text import string as render_string - # lines = expr.value.split("\n") - # max_len = max([len(line) for line in lines]) - # lines = [line + (max_len - len(line)) * " " for line in lines] - # return "\n".join(lines) - # value = expr.value - # return value - kwargs["System`ShowStringCharacters"] = SymbolTrue + # To render a string in OutputForm, we use the + # function that render strings from Boxed expressions. + # When a String object is converted into Boxes, + # MakeBoxes enclose the original string with quotes. + # Then, depending on the value of the option + # `System`ShowStringCharacters`, these quotes are render or not. + # If this option is set to `False`, the added quotes are removed. + # Here we are not going through that route: if + # `System`ShowStringCharacters` is set to True, add the quotes: + if kwargs.get("System`ShowStringCharacters", None) is SymbolTrue: + expr = String(f'"{expr.value}"') + else: + # if not, set "System`ShowStringCharacters" to True, + # to avoid remove quotes that was there before formatting: + kwargs["System`ShowStringCharacters"] = SymbolTrue return render_string(expr, **kwargs) From ade5665eed512a12d01cfae744ea60c13944a8e0 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sun, 29 Mar 2026 12:21:22 -0300 Subject: [PATCH 10/11] revert changes in mathice.core.evaluation --- mathics/core/evaluation.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/mathics/core/evaluation.py b/mathics/core/evaluation.py index ae8c162ba..e048c7418 100644 --- a/mathics/core/evaluation.py +++ b/mathics/core/evaluation.py @@ -419,15 +419,10 @@ 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 = "UTF-8" - try: # With the new implementation, if result is not a ``BoxExpression`` # then we should raise a BoxError here. - boxes = result.to_text(evaluation=self, encoding=encoding) + boxes = result.to_text(evaluation=self) except BoxError: self.message( "General", "notboxes", Expression(SymbolFullForm, result).evaluate(self) From 27bbf63452e31c4606a4c5d96a3f4a93b71a6aa6 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sun, 29 Mar 2026 14:32:33 -0300 Subject: [PATCH 11/11] update SYMBOLS_MANIFEST --- SYMBOLS_MANIFEST.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/SYMBOLS_MANIFEST.txt b/SYMBOLS_MANIFEST.txt index 7e9a9a8fd..98dd4c9af 100644 --- a/SYMBOLS_MANIFEST.txt +++ b/SYMBOLS_MANIFEST.txt @@ -1105,6 +1105,7 @@ System`ShortRightArrow System`ShortUpArrow System`Shortest System`Show +System`ShowSpecialCharacters System`ShowStringCharacters System`Sign System`Simplify