diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index fa02854d3..7aaed5a8c 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -28,10 +28,9 @@ jobs:
python -m pip install --upgrade pip
- name: Install Mathics3 with full Python dependencies
run: |
- python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
# We can comment out after next Mathics-Scanner release
# python -m pip install Mathics-Scanner[full]
- # python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
+ python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
pip install -e .
remake -x develop-full
- name: Test Mathics3
diff --git a/.github/workflows/plot-tests.yml b/.github/workflows/plot-tests.yml
index a7afa1da5..2dad1867e 100644
--- a/.github/workflows/plot-tests.yml
+++ b/.github/workflows/plot-tests.yml
@@ -25,9 +25,8 @@ jobs:
run: |
python -m pip install --upgrade pip
# We can comment out after next Mathics-Scanner release
- # python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
+ python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
git clone --depth 1 https://github.com/Mathics3/mathics-scanner.git
- # git clone --single-branch --branch operator-refactor-part1.5 https://github.com/Mathics3/mathics-scanner.git
cd mathics-scanner/
pip install -e .
cd ..
diff --git a/.github/workflows/pyodide.yml b/.github/workflows/pyodide.yml
index baa7834d1..dfea4b728 100644
--- a/.github/workflows/pyodide.yml
+++ b/.github/workflows/pyodide.yml
@@ -55,9 +55,7 @@ jobs:
pip install "setuptools>=70.0.0" PyYAML click packaging pytest
# We can comment out after next Mathics-Scanner release
- # python -m pip install --no-build-isolation -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner
- # pip install --no-build-isolation -e .
- # cd ..
+ python -m pip install --no-build-isolation -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner
pip install --no-build-isolation -e .
make mathics/data/op-tables.json mathics/data/operator-tables.json
diff --git a/.github/workflows/ubuntu-bench.yml b/.github/workflows/ubuntu-bench.yml
index 501629714..a958f7a02 100644
--- a/.github/workflows/ubuntu-bench.yml
+++ b/.github/workflows/ubuntu-bench.yml
@@ -26,8 +26,8 @@ jobs:
python -m pip install --upgrade pip
python -m pip install pytest-benchmark
# We can comment out after next Mathics-Scanner release
+ python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
# python -m pip install Mathics-Scanner[full]
- # python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
pip install -e .
remake -x develop
- name: Test Mathics
diff --git a/.github/workflows/ubuntu-cython.yml b/.github/workflows/ubuntu-cython.yml
index 453745833..17d83064a 100644
--- a/.github/workflows/ubuntu-cython.yml
+++ b/.github/workflows/ubuntu-cython.yml
@@ -25,7 +25,7 @@ jobs:
sudo apt-get update -qq && sudo apt-get install -qq liblapack-dev llvm-dev tesseract-ocr
python -m pip install --upgrade pip
# We can comment out after next Mathics-Scanner release
- # python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
+ python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
pip install -e .
cd ..
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 640f5e5db..da2a005bf 100755
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -34,7 +34,7 @@ jobs:
- name: Install Mathics3 with Python dependencies
run: |
# We can comment out after next Mathics-Scanner release
- # python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
+ python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
pip install -e .
# python -m pip install Mathics-Scanner[full]
diff --git a/mathics/builtin/forms/output.py b/mathics/builtin/forms/output.py
index b12eda19f..ca47b84f5 100644
--- a/mathics/builtin/forms/output.py
+++ b/mathics/builtin/forms/output.py
@@ -186,23 +186,48 @@ def eval_mathml(self, expr, evaluation) -> Expression:
class InputForm(FormBaseClass):
r"""
-
- :WMA link:
- https://reference.wolfram.com/language/ref/InputForm.html
+
+ :WMA link:
+ https://reference.wolfram.com/language/ref/InputForm.html
-
- - 'InputForm'[$expr$]
-
- displays $expr$ in an unambiguous form suitable for input.
-
+
+ - 'InputForm'[$expr$]
+
- displays $expr$ in an unambiguous form suitable for input to Mathics3.
+
- >> InputForm[a + b * c]
- = a + b*c
- >> InputForm["A string"]
- = "A string"
- >> InputForm[f'[x]]
- = Derivative[1][f][x]
- >> InputForm[Derivative[1, 0][f][x]]
- = Derivative[1, 0][f][x]
+ 'InputForm' produces one-dimensional output that is suitable for input to Mathics3:
+
+ >> InputForm["A string"]
+ = "A string"
+
+ >> InputForm[f'[x]]
+ = Derivative[1][f][x]
+
+ >> InputForm[Derivative[1, 0][f][x]]
+ = Derivative[1, 0][f][x]
+
+ 'InputForm' shows arithmetic expressions in traditional mathematical notation:
+
+ >> 2+F[x] // InputForm
+ = 2 + F[x]
+
+ Compare this to 'FullForm':
+
+ >> 2+F[x] // FullForm
+ = Plus[2, F[x]]
+
+ 'InputForm' output can be altered via 'Format' assignment :
+
+ >> Format[Foo[x], InputForm] := Bar
+
+ >> Foo[x] // InputForm
+ = Bar
+
+ In contrast, 'FullForm' output is not altered via 'Format' assignment :
+ >> Format[Foo[x], InputForm] := Baz
+
+ >> Foo[x] // FullForm
+ = Foo[x]
"""
in_outputforms = True
diff --git a/mathics/core/parser/operators.py b/mathics/core/parser/operators.py
index ab6e8814e..cc72da78a 100644
--- a/mathics/core/parser/operators.py
+++ b/mathics/core/parser/operators.py
@@ -35,6 +35,9 @@
nonassoc_binary_operators = OPERATOR_DATA["non-associative-binary-operators"]
operator_precedences = OPERATOR_DATA["operator-precedences"]
operator_to_amslatex = OPERATOR_DATA["operator-to-amslatex"]
+operator_to_string = OPERATOR_DATA[
+ "operator-to_string"
+] # FIXME: should be operator-to-string
postfix_operators = OPERATOR_DATA["postfix-operators"]
prefix_operators = OPERATOR_DATA["prefix-operators"]
right_binary_operators = OPERATOR_DATA["right-binary-operators"]
diff --git a/mathics/form/__init__.py b/mathics/form/__init__.py
index 6cf8f0856..1b37251f4 100644
--- a/mathics/form/__init__.py
+++ b/mathics/form/__init__.py
@@ -1,5 +1,5 @@
"""
-Function that generate $PrintForms
+Module containing functions for rendering $PrintForms Forms
"""
from mathics.form.inputform import render_input_form
diff --git a/mathics/form/inputform.py b/mathics/form/inputform.py
index 3c9951ce6..1ffc4f3c9 100644
--- a/mathics/form/inputform.py
+++ b/mathics/form/inputform.py
@@ -1,72 +1,37 @@
-"""
-This module builts the string associated to the InputForm.
-
-`InputForm` produces a textual output suitable for being parsed and directly
-evaluated in Mathics CLI. Differently from `FullForm`, `InputForm`
-show arithmetic expressions using Infix/Prefix/Postfix forms. Apart from that,
-the apareance of the result is almost the same that produce `FullForm`.
-
-On the other hand, internally, there are more differences. In the first place,
-InputForm always produces a single `String` object, while `FullForm` produces
-a nested `RowBox` structure.
-
-```
-In[1]:= 2+F[x] // FullForm // MakeBoxes // InputForm
-Out[1]//InputForm=
-TagBox[StyleBox[RowBox[{"Plus", "[", RowBox[{"2", ",", RowBox[{"F", "[", "x", "]"}]}], "]"}], ShowSpecialCharacters -> False, ShowStringCharacters -> True,
- NumberMarks -> True], FullForm]
-
-In[2]:= 2+F[x] // InputForm // MakeBoxes // InputForm
-Out[2]//InputForm= InterpretationBox[StyleBox["2 + F[x]", ShowStringCharacters -> True, NumberMarks -> True], InputForm[2 + F[x]], Editable -> True, AutoDelete -> True]
-```
-In the case of `FullForm`, we get a `TagBox`, which ensures the content to be interpreted as a `FullForm` boxed expression. In the case of the `InputForm`, we get an `InterpretationBox`, which keeps the information about the original expression. But the main difference is inside the `StyleBox`: for `InputForm` we have a shallow `String object, while for `FullForm` we have a nested `RowBox` expression.
-
-
-Another important difference between `FullForm` and `InputForm` is that `FullForm` does not take into account `FormatValues`, while `InputForm` does:
-
+"""This module contains functions for turning Mathics3 expressions to
+InputForm-formatted strings.
+`InputForm` produces textual output suitable for being parsed and
+evaluated in Mathics CLI.
-Differently from `FullForm`, which produces a nested `RowBox`
-expression, InputForm produces a single `String` (not boxed), which can be
-parsed and interpreted as an expression in the Mathics3 interpreter.
-```
-In[3]:= Format[F[x_],InputForm]:="-inputform formatted "<>ToString[x]<>"F-"
-In[4]:= Format[F[x_],FullForm]:="-fullform formatted "<>ToString[x]<>"F-"
+`InputForm` is not affected by MakeBox assignment.
-In[5]:= 3 F[r] //InputForm
-Out[5]//InputForm= 3*"-inputform formatted rF-"
+InputForm versus FullForm
+--------------------------
-In[6]:= 3 F[r] //FullForm
-Out[6]//FullForm= Times[3, F[r]]
-```
+In contrast to `FullForm`, `InputForm` shows arithmetic expressions in
+traditional mathematical notation. Apart from that and the allowance
+of `InputForm` output to be altered via a `Format` assignment, the
+appearance of the result is about the same as `FullForm`.
-On the other hand, neither `InputForm` or `FulLForm` do not take into accout MakeBoxes rules: setting
+Internally, `FullForm` produces `String` object, while `FullForm`
+produces a nested `RowBox` structure.
-```
-In[7]:= MakeBoxes[InputForm[G[x_]],f_]:=RowBox[{"--mb if G--", MakeBoxes[InputForm[x],f]}]
-In[8]:= MakeBoxes[FullForm[G[x_]],f_]:=RowBox[{"--mb ff G--", MakeBoxes[FullForm[x],f]}]
-```
-
-Evaluating `G[3 F[t]]` in any of these forms we get
-
-```
-In[9]:= G[3 F[t]]//InputForm
-Out[9]//InputForm= G[3*"-inputform formatted tF-"]
-
-In[10]:= G[3 F[t]]//FullForm
-Out[10]//FullForm= G[Times[3, F[t]]]
-```
-In the first case, the `FormatValue` rule for `F` is applied but not the `MakeBoxes` rules for `G`.
+`InputForm` conversion produces an `InterpretationBox` The
+`InterpetationBox` preserves information about the original
+expression. In contrast, `FullForm` output produces a `TagBox`. In
+both cases, underneath the `InterpretationBox` or `Tagbox` is a
+`StyleBox`.
"""
-from typing import Callable, Dict, List, Optional, Tuple
+from typing import Callable, Dict, Final, FrozenSet, List, Optional, Tuple
from mathics.core.atoms import Integer, String
from mathics.core.convert.op import operator_to_ascii, operator_to_unicode
from mathics.core.evaluation import Evaluation
from mathics.core.expression import Expression
-from mathics.core.parser.operators import OPERATOR_DATA
+from mathics.core.parser.operators import OPERATOR_DATA, operator_to_string
from mathics.core.symbols import Atom, Symbol
from mathics.core.systemsymbols import (
SymbolBlank,
@@ -87,11 +52,13 @@
SymbolPrefix = Symbol("System`Prefix")
-PRECEDENCES = OPERATOR_DATA.get("operator-precedences")
-PRECEDENCE_DEFAULT = PRECEDENCES.get("FunctionApply")
-PRECEDENCE_PLUS = PRECEDENCES.get("Plus")
-PRECEDENCE_TIMES = PRECEDENCES.get("Times")
-PRECEDENCE_POWER = PRECEDENCES.get("Power")
+# Use 670 until BoxGroup precedence gets in.
+PRECEDENCE_BOX_GROUP: Final[int] = 670 # box_operators["BoxGroup"]
+
+PRECEDENCES: Final = OPERATOR_DATA.get("operator-precedences")
+PRECEDENCE_PLUS: Final[int] = PRECEDENCES.get("Plus", 310)
+PRECEDENCE_TIMES: Final[int] = PRECEDENCES.get("Times", 400)
+PRECEDENCE_POWER: Final[int] = PRECEDENCES.get("Power", 590)
EXPR_TO_INPUTFORM_TEXT_MAP: Dict[str, Callable] = {}
@@ -119,12 +86,12 @@ def get_operator_str(head, evaluation, **kwargs) -> str:
def bracket(expr_str: str) -> str:
- """wrap with parenthesis"""
+ """Wrap `expr_str` with square braces"""
return f"[{expr_str}]"
def parenthesize(expr_str: str) -> str:
- """wrap with parenthesis"""
+ """Wrap `expr_str` with parenthesis"""
return f"({expr_str})"
@@ -233,24 +200,26 @@ def collect_in_pre_post_arguments(
head = expr.head
group = None
- precedence = PRECEDENCE_DEFAULT
+ precedence = PRECEDENCE_BOX_GROUP
operands = list(target.elements)
# Just one parameter:
if len(elements) == 1:
operator_spec = render_input_form(head, evaluation, **kwargs)
if head is SymbolInfix:
- operator_spec = [f"~{operator_spec}~"]
+ operator_spec = [
+ f"{operator_to_string['Infix']}{operator_spec}{operator_to_string['Infix']}"
+ ]
elif head is SymbolPrefix:
- operator_spec = f"{operator_spec}@"
+ operator_spec = f"{operator_spec}{operator_to_string['Prefix']}"
elif head is SymbolPostfix:
- operator_spec = f"//{operator_spec}"
+ operator_spec = f"{operator_to_string['Postfix']}{operator_spec}"
return operands, operator_spec, precedence, group
# At least two parameters: get the operator spec.
ops = elements[1]
if head is SymbolInfix:
- # This is not the WMA behaviour, but the Mathics current implementation requires it:
+ # This is not the WMA behaviour, but the Mathics3 current implementation requires it:
ops = ops.elements if ops.has_form("List", None) else (ops,)
operator_spec = [get_operator_str(op, evaluation, **kwargs) for op in ops]
else:
@@ -274,6 +243,17 @@ def collect_in_pre_post_arguments(
return operands, operator_spec, precedence, group
+ARITHMETIC_OPERATOR_STRINGS: Final[FrozenSet[str]] = frozenset(
+ [
+ *operator_to_string["Divide"],
+ *operator_to_string["NonCommutativeMultiply"],
+ *operator_to_string["Power"],
+ *operator_to_string["Times"],
+ " ",
+ ]
+)
+
+
@register_inputform("System`Infix")
def _infix_expression_to_inputform_text(
expr: Expression, evaluation: Evaluation, **kwargs
@@ -313,7 +293,7 @@ def _infix_expression_to_inputform_text(
else:
num_ops = len(ops_lst)
curr_op = ops_lst[index % num_ops]
- if curr_op not in ("*", "**", "/", "^", " "):
+ if curr_op not in ARITHMETIC_OPERATOR_STRINGS:
# In the tests, we add spaces just for + and -:
curr_op = f" {curr_op} "
diff --git a/test/format/format_tests-WMA.yaml b/test/format/format_tests-WMA.yaml
index 26eb83b7e..f0e35a50e 100644
--- a/test/format/format_tests-WMA.yaml
+++ b/test/format/format_tests-WMA.yaml
@@ -1,7 +1,7 @@
# Tests for formatting according WMA.
#
# These tests are prepared to compare the output obtained using WL code in
-# both WMA and Mathics interpreters:
+# both WMA and Mathics3 interpreters:
# ./convert_yaml2json.py format_tests.yaml && $(WMAINTERPRETER) -f format_tests.m
#
# In this case, 'text' are certificated against the WMA result,
@@ -12,7 +12,7 @@
# Running the same code against `mathics` CLI, most of these tests fails because
# - `ToString` with `CharacterEncoding->"ASCII"` does not produce string
# representations of Boxes.
-# - `OutputForm` in Mathics does not produce *pretty-print-like* output.
+# - `OutputForm` in Mathics3 does not produce *pretty-print-like* output.
# And eventually, if we start to do it, probably we can also do it better,
# so we are not going to coincide neither.
# - Box constructions are still too diferent in both interpreters.
diff --git a/test/format/format_tests.m b/test/format/format_tests.m
index de0e3f962..44f184cf7 100755
--- a/test/format/format_tests.m
+++ b/test/format/format_tests.m
@@ -1,7 +1,7 @@
(**************************************************************************************
Run the format tests in WMA.
-Notice that some of the tests that produce meaninful outputs in Mathics,
+Notice that some of the tests that produce meaninful outputs in Mathics3,
fails miserably to produce an output in WMA. Also, the results of these tests
in the Notebook interface, the CLI (math) and wolframscript are not fully consistent.
@@ -29,9 +29,9 @@
Do[form = ToExpression[subtest[[1]]]; expr = form[key];
result = ToString[expr, CharacterEncoding->"ASCII"];
expected = subtest[[2]];
- If[result != expected,
- Print[" * ", FullForm[expr], " //", form, "(text) [Failed]\n result:", "<<" <> result <> ">>(", StringLength[result],
- ")\n expected: ", "<<" <> expected <> ">> (", StringLength[expected],")\n"],
+ If[result != expected,
+ Print[" * ", FullForm[expr], " //", form, "(text) [Failed]\n result:", "<<" <> result <> ">>(", StringLength[result],
+ ")\n expected: ", "<<" <> expected <> ">> (", StringLength[expected],")\n"],
Print[" * ", FullForm[expr], " //", form, "(text) [OK]"]];,
{subtest, text}]
];
@@ -41,23 +41,23 @@
Do[form = ToExpression[subtest[[1]]]; expr = form[key];
result = ToString[expr, TeXForm, CharacterEncoding->"ASCII"];
expected = subtest[[2]];
- If[result != subtest[[2]],
- Print[" * ", key, " //", form, "(latex) [Failed]\n result:",
- "<<" <> result <> ">>", "\n expected: ",
- "<<" <> expected <> ">>\n"],
+ If[result != subtest[[2]],
+ Print[" * ", key, " //", form, "(latex) [Failed]\n result:",
+ "<<" <> result <> ">>", "\n expected: ",
+ "<<" <> expected <> ">>\n"],
Print[" * ", key, " //", form, "(latex) [OK]"]];,
{subtest, latex}]
];
(*MathML*)
- If[And[ISMATHICSINTERPRETER, Head[mathml]===List],
+ If[And[ISMATHICSINTERPRETER, Head[mathml]===List],
Print[" mathml", "\n ------", "\n"];
Do[form = ToExpression[subtest[[1]]]; expr = form[key];
result = ToString[expr, MathMLForm, CharacterEncoding->"ASCII"];
expected = subtest[[2]];
- If[result != subtest[[2]],
- Print[" * ", key, " //", form, "(mathml) [Failed]\n result:",
- "<<" <> result <> ">>", "\n expected: ",
- "<<" <> expected <> ">>\n"],
+ If[result != subtest[[2]],
+ Print[" * ", key, " //", form, "(mathml) [Failed]\n result:",
+ "<<" <> result <> ">>", "\n expected: ",
+ "<<" <> expected <> ">>\n"],
Print[" * ", key, " //", form, "(mathml) [OK]"]];, {subtest, mathml}]];
, {tests, data}
]
diff --git a/test/format/test_format.py b/test/format/test_format.py
index 588bc978a..ba469dafc 100644
--- a/test/format/test_format.py
+++ b/test/format/test_format.py
@@ -15,7 +15,7 @@
# In these tests, we check that the current behavior of makeboxes does not change
# without noticing that it could affect compatibility with WL and with
# mathics-django. Also looking at some issues in the current behavior regarding
-# the WL standard (for instance, how to represent $a^(b/c)$) and the Mathics
+# the WL standard (for instance, how to represent $a^(b/c)$) and the Mathics3
# own implementation (BoxError raising in some simple conditions).
# These test should be updated as we fix pending issues.