diff --git a/mathics/builtin/__init__.py b/mathics/builtin/__init__.py
index ac9d77517..f7e9b0a61 100755
--- a/mathics/builtin/__init__.py
+++ b/mathics/builtin/__init__.py
@@ -218,6 +218,7 @@ def sanity_check(cls, module):
"drawing",
"fileformats",
"files_io",
+ "forms",
"functional",
"intfns",
"list",
diff --git a/mathics/builtin/atomic/strings.py b/mathics/builtin/atomic/strings.py
index 2d3346b9b..4e3182e09 100644
--- a/mathics/builtin/atomic/strings.py
+++ b/mathics/builtin/atomic/strings.py
@@ -31,8 +31,8 @@
from mathics.core.convert.op import ascii_op_to_unicode
from mathics.core.convert.python import from_bool
from mathics.core.element import BaseElement
-from mathics.core.formatter import format_element
from mathics.core.list import ListExpression
+from mathics.core.makeboxes import format_element
from mathics.core.parser import MathicsFileLineFeeder, parse
from mathics.core.symbols import (
Symbol,
diff --git a/mathics/builtin/box/graphics.py b/mathics/builtin/box/graphics.py
index 09187e48f..a1d263974 100644
--- a/mathics/builtin/box/graphics.py
+++ b/mathics/builtin/box/graphics.py
@@ -41,8 +41,9 @@
from mathics.core.attributes import A_HOLD_ALL, A_PROTECTED, A_READ_PROTECTED
from mathics.core.expression import Expression
-from mathics.core.formatter import format_element, lookup_method
+from mathics.core.formatter import lookup_method
from mathics.core.list import ListExpression
+from mathics.core.makeboxes import format_element
from mathics.core.symbols import Symbol, SymbolTrue
from mathics.core.systemsymbols import SymbolAutomatic, SymbolTraditionalForm
diff --git a/mathics/builtin/box/layout.py b/mathics/builtin/box/layout.py
index 5d4f9dba2..f9e4a7401 100644
--- a/mathics/builtin/box/layout.py
+++ b/mathics/builtin/box/layout.py
@@ -21,13 +21,15 @@
from mathics.core.expression import Expression
from mathics.core.list import ListExpression
from mathics.core.symbols import Symbol, SymbolMakeBoxes
-from mathics.core.systemsymbols import SymbolRowBox, SymbolStandardForm
-
-SymbolFractionBox = Symbol("System`FractionBox")
-SymbolSubscriptBox = Symbol("System`SubscriptBox")
-SymbolSubsuperscriptBox = Symbol("System`SubsuperscriptBox")
-SymbolSuperscriptBox = Symbol("System`SuperscriptBox")
-SymbolSqrtBox = Symbol("System`SqrtBox")
+from mathics.core.systemsymbols import (
+ SymbolFractionBox,
+ SymbolRowBox,
+ SymbolSqrtBox,
+ SymbolStandardForm,
+ SymbolSubsuperscriptBox,
+ SymbolSubscriptBox,
+ SymbolSuperscriptBox,
+)
# this temporarily replaces the _BoxedString class
@@ -165,8 +167,7 @@ class InterpretationBox(BoxExpression):
"""
- 'InterpretationBox[{...}, expr]'
-
- is a low-level box construct that displays as
- boxes but is interpreted on input as expr.
+
- is a low-level box construct that displays as boxes, but is interpreted on input as expr.
>> A = InterpretationBox["Pepe", 4]
diff --git a/mathics/builtin/files_io/files.py b/mathics/builtin/files_io/files.py
index e989eba9f..a1f5d8650 100644
--- a/mathics/builtin/files_io/files.py
+++ b/mathics/builtin/files_io/files.py
@@ -29,7 +29,7 @@
from mathics.core.convert.expression import to_expression, to_mathics_list
from mathics.core.convert.python import from_python
from mathics.core.expression import BoxError, Expression
-from mathics.core.formatter import format_element, do_format
+from mathics.core.makeboxes import format_element, do_format
from mathics.core.parser import MathicsFileLineFeeder, parse
from mathics.core.read import (
channel_to_stream,
diff --git a/mathics/builtin/forms.py b/mathics/builtin/forms.py
deleted file mode 100644
index 5098daedf..000000000
--- a/mathics/builtin/forms.py
+++ /dev/null
@@ -1,643 +0,0 @@
-"""
-Forms of Input and Output
-
-A Form format specifies the way Mathics Expression input is read or output written.
-
-The variable :$OutputForms':
-http://localhost:8000/doc/reference-of-built-in-symbols/forms-of-input-and-output/$outputforms/ has a list of Forms defined.
-
-See also :WMA link:
-https://reference.wolfram.com/language/tutorial/TextualInputAndOutput.html#12368.
-"""
-
-import re
-
-from typing import Optional
-
-
-from mathics.builtin.base import (
- Builtin,
- Predefined,
-)
-from mathics.builtin.box.layout import GridBox, RowBox, to_boxes
-from mathics.builtin.comparison import expr_min
-from mathics.builtin.makeboxes import MakeBoxes
-from mathics.builtin.tensors import get_dimensions
-
-from mathics.core.atoms import Integer, String, StringFromPython
-from mathics.core.attributes import A_LOCKED, A_PROTECTED
-
-from mathics.core.element import EvalMixin
-from mathics.core.expression import Expression, BoxError
-from mathics.core.formatter import format_element
-from mathics.core.list import ListExpression
-from mathics.core.symbols import (
- Symbol,
- SymbolFullForm,
- SymbolList,
-)
-
-from mathics.core.systemsymbols import (
- SymbolRowBox,
-)
-
-import mathics.core.definitions as definitions
-
-
-MULTI_NEWLINE_RE = re.compile(r"\n{2,}")
-
-SymbolNumberForm = Symbol("System`NumberForm")
-SymbolSuperscriptBox = Symbol("System`SuperscriptBox")
-SymbolTableDepth = Symbol("TableDepth")
-
-
-class FormBaseClass(Builtin):
- """
- Base class for a Mathics Form.
-
- All Forms should subclass this.
- """
-
- # Using "__new__" is not optimal for what we want.
- # We basically want to hook into class construction in order to
- # detect certain class attributes so we can add them to a list.
- # __new__ has this feature. However we do not really need (or want)
- # to do the memory allocation aspect that "__new__" is intended for.
- # We considered __prepare__ and metaclass, instead but could not figure
- # out how to get that to work.
- def __new__(cls, *args, **kwargs):
- """ """
- instance = super().__new__(cls, expression=False)
- name = cls.__name__
-
- if hasattr(cls, "in_printforms") and cls.in_printforms:
- definitions.PrintForms.add(Symbol(name))
- if hasattr(cls, "in_outputforms") and cls.in_outputforms:
- if name in definitions.OutputForms:
- raise RuntimeError(f"{name} already added to $OutputsForms")
- definitions.OutputForms.add(Symbol(name))
- return instance
-
-
-class FullForm(FormBaseClass):
- """
-
- :WMA link:
- https://reference.wolfram.com/language/ref/FullForm.html
-
-
- - 'FullForm[$expr$]'
-
- displays the underlying form of $expr$.
-
-
- >> FullForm[a + b * c]
- = Plus[a, Times[b, c]]
- >> FullForm[2/3]
- = Rational[2, 3]
- >> FullForm["A string"]
- = "A string"
- """
-
- in_outputforms = True
- in_printforms = True
- summary_text = "underlying M-Expression representation"
-
-
-class MathMLForm(FormBaseClass):
- """
-
- :WMA link:
- https://reference.wolfram.com/language/ref/MathMLForm.html
-
-
- - 'MathMLForm[$expr$]'
-
- displays $expr$ as a MathML expression.
-
-
- >> MathMLForm[HoldForm[Sqrt[a^3]]]
- = ...
-
- ## Test cases for Unicode - redo please as a real test
- >> MathMLForm[\\[Mu]]
- = ...
-
- # This can causes the TeX to fail
- # >> MathMLForm[Graphics[Text["\u03bc"]]]
- # = ...
-
- ## The should contain U+2062 INVISIBLE TIMES
- ## MathMLForm[MatrixForm[{{2*a, 0},{0,0}}]]
- = ...
- """
-
- in_outputforms = True
- in_printforms = True
-
- summary_text = "formatted expression as MathML commands"
-
- def eval_mathml(self, expr, evaluation) -> Expression:
- "MakeBoxes[expr_, MathMLForm]"
-
- boxes = MakeBoxes(expr).evaluate(evaluation)
- try:
- mathml = boxes.boxes_to_mathml(evaluation=evaluation)
- except BoxError:
- evaluation.message(
- "General",
- "notboxes",
- Expression(SymbolFullForm, boxes).evaluate(evaluation),
- )
- mathml = ""
- is_a_picture = mathml[:6] == "
- :WMA link:
- https://reference.wolfram.com/language/ref/InputForm.html
-
-
- - 'InputForm[$expr$]'
-
- displays $expr$ in an unambiguous form suitable for input.
-
-
- >> 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[2 x ^ 2 + 4z!]
- = 2*x^2 + 4*z!
- #> InputForm["\$"]
- = "\\$"
- """
-
- in_outputforms = True
- in_printforms = True
- summary_text = "plain-text input format"
-
-
-class OutputForm(FormBaseClass):
- """
-
- :WMA link:
- https://reference.wolfram.com/language/ref/OutputForm.html
-
-
- - 'OutputForm[$expr$]'
-
- displays $expr$ in a plain-text form.
-
-
- >> OutputForm[f'[x]]
- = f'[x]
- >> OutputForm[Derivative[1, 0][f][x]]
- = Derivative[1, 0][f][x]
- >> OutputForm["A string"]
- = A string
- >> OutputForm[Graphics[Rectangle[]]]
- = -Graphics-
- """
-
- summary_text = "plain-text output format"
-
-
-class OutputForms_(Predefined):
- r"""
-
- - '$OutputForms'
-
- contains the list of all output forms. It is updated automatically when new 'OutputForms' are defined by setting format values.
-
-
- >> $OutputForms
- = ...
- """
-
- attributes = A_LOCKED | A_PROTECTED
- name = "$OutputForms"
- summary_text = "list all output forms"
-
- def evaluate(self, evaluation):
- return ListExpression(*evaluation.definitions.outputforms)
-
-
-class PrintForms_(Predefined):
- r"""
-
- - '$PrintForms'
-
- contains the list of basic print forms. It is updated automatically when new 'PrintForms' are defined by setting format values.
-
-
- >> $PrintForms
- = ...
-
- Suppose now that we want to add a new format 'MyForm'. Initially, it does not belong to '$PrintForms':
- >> MemberQ[$PrintForms, MyForm]
- = False
- Now, let's define a format rule:
- >> Format[MyForm[F[x_]]]:= "F<<" <> ToString[x] <> ">>"
- >> Format[F[x_], MyForm]:= MyForm[F[x]]
- Now, the new format belongs to the '$PrintForms' list
- >> MemberQ[$PrintForms, MyForm]
- = True
-
- """
-
- attributes = A_LOCKED | A_PROTECTED
- name = "$PrintForms"
- summary_text = "list common print forms"
-
- def evaluate(self, evaluation):
- return ListExpression(*evaluation.definitions.printforms)
-
-
-class PythonForm(FormBaseClass):
- """
-
- - 'PythonForm[$expr$]'
-
- returns an approximate equivalent of $expr$ in Python, when that is possible. We assume
- that Python has SymPy imported. No explicit import will be include in the result.
-
-
- >> PythonForm[Infinity]
- = math.inf
- >> PythonForm[Pi]
- = sympy.pi
- >> E // PythonForm
- = sympy.E
- >> {1, 2, 3} // PythonForm
- = [1, 2, 3]
- """
-
- in_outputforms = True
- in_printforms = True
- summary_text = "translate expressions as Python source code"
- # >> PythonForm[HoldForm[Sqrt[a^3]]]
- # = sympy.sqrt{a**3} # or something like this
-
- def eval_python(self, expr, evaluation) -> Expression:
- "MakeBoxes[expr_, PythonForm]"
-
- def build_python_form(expr):
- if isinstance(expr, Symbol):
- return expr.to_sympy()
- return expr.to_python()
-
- try:
- python_equivalent = build_python_form(expr)
- except Exception:
- return
- return StringFromPython(python_equivalent)
-
- def eval(self, expr, evaluation) -> Expression:
- "PythonForm[expr_]"
- return self.eval_python(expr, evaluation)
-
-
-class SympyForm(FormBaseClass):
- """
-
- - 'SympyForm[$expr$]'
-
- returns an Sympy $expr$ in Python. Sympy is used internally
- to implement a number of Mathics functions, like Simplify.
-
-
- >> SympyForm[Pi^2]
- = pi**2
- >> E^2 + 3E // SympyForm
- = exp(2) + 3*E
- """
-
- in_outputforms = True
- in_printforms = True
- summary_text = "translate expressions to SymPy"
-
- def eval_sympy(self, expr, evaluation) -> Optional[Expression]:
- "MakeBoxes[expr_, SympyForm]"
-
- try:
- sympy_equivalent = expr.to_sympy()
- except Exception:
- return
- return StringFromPython(sympy_equivalent)
-
- def eval(self, expr, evaluation) -> Expression:
- "SympyForm[expr_]"
- return self.eval_sympy(expr, evaluation)
-
-
-class StringForm(FormBaseClass):
- """
-
- :WMA link:
- https://reference.wolfram.com/language/ref/StringForm.html
-
-
- - 'StringForm[$str$, $expr1$, $expr2$, ...]'
-
- displays the string $str$, replacing placeholders in $str$
- with the corresponding expressions.
-
-
- >> StringForm["`1` bla `2` blub `` bla `2`", a, b, c]
- = a bla b blub c bla b
- """
-
- in_outputforms = False
- in_printforms = False
- summary_text = "make an string from a template and a list of parameters"
-
- def eval_makeboxes(self, s, args, f, evaluation):
- """MakeBoxes[StringForm[s_String, args___],
- f:StandardForm|TraditionalForm|OutputForm]"""
-
- s = s.value
- args = args.get_sequence()
- result = []
- pos = 0
- last_index = 0
- for match in re.finditer(r"(\`(\d*)\`)", s):
- start, end = match.span(1)
- if match.group(2):
- index = int(match.group(2))
- else:
- index = last_index + 1
- if index > last_index:
- last_index = index
- if start > pos:
- result.append(to_boxes(String(s[pos:start]), evaluation))
- pos = end
- if 1 <= index <= len(args):
- arg = args[index - 1]
- result.append(
- to_boxes(MakeBoxes(arg, f).evaluate(evaluation), evaluation)
- )
- if pos < len(s):
- result.append(to_boxes(String(s[pos:]), evaluation))
- return RowBox(
- *tuple(
- r.evaluate(evaluation) if isinstance(r, EvalMixin) else r
- for r in result
- )
- )
-
-
-class StandardForm(FormBaseClass):
- """
-
- :WMA link:
- https://reference.wolfram.com/language/ref/StandardForm.html
-
-
- - 'StandardForm[$expr$]'
-
- displays $expr$ in the default form.
-
-
- >> StandardForm[a + b * c]
- = a + b c
- >> StandardForm["A string"]
- = A string
- 'StandardForm' is used by default:
- >> "A string"
- = A string
- >> f'[x]
- = f'[x]
- """
-
- in_outputforms = True
- in_printforms = True
- summary_text = "default output format"
-
-
-class TraditionalForm(FormBaseClass):
- """
-
- :WMA link:
- https://reference.wolfram.com/language/ref/TraditionalForm.html
-
-
- - 'TraditionalForm[$expr$]'
-
- displays $expr$ in a format similar to the traditional mathematical notation, where
- function evaluations are represented by brackets instead of square brackets.
-
-
- ## To pass this test, we need to improve the implementation of Element.format
- ## >> TraditionalForm[g[x]]
- ## = g(x)
- """
-
- in_outputforms = True
- in_printforms = True
-
- summary_text = "traditional output format"
-
-
-class TeXForm(FormBaseClass):
- r"""
-
- :WMA link:
- https://reference.wolfram.com/language/ref/TeXForm.html
-
-
- - 'TeXForm[$expr$]'
-
- displays $expr$ using TeX math mode commands.
-
-
- >> TeXForm[HoldForm[Sqrt[a^3]]]
- = \sqrt{a^3}
-
- #> {"hi","you"} //InputForm //TeXForm
- = \left\{\text{``hi''}, \text{``you''}\right\}
-
- #> TeXForm[a+b*c]
- = a+b c
- #> TeXForm[InputForm[a+b*c]]
- = a\text{ + }b*c
- """
-
- in_outputforms = True
- in_printforms = True
- summary_text = "formatted expression as TeX commands"
-
- def eval_tex(self, expr, evaluation) -> Expression:
- "MakeBoxes[expr_, TeXForm]"
- boxes = MakeBoxes(expr).evaluate(evaluation)
- try:
- # Here we set ``show_string_characters`` to False, to reproduce
- # the standard behaviour in WMA. Remove this parameter to recover the
- # quotes in InputForm and FullForm
- tex = boxes.boxes_to_tex(
- show_string_characters=False, evaluation=evaluation
- )
-
- # Replace multiple newlines by a single one e.g. between asy-blocks
- tex = MULTI_NEWLINE_RE.sub("\n", tex)
-
- tex = tex.replace(" \uF74c", " \\, d") # tmp hack for Integrate
- except BoxError:
- evaluation.message(
- "General",
- "notboxes",
- Expression(SymbolFullForm, boxes).evaluate(evaluation),
- )
- tex = ""
- return Expression(SymbolRowBox, ListExpression(String(tex)))
-
-
-class TableForm(FormBaseClass):
- """
-
- :WMA link:
- https://reference.wolfram.com/language/ref/TableForm.html
-
-
- - 'TableForm[$expr$]'
-
- displays $expr$ as a table.
-
-
- >> TableForm[Array[a, {3,2}],TableDepth->1]
- = {a[1, 1], a[1, 2]}
- .
- . {a[2, 1], a[2, 2]}
- .
- . {a[3, 1], a[3, 2]}
-
- A table of Graphics:
- >> Table[Style[Graphics[{EdgeForm[{Black}], RGBColor[r,g,b], Rectangle[]}], ImageSizeMultipliers->{0.2, 1}], {r,0,1,1/2}, {g,0,1,1/2}, {b,0,1,1/2}] // TableForm
- = -Graphics- -Graphics- -Graphics-
- .
- . -Graphics- -Graphics- -Graphics-
- .
- . -Graphics- -Graphics- -Graphics-
- .
- . -Graphics- -Graphics- -Graphics-
- .
- . -Graphics- -Graphics- -Graphics-
- .
- . -Graphics- -Graphics- -Graphics-
- .
- . -Graphics- -Graphics- -Graphics-
- .
- . -Graphics- -Graphics- -Graphics-
- .
- . -Graphics- -Graphics- -Graphics-
-
- #> TableForm[{}]
- = #<--#
- """
-
- in_outputforms = True
- in_printforms = False
- options = {"TableDepth": "Infinity"}
- summary_text = "format as a table"
-
- def eval_makeboxes(self, table, f, evaluation, options):
- """MakeBoxes[%(name)s[table_, OptionsPattern[%(name)s]],
- f:StandardForm|TraditionalForm|OutputForm]"""
- dims = len(get_dimensions(table, head=SymbolList))
- depth = self.get_option(options, "TableDepth", evaluation, pop=True)
- options["System`TableDepth"] = depth
- depth = expr_min((Integer(dims), depth))
- depth = depth.value
- if depth is None:
- evaluation.message(self.get_name(), "int")
- return
-
- if depth <= 0:
- return format_element(table, evaluation, f)
- elif depth == 1:
- return GridBox(
- ListExpression(
- *(
- ListExpression(format_element(item, evaluation, f))
- for item in table.elements
- ),
- )
- )
- # return Expression(
- # 'GridBox', Expression('List', *(
- # Expression('List', Expression('MakeBoxes', item, f))
- # for item in table.elements)))
- else:
- options["System`TableDepth"] = Integer(depth - 2)
-
- def transform_item(item):
- if depth > 2:
- return self.eval_makeboxes(item, f, evaluation, options)
- else:
- return format_element(item, evaluation, f)
-
- result = GridBox(
- ListExpression(
- *(
- ListExpression(
- *(transform_item(item) for item in row.elements),
- )
- for row in table.elements
- ),
- )
- )
- options["System`TableDepth"] = Integer(depth)
- return result
-
-
-class MatrixForm(TableForm):
- """
-
- :WMA link:
- https://reference.wolfram.com/language/ref/MatrixForm.html
-
-
- - 'MatrixForm[$m$]'
-
- displays a matrix $m$, hiding the underlying list structure.
-
-
- >> Array[a,{4,3}]//MatrixForm
- = a[1, 1] a[1, 2] a[1, 3]
- .
- . a[2, 1] a[2, 2] a[2, 3]
- .
- . a[3, 1] a[3, 2] a[3, 3]
- .
- . a[4, 1] a[4, 2] a[4, 3]
-
- ## Issue #182
- #> {{2*a, 0},{0,0}}//MatrixForm
- = 2 a 0
- .
- . 0 0
- """
-
- in_outputforms = True
- in_printforms = False
- summary_text = "format as a matrix"
-
- def eval_makeboxes_matrix(self, table, f, evaluation, options):
- """MakeBoxes[%(name)s[table_, OptionsPattern[%(name)s]],
- f:StandardForm|TraditionalForm]"""
-
- result = super(MatrixForm, self).eval_makeboxes(table, f, evaluation, options)
- if result.get_head_name() == "System`GridBox":
- return RowBox(String("("), result, String(")"))
-
- return result
-
-
-# FormBaseClass is a public Builtin class that
-# should not get added as a definition (and therefore not added to
-# to external documentation.
-
-DOES_NOT_ADD_BUILTIN_DEFINITION = [FormBaseClass]
diff --git a/mathics/builtin/forms/__init__.py b/mathics/builtin/forms/__init__.py
new file mode 100644
index 000000000..d1b0ff25c
--- /dev/null
+++ b/mathics/builtin/forms/__init__.py
@@ -0,0 +1,11 @@
+"""
+Forms of Input and Output
+
+A Form format specifies the way Mathics Expression input is read or output written.
+
+The variable :$OutputForms':
+http://localhost:8000/doc/reference-of-built-in-symbols/forms-of-input-and-output/form-variables/$outputforms/ has a list of Forms defined.
+
+See also :WMA link:
+https://reference.wolfram.com/language/tutorial/TextualInputAndOutput.html#12368.
+"""
diff --git a/mathics/builtin/forms/base.py b/mathics/builtin/forms/base.py
new file mode 100644
index 000000000..f73a542d8
--- /dev/null
+++ b/mathics/builtin/forms/base.py
@@ -0,0 +1,45 @@
+import mathics.core.definitions as definitions
+
+from mathics.builtin.base import Builtin
+from mathics.core.symbols import Symbol
+
+form_symbol_to_class = {}
+
+
+class FormBaseClass(Builtin):
+ """
+ Base class for a Mathics Form.
+
+ All Forms should subclass this.
+ """
+
+ # Mapping from Form and element type to a callable boxing method
+ form_box_methods = {}
+
+ # Using "__new__" is not optimal for what we want.
+ # We basically want to hook into class construction in order to
+ # detect certain class attributes so we can add them to a list.
+ # __new__ has this feature. However we do not really need (or want)
+ # to do the memory allocation aspect that "__new__" is intended for.
+ # We considered __prepare__ and metaclass, instead but could not figure
+ # out how to get that to work.
+ def __new__(cls, *args, **kwargs):
+ """ """
+ instance = super().__new__(cls, expression=False)
+ name = cls.__name__
+
+ if hasattr(cls, "in_printforms") and cls.in_printforms:
+ definitions.PrintForms.add(Symbol(name))
+ if hasattr(cls, "in_outputforms") and cls.in_outputforms:
+ if name in definitions.OutputForms:
+ raise RuntimeError(f"{name} already added to $OutputsForms")
+ definitions.OutputForms.add(Symbol(name))
+ form_symbol_to_class[Symbol(name)] = cls
+ return instance
+
+
+# FormBaseClass is a public Builtin class that
+# should not get added as a definition (and therefore not added to
+# to external documentation.
+
+DOES_NOT_ADD_BUILTIN_DEFINITION = [FormBaseClass]
diff --git a/mathics/builtin/forms/other.py b/mathics/builtin/forms/other.py
new file mode 100644
index 000000000..eef201e6b
--- /dev/null
+++ b/mathics/builtin/forms/other.py
@@ -0,0 +1,67 @@
+"""
+Forms which are not in '$OutputForms'
+"""
+
+import re
+
+from mathics.builtin.box.layout import RowBox, to_boxes
+from mathics.builtin.forms.base import FormBaseClass
+from mathics.builtin.makeboxes import MakeBoxes
+from mathics.core.element import EvalMixin
+
+from mathics.core.atoms import String
+
+
+class StringForm(FormBaseClass):
+ """
+
+ :WMA link:
+ https://reference.wolfram.com/language/ref/StringForm.html
+
+
+ - 'StringForm[$str$, $expr1$, $expr2$, ...]'
+
- displays the string $str$, replacing placeholders in $str$
+ with the corresponding expressions.
+
+
+ >> StringForm["`1` bla `2` blub `` bla `2`", a, b, c]
+ = a bla b blub c bla b
+ """
+
+ in_outputforms = False
+ in_printforms = False
+ summary_text = "make an string from a template and a list of parameters"
+
+ def eval_makeboxes(self, s, args, f, evaluation):
+ """MakeBoxes[StringForm[s_String, args___],
+ f:StandardForm|TraditionalForm|OutputForm]"""
+
+ s = s.value
+ args = args.get_sequence()
+ result = []
+ pos = 0
+ last_index = 0
+ for match in re.finditer(r"(\`(\d*)\`)", s):
+ start, end = match.span(1)
+ if match.group(2):
+ index = int(match.group(2))
+ else:
+ index = last_index + 1
+ if index > last_index:
+ last_index = index
+ if start > pos:
+ result.append(to_boxes(String(s[pos:start]), evaluation))
+ pos = end
+ if 1 <= index <= len(args):
+ arg = args[index - 1]
+ result.append(
+ to_boxes(MakeBoxes(arg, f).evaluate(evaluation), evaluation)
+ )
+ if pos < len(s):
+ result.append(to_boxes(String(s[pos:]), evaluation))
+ return RowBox(
+ *tuple(
+ r.evaluate(evaluation) if isinstance(r, EvalMixin) else r
+ for r in result
+ )
+ )
diff --git a/mathics/builtin/forms/output.py b/mathics/builtin/forms/output.py
new file mode 100644
index 000000000..63d27b6ef
--- /dev/null
+++ b/mathics/builtin/forms/output.py
@@ -0,0 +1,1078 @@
+# FIXME: split these forms up further.
+# MathML and TeXForm feel more closely related since they go with specific kinds of interpreters:
+# LaTeX and MathML
+
+# SympyForm and PythonForm feel related since are our own hacky thing (and mostly broken for now)
+
+# NumberForm, TableForm, and MatrixForm seem closely related since they seem to be relevant
+# for particular kinds of structures rather than applicable to all kinds of expressions.
+
+"""
+Forms which appear in '$OutputForms'.
+"""
+import re
+
+from typing import Optional
+
+from mathics.builtin.base import Builtin
+from mathics.builtin.box.layout import GridBox, RowBox, to_boxes
+from mathics.builtin.comparison import expr_min
+from mathics.builtin.forms.base import FormBaseClass
+from mathics.builtin.makeboxes import MakeBoxes, number_form
+from mathics.builtin.tensors import get_dimensions
+
+from mathics.core.atoms import (
+ Integer,
+ MachineReal,
+ PrecisionReal,
+ Real,
+ String,
+ StringFromPython,
+)
+
+from mathics.core.expression import Expression, BoxError
+from mathics.core.list import ListExpression
+from mathics.core.makeboxes import format_element
+from mathics.core.number import (
+ convert_base,
+ dps,
+ machine_precision,
+ reconstruct_digits,
+)
+
+from mathics.core.symbols import (
+ Symbol,
+ SymbolFullForm,
+ SymbolList,
+ SymbolFalse,
+ SymbolNull,
+ SymbolTrue,
+)
+
+from mathics.core.systemsymbols import (
+ SymbolAutomatic,
+ SymbolInfinity,
+ SymbolMakeBoxes,
+ SymbolNumberForm,
+ SymbolOutputForm,
+ SymbolRowBox,
+ SymbolRuleDelayed,
+ SymbolSubscriptBox,
+ SymbolSuperscriptBox,
+)
+
+MULTI_NEWLINE_RE = re.compile(r"\n{2,}")
+
+
+class BaseForm(Builtin):
+ """
+
+ - 'BaseForm[$expr$, $n$]'
+
- prints numbers in $expr$ in base $n$.
+
+
+ >> BaseForm[33, 2]
+ = 100001_2
+
+ >> BaseForm[234, 16]
+ = ea_16
+
+ >> BaseForm[12.3, 2]
+ = 1100.01001100110011001_2
+
+ >> BaseForm[-42, 16]
+ = -2a_16
+
+ >> BaseForm[x, 2]
+ = x
+
+ >> BaseForm[12, 3] // FullForm
+ = BaseForm[12, 3]
+
+ Bases must be between 2 and 36:
+ >> BaseForm[12, -3]
+ : Positive machine-sized integer expected at position 2 in BaseForm[12, -3].
+ = BaseForm[12, -3]
+ >> BaseForm[12, 100]
+ : Requested base 100 must be between 2 and 36.
+ = BaseForm[12, 100]
+
+ #> BaseForm[0, 2]
+ = 0_2
+ #> BaseForm[0.0, 2]
+ = 0.0_2
+
+ #> BaseForm[N[Pi, 30], 16]
+ = 3.243f6a8885a308d313198a2e_16
+ """
+
+ summary_text = "print with all numbers given in a base"
+ messages = {
+ "intpm": (
+ "Positive machine-sized integer expected at position 2 in "
+ "BaseForm[`1`, `2`]."
+ ),
+ "basf": "Requested base `1` must be between 2 and 36.",
+ }
+
+ def apply_makeboxes(self, expr, n, f, evaluation):
+ """MakeBoxes[BaseForm[expr_, n_],
+ f:StandardForm|TraditionalForm|OutputForm]"""
+
+ base = n.get_int_value()
+ if base <= 0:
+ evaluation.message("BaseForm", "intpm", expr, n)
+ return None
+
+ if isinstance(expr, PrecisionReal):
+ x = expr.to_sympy()
+ p = reconstruct_digits(expr.get_precision())
+ elif isinstance(expr, MachineReal):
+ x = expr.value
+ p = reconstruct_digits(machine_precision)
+ elif isinstance(expr, Integer):
+ x = expr.value
+ p = 0
+ else:
+ return to_boxes(Expression(SymbolMakeBoxes, expr, f), evaluation)
+
+ try:
+ val = convert_base(x, base, p)
+ except ValueError:
+ return evaluation.message("BaseForm", "basf", n)
+
+ if f is SymbolOutputForm:
+ return to_boxes(String("%s_%d" % (val, base)), evaluation)
+ else:
+ return to_boxes(
+ Expression(SymbolSubscriptBox, String(val), String(base)), evaluation
+ )
+
+
+class FullForm(FormBaseClass):
+ """
+
+ :WMA link:
+ https://reference.wolfram.com/language/ref/FullForm.html
+
+
+ - 'FullForm[$expr$]'
+
- displays the underlying form of $expr$.
+
+
+ >> FullForm[a + b * c]
+ = Plus[a, Times[b, c]]
+ >> FullForm[2/3]
+ = Rational[2, 3]
+ >> FullForm["A string"]
+ = "A string"
+ """
+
+ in_outputforms = True
+ in_printforms = True
+ summary_text = "underlying M-Expression representation"
+
+
+class MathMLForm(FormBaseClass):
+ """
+
+ :WMA link:
+ https://reference.wolfram.com/language/ref/MathMLForm.html
+
+
+ - 'MathMLForm[$expr$]'
+
- displays $expr$ as a MathML expression.
+
+
+ >> MathMLForm[HoldForm[Sqrt[a^3]]]
+ = ...
+
+ ## Test cases for Unicode - redo please as a real test
+ >> MathMLForm[\\[Mu]]
+ = ...
+
+ # This can causes the TeX to fail
+ # >> MathMLForm[Graphics[Text["\u03bc"]]]
+ # = ...
+
+ ## The should contain U+2062 INVISIBLE TIMES
+ ## MathMLForm[MatrixForm[{{2*a, 0},{0,0}}]]
+ = ...
+ """
+
+ in_outputforms = True
+ in_printforms = True
+
+ summary_text = "formatted expression as MathML commands"
+
+ def eval_mathml(self, expr, evaluation) -> Expression:
+ "MakeBoxes[expr_, MathMLForm]"
+
+ boxes = MakeBoxes(expr).evaluate(evaluation)
+ try:
+ mathml = boxes.boxes_to_mathml(evaluation=evaluation)
+ except BoxError:
+ evaluation.message(
+ "General",
+ "notboxes",
+ Expression(SymbolFullForm, boxes).evaluate(evaluation),
+ )
+ mathml = ""
+ is_a_picture = mathml[:6] == "
+ :WMA link:
+ https://reference.wolfram.com/language/ref/InputForm.html
+
+
+ - 'InputForm[$expr$]'
+
- displays $expr$ in an unambiguous form suitable for input.
+
+
+ >> 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[2 x ^ 2 + 4z!]
+ = 2*x^2 + 4*z!
+ #> InputForm["\$"]
+ = "\\$"
+ """
+
+ in_outputforms = True
+ in_printforms = True
+ summary_text = "plain-text input format"
+
+
+class _NumberForm(Builtin):
+ """
+ Base class for NumberForm, AccountingForm, EngineeringForm, and ScientificForm.
+ """
+
+ default_ExponentFunction = None
+ default_NumberFormat = None
+
+ messages = {
+ "npad": "Value for option NumberPadding -> `1` should be a string or a pair of strings.",
+ "dblk": "Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.",
+ "npt": "Value for option `1` -> `2` is expected to be a string.",
+ "nsgn": "Value for option NumberSigns -> `1` should be a pair of strings or two pairs of strings.",
+ "nspr": "Value for option NumberSeparator -> `1` should be a string or a pair of strings.",
+ "opttf": "Value of option `1` -> `2` should be True or False.",
+ "estep": "Value of option `1` -> `2` is not a positive integer.",
+ "iprf": "Formatting specification `1` should be a positive integer or a pair of positive integers.", # NumberFormat only
+ "sigz": "In addition to the number of digits requested, one or more zeros will appear as placeholders.",
+ }
+
+ def check_options(self, options, evaluation):
+ """
+ Checks options are valid and converts them to python.
+ """
+ result = {}
+ for option_name in self.options:
+ method = getattr(self, "check_" + option_name)
+ arg = options["System`" + option_name]
+ value = method(arg, evaluation)
+ if value is None:
+ return None
+ result[option_name] = value
+ return result
+
+ def check_DigitBlock(self, value, evaluation):
+ py_value = value.get_int_value()
+ if value.sameQ(SymbolInfinity):
+ return [0, 0]
+ elif py_value is not None and py_value > 0:
+ return [py_value, py_value]
+ elif value.has_form("List", 2):
+ nleft, nright = value.elements
+ py_left, py_right = nleft.get_int_value(), nright.get_int_value()
+ if nleft.sameQ(SymbolInfinity):
+ nleft = 0
+ elif py_left is not None and py_left > 0:
+ nleft = py_left
+ else:
+ nleft = None
+ if nright.sameQ(SymbolInfinity):
+ nright = 0
+ elif py_right is not None and py_right > 0:
+ nright = py_right
+ else:
+ nright = None
+ result = [nleft, nright]
+ if None not in result:
+ return result
+ return evaluation.message(self.get_name(), "dblk", value)
+
+ def check_ExponentFunction(self, value, evaluation):
+ if value.sameQ(SymbolAutomatic):
+ return self.default_ExponentFunction
+
+ def exp_function(x):
+ return Expression(value, x).evaluate(evaluation)
+
+ return exp_function
+
+ def check_NumberFormat(self, value, evaluation):
+ if value.sameQ(SymbolAutomatic):
+ return self.default_NumberFormat
+
+ def num_function(man, base, exp, options):
+ return Expression(value, man, base, exp).evaluate(evaluation)
+
+ return num_function
+
+ def check_NumberMultiplier(self, value, evaluation):
+ result = value.get_string_value()
+ if result is None:
+ evaluation.message(self.get_name(), "npt", "NumberMultiplier", value)
+ return result
+
+ def check_NumberPoint(self, value, evaluation):
+ result = value.get_string_value()
+ if result is None:
+ evaluation.message(self.get_name(), "npt", "NumberPoint", value)
+ return result
+
+ def check_ExponentStep(self, value, evaluation):
+ result = value.get_int_value()
+ if result is None or result <= 0:
+ return evaluation.message(self.get_name(), "estep", "ExponentStep", value)
+ return result
+
+ def check_SignPadding(self, value, evaluation):
+ if value.sameQ(SymbolTrue):
+ return True
+ elif value.sameQ(SymbolFalse):
+ return False
+ return evaluation.message(self.get_name(), "opttf", value)
+
+ def _check_List2str(self, value, msg, evaluation):
+ if value.has_form("List", 2):
+ result = [element.get_string_value() for element in value.elements]
+ if None not in result:
+ return result
+ return evaluation.message(self.get_name(), msg, value)
+
+ def check_NumberSigns(self, value, evaluation):
+ return self._check_List2str(value, "nsgn", evaluation)
+
+ def check_NumberPadding(self, value, evaluation):
+ return self._check_List2str(value, "npad", evaluation)
+
+ def check_NumberSeparator(self, value, evaluation):
+ py_str = value.get_string_value()
+ if py_str is not None:
+ return [py_str, py_str]
+ return self._check_List2str(value, "nspr", evaluation)
+
+
+class NumberForm(_NumberForm):
+ """
+
+ - 'NumberForm[$expr$, $n$]'
+
- prints a real number $expr$ with $n$-digits of precision.
+
+
- 'NumberForm[$expr$, {$n$, $f$}]'
+
- prints with $n$-digits and $f$ digits to the right of the decimal point.
+
+
+ >> NumberForm[N[Pi], 10]
+ = 3.141592654
+
+ >> NumberForm[N[Pi], {10, 5}]
+ = 3.14159
+
+
+ ## Undocumented edge cases
+ #> NumberForm[Pi, 20]
+ = Pi
+ #> NumberForm[2/3, 10]
+ = 2 / 3
+
+ ## No n or f
+ #> NumberForm[N[Pi]]
+ = 3.14159
+ #> NumberForm[N[Pi, 20]]
+ = 3.1415926535897932385
+ #> NumberForm[14310983091809]
+ = 14310983091809
+
+ ## Zero case
+ #> z0 = 0.0;
+ #> z1 = 0.0000000000000000000000000000;
+ #> NumberForm[{z0, z1}, 10]
+ = {0., 0.×10^-28}
+ #> NumberForm[{z0, z1}, {10, 4}]
+ = {0.0000, 0.0000×10^-28}
+
+ ## Trailing zeros
+ #> NumberForm[1.0, 10]
+ = 1.
+ #> NumberForm[1.000000000000000000000000, 10]
+ = 1.000000000
+ #> NumberForm[1.0, {10, 8}]
+ = 1.00000000
+ #> NumberForm[N[Pi, 33], 33]
+ = 3.14159265358979323846264338327950
+
+ ## Correct rounding - see sympy/issues/11472
+ #> NumberForm[0.645658509, 6]
+ = 0.645659
+ #> NumberForm[N[1/7], 30]
+ = 0.1428571428571428
+
+ ## Integer case
+ #> NumberForm[{0, 2, -415, 83515161451}, 5]
+ = {0, 2, -415, 83515161451}
+ #> NumberForm[{2^123, 2^123.}, 4, ExponentFunction -> ((#1) &)]
+ = {10633823966279326983230456482242756608, 1.063×10^37}
+ #> NumberForm[{0, 10, -512}, {10, 3}]
+ = {0.000, 10.000, -512.000}
+
+ ## Check arguments
+ #> NumberForm[1.5, -4]
+ : Formatting specification -4 should be a positive integer or a pair of positive integers.
+ = 1.5
+ #> NumberForm[1.5, {1.5, 2}]
+ : Formatting specification {1.5, 2} should be a positive integer or a pair of positive integers.
+ = 1.5
+ #> NumberForm[1.5, {1, 2.5}]
+ : Formatting specification {1, 2.5} should be a positive integer or a pair of positive integers.
+ = 1.5
+
+ ## Right padding
+ #> NumberForm[153., 2]
+ : In addition to the number of digits requested, one or more zeros will appear as placeholders.
+ = 150.
+ #> NumberForm[0.00125, 1]
+ = 0.001
+ #> NumberForm[10^5 N[Pi], {5, 3}]
+ : In addition to the number of digits requested, one or more zeros will appear as placeholders.
+ = 314160.000
+ #> NumberForm[10^5 N[Pi], {6, 3}]
+ = 314159.000
+ #> NumberForm[10^5 N[Pi], {6, 10}]
+ = 314159.0000000000
+ #> NumberForm[1.0000000000000000000, 10, NumberPadding -> {"X", "Y"}]
+ = X1.000000000
+
+ ## Check options
+
+ ## DigitBlock
+ #> NumberForm[12345.123456789, 14, DigitBlock -> 3]
+ = 12,345.123 456 789
+ #> NumberForm[12345.12345678, 14, DigitBlock -> 3]
+ = 12,345.123 456 78
+ #> NumberForm[N[10^ 5 Pi], 15, DigitBlock -> {4, 2}]
+ = 31,4159.26 53 58 97 9
+ #> NumberForm[1.2345, 3, DigitBlock -> -4]
+ : Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.
+ = 1.2345
+ #> NumberForm[1.2345, 3, DigitBlock -> x]
+ : Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.
+ = 1.2345
+ #> NumberForm[1.2345, 3, DigitBlock -> {x, 3}]
+ : Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.
+ = 1.2345
+ #> NumberForm[1.2345, 3, DigitBlock -> {5, -3}]
+ : Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.
+ = 1.2345
+
+ ## ExponentFunction
+ #> NumberForm[12345.123456789, 14, ExponentFunction -> ((#) &)]
+ = 1.2345123456789×10^4
+ #> NumberForm[12345.123456789, 14, ExponentFunction -> (Null&)]
+ = 12345.123456789
+ #> y = N[Pi^Range[-20, 40, 15]];
+ #> NumberForm[y, 10, ExponentFunction -> (3 Quotient[#, 3] &)]
+ = {114.0256472×10^-12, 3.267763643×10^-3, 93.64804748×10^3, 2.683779414×10^12, 76.91214221×10^18}
+ #> NumberForm[y, 10, ExponentFunction -> (Null &)]
+ : In addition to the number of digits requested, one or more zeros will appear as placeholders.
+ : In addition to the number of digits requested, one or more zeros will appear as placeholders.
+ = {0.0000000001140256472, 0.003267763643, 93648.04748, 2683779414000., 76912142210000000000.}
+
+ ## ExponentStep
+ #> NumberForm[10^8 N[Pi], 10, ExponentStep -> 3]
+ = 314.1592654×10^6
+ #> NumberForm[1.2345, 3, ExponentStep -> x]
+ : Value of option ExponentStep -> x is not a positive integer.
+ = 1.2345
+ #> NumberForm[1.2345, 3, ExponentStep -> 0]
+ : Value of option ExponentStep -> 0 is not a positive integer.
+ = 1.2345
+ #> NumberForm[y, 10, ExponentStep -> 6]
+ = {114.0256472×10^-12, 3267.763643×10^-6, 93648.04748, 2.683779414×10^12, 76.91214221×10^18}
+
+ ## NumberFormat
+ #> NumberForm[y, 10, NumberFormat -> (#1 &)]
+ = {1.140256472, 0.003267763643, 93648.04748, 2.683779414, 7.691214221}
+
+ ## NumberMultiplier
+ #> NumberForm[1.2345, 3, NumberMultiplier -> 0]
+ : Value for option NumberMultiplier -> 0 is expected to be a string.
+ = 1.2345
+ #> NumberForm[N[10^ 7 Pi], 15, NumberMultiplier -> "*"]
+ = 3.14159265358979*10^7
+
+ ## NumberPoint
+ #> NumberForm[1.2345, 5, NumberPoint -> ","]
+ = 1,2345
+ #> NumberForm[1.2345, 3, NumberPoint -> 0]
+ : Value for option NumberPoint -> 0 is expected to be a string.
+ = 1.2345
+
+ ## NumberPadding
+ #> NumberForm[1.41, {10, 5}]
+ = 1.41000
+ #> NumberForm[1.41, {10, 5}, NumberPadding -> {"", "X"}]
+ = 1.41XXX
+ #> NumberForm[1.41, {10, 5}, NumberPadding -> {"X", "Y"}]
+ = XXXXX1.41YYY
+ #> NumberForm[1.41, 10, NumberPadding -> {"X", "Y"}]
+ = XXXXXXXX1.41
+ #> NumberForm[1.2345, 3, NumberPadding -> 0]
+ : Value for option NumberPadding -> 0 should be a string or a pair of strings.
+ = 1.2345
+ #> NumberForm[1.41, 10, NumberPadding -> {"X", "Y"}, NumberSigns -> {"-------------", ""}]
+ = XXXXXXXXXXXXXXXXXXXX1.41
+ #> NumberForm[{1., -1., 2.5, -2.5}, {4, 6}, NumberPadding->{"X", "Y"}]
+ = {X1.YYYYYY, -1.YYYYYY, X2.5YYYYY, -2.5YYYYY}
+
+ ## NumberSeparator
+ #> NumberForm[N[10^ 5 Pi], 15, DigitBlock -> 3, NumberSeparator -> " "]
+ = 314 159.265 358 979
+ #> NumberForm[N[10^ 5 Pi], 15, DigitBlock -> 3, NumberSeparator -> {" ", ","}]
+ = 314 159.265,358,979
+ #> NumberForm[N[10^ 5 Pi], 15, DigitBlock -> 3, NumberSeparator -> {",", " "}]
+ = 314,159.265 358 979
+ #> NumberForm[N[10^ 7 Pi], 15, DigitBlock -> 3, NumberSeparator -> {",", " "}]
+ = 3.141 592 653 589 79×10^7
+ #> NumberForm[1.2345, 3, NumberSeparator -> 0]
+ : Value for option NumberSeparator -> 0 should be a string or a pair of strings.
+ = 1.2345
+
+ ## NumberSigns
+ #> NumberForm[1.2345, 5, NumberSigns -> {"-", "+"}]
+ = +1.2345
+ #> NumberForm[-1.2345, 5, NumberSigns -> {"- ", ""}]
+ = - 1.2345
+ #> NumberForm[1.2345, 3, NumberSigns -> 0]
+ : Value for option NumberSigns -> 0 should be a pair of strings or two pairs of strings.
+ = 1.2345
+
+ ## SignPadding
+ #> NumberForm[1.234, 6, SignPadding -> True, NumberPadding -> {"X", "Y"}]
+ = XXX1.234
+ #> NumberForm[-1.234, 6, SignPadding -> True, NumberPadding -> {"X", "Y"}]
+ = -XX1.234
+ #> NumberForm[-1.234, 6, SignPadding -> False, NumberPadding -> {"X", "Y"}]
+ = XX-1.234
+ #> NumberForm[-1.234, {6, 4}, SignPadding -> False, NumberPadding -> {"X", "Y"}]
+ = X-1.234Y
+
+ ## 1-arg, Option case
+ #> NumberForm[34, ExponentFunction->(Null&)]
+ = 34
+
+ ## zero padding integer x0.0 case
+ #> NumberForm[50.0, {5, 1}]
+ = 50.0
+ #> NumberForm[50, {5, 1}]
+ = 50.0
+
+ ## Rounding correctly
+ #> NumberForm[43.157, {10, 1}]
+ = 43.2
+ #> NumberForm[43.15752525, {10, 5}, NumberSeparator -> ",", DigitBlock -> 1]
+ = 4,3.1,5,7,5,3
+ #> NumberForm[80.96, {16, 1}]
+ = 81.0
+ #> NumberForm[142.25, {10, 1}]
+ = 142.3
+ """
+
+ options = {
+ "DigitBlock": "Infinity",
+ "ExponentFunction": "Automatic",
+ "ExponentStep": "1",
+ "NumberFormat": "Automatic",
+ "NumberMultiplier": '"×"',
+ "NumberPadding": '{"", "0"}',
+ "NumberPoint": '"."',
+ "NumberSeparator": '{",", " "}',
+ "NumberSigns": '{"-", ""}',
+ "SignPadding": "False",
+ }
+ summary_text = "print at most a number of digits of all approximate real numbers in the expression"
+
+ @staticmethod
+ def default_ExponentFunction(value):
+ n = value.get_int_value()
+ if -5 <= n <= 5:
+ return SymbolNull
+ else:
+ return value
+
+ @staticmethod
+ def default_NumberFormat(man, base, exp, options):
+ py_exp = exp.get_string_value()
+ if py_exp:
+ mul = String(options["NumberMultiplier"])
+ return Expression(
+ SymbolRowBox,
+ ListExpression(man, mul, Expression(SymbolSuperscriptBox, base, exp)),
+ )
+ else:
+ return man
+
+ def apply_list_n(self, expr, n, evaluation, options) -> Expression:
+ "NumberForm[expr_List, n_, OptionsPattern[NumberForm]]"
+ options = [
+ Expression(SymbolRuleDelayed, Symbol(key), value)
+ for key, value in options.items()
+ ]
+ return ListExpression(
+ *[
+ Expression(SymbolNumberForm, element, n, *options)
+ for element in expr.elements
+ ]
+ )
+
+ def apply_list_nf(self, expr, n, f, evaluation, options) -> Expression:
+ "NumberForm[expr_List, {n_, f_}, OptionsPattern[NumberForm]]"
+ options = [
+ Expression(SymbolRuleDelayed, Symbol(key), value)
+ for key, value in options.items()
+ ]
+ return ListExpression(
+ *[
+ Expression(SymbolNumberForm, element, ListExpression(n, f), *options)
+ for element in expr.elements
+ ],
+ )
+
+ def apply_makeboxes(self, expr, form, evaluation, options={}):
+ """MakeBoxes[NumberForm[expr_, OptionsPattern[NumberForm]],
+ form:StandardForm|TraditionalForm|OutputForm]"""
+
+ fallback = Expression(SymbolMakeBoxes, expr, form)
+
+ py_options = self.check_options(options, evaluation)
+ if py_options is None:
+ return fallback
+
+ if isinstance(expr, Integer):
+ py_n = len(str(abs(expr.get_int_value())))
+ elif isinstance(expr, Real):
+ if expr.is_machine_precision():
+ py_n = 6
+ else:
+ py_n = dps(expr.get_precision())
+ else:
+ py_n = None
+
+ if py_n is not None:
+ py_options["_Form"] = form.get_name()
+ return number_form(expr, py_n, None, evaluation, py_options)
+ return Expression(SymbolMakeBoxes, expr, form)
+
+ def apply_makeboxes_n(self, expr, n, form, evaluation, options={}):
+ """MakeBoxes[NumberForm[expr_, n_?NotOptionQ, OptionsPattern[NumberForm]],
+ form:StandardForm|TraditionalForm|OutputForm]"""
+
+ fallback = Expression(SymbolMakeBoxes, expr, form)
+
+ py_n = n.get_int_value()
+ if py_n is None or py_n <= 0:
+ evaluation.message("NumberForm", "iprf", n)
+ return fallback
+
+ py_options = self.check_options(options, evaluation)
+ if py_options is None:
+ return fallback
+
+ if isinstance(expr, (Integer, Real)):
+ py_options["_Form"] = form.get_name()
+ return number_form(expr, py_n, None, evaluation, py_options)
+ return Expression(SymbolMakeBoxes, expr, form)
+
+ def apply_makeboxes_nf(self, expr, n, f, form, evaluation, options={}):
+ """MakeBoxes[NumberForm[expr_, {n_, f_}, OptionsPattern[NumberForm]],
+ form:StandardForm|TraditionalForm|OutputForm]"""
+
+ fallback = Expression(SymbolMakeBoxes, expr, form)
+
+ nf = ListExpression(n, f)
+ py_n = n.get_int_value()
+ py_f = f.get_int_value()
+ if py_n is None or py_n <= 0 or py_f is None or py_f < 0:
+ evaluation.message("NumberForm", "iprf", nf)
+ return fallback
+
+ py_options = self.check_options(options, evaluation)
+ if py_options is None:
+ return fallback
+
+ if isinstance(expr, (Integer, Real)):
+ py_options["_Form"] = form.get_name()
+ return number_form(expr, py_n, py_f, evaluation, py_options)
+ return Expression(SymbolMakeBoxes, expr, form)
+
+
+class OutputForm(FormBaseClass):
+ """
+
+ :WMA link:
+ https://reference.wolfram.com/language/ref/OutputForm.html
+
+
+ - 'OutputForm[$expr$]'
+
- displays $expr$ in a plain-text form.
+
+
+ >> OutputForm[f'[x]]
+ = f'[x]
+ >> OutputForm[Derivative[1, 0][f][x]]
+ = Derivative[1, 0][f][x]
+ >> OutputForm["A string"]
+ = A string
+ >> OutputForm[Graphics[Rectangle[]]]
+ = -Graphics-
+ """
+
+ summary_text = "plain-text output format"
+
+
+class PythonForm(FormBaseClass):
+ """
+
+ - 'PythonForm[$expr$]'
+
- returns an approximate equivalent of $expr$ in Python, when that is possible. We assume
+ that Python has SymPy imported. No explicit import will be include in the result.
+
+
+ >> PythonForm[Infinity]
+ = math.inf
+ >> PythonForm[Pi]
+ = sympy.pi
+ >> E // PythonForm
+ = sympy.E
+ >> {1, 2, 3} // PythonForm
+ = [1, 2, 3]
+ """
+
+ in_outputforms = True
+ in_printforms = True
+ summary_text = "translate expressions as Python source code"
+ # >> PythonForm[HoldForm[Sqrt[a^3]]]
+ # = sympy.sqrt{a**3} # or something like this
+
+ def eval_python(self, expr, evaluation) -> Expression:
+ "MakeBoxes[expr_, PythonForm]"
+
+ def build_python_form(expr):
+ if isinstance(expr, Symbol):
+ return expr.to_sympy()
+ return expr.to_python()
+
+ try:
+ python_equivalent = build_python_form(expr)
+ except Exception:
+ return
+ return StringFromPython(python_equivalent)
+
+ def eval(self, expr, evaluation) -> Expression:
+ "PythonForm[expr_]"
+ return self.eval_python(expr, evaluation)
+
+
+class SympyForm(FormBaseClass):
+ """
+
+ - 'SympyForm[$expr$]'
+
- returns an Sympy $expr$ in Python. Sympy is used internally
+ to implement a number of Mathics functions, like Simplify.
+
+
+ >> SympyForm[Pi^2]
+ = pi**2
+ >> E^2 + 3E // SympyForm
+ = exp(2) + 3*E
+ """
+
+ in_outputforms = True
+ in_printforms = True
+ summary_text = "translate expressions to SymPy"
+
+ def eval_sympy(self, expr, evaluation) -> Optional[Expression]:
+ "MakeBoxes[expr_, SympyForm]"
+
+ try:
+ sympy_equivalent = expr.to_sympy()
+ except Exception:
+ return
+ return StringFromPython(sympy_equivalent)
+
+ def eval(self, expr, evaluation) -> Expression:
+ "SympyForm[expr_]"
+ return self.eval_sympy(expr, evaluation)
+
+
+class StandardForm(FormBaseClass):
+ """
+
+ :WMA link:
+ https://reference.wolfram.com/language/ref/StandardForm.html
+
+
+ - 'StandardForm[$expr$]'
+
- displays $expr$ in the default form.
+
+
+ >> StandardForm[a + b * c]
+ = a + b c
+ >> StandardForm["A string"]
+ = A string
+ 'StandardForm' is used by default:
+ >> "A string"
+ = A string
+ >> f'[x]
+ = f'[x]
+ """
+
+ in_outputforms = True
+ in_printforms = True
+ summary_text = "default output format"
+
+
+class TraditionalForm(FormBaseClass):
+ """
+
+ :WMA link:
+ https://reference.wolfram.com/language/ref/TraditionalForm.html
+
+
+ - 'TraditionalForm[$expr$]'
+
- displays $expr$ in a format similar to the traditional mathematical notation, where
+ function evaluations are represented by brackets instead of square brackets.
+
+
+ ## To pass this test, we need to improve the implementation of Element.format
+ ## >> TraditionalForm[g[x]]
+ ## = g(x)
+ """
+
+ in_outputforms = True
+ in_printforms = True
+
+ summary_text = "traditional output format"
+
+
+class TeXForm(FormBaseClass):
+ r"""
+
+ :WMA link:
+ https://reference.wolfram.com/language/ref/TeXForm.html
+
+
+ - 'TeXForm[$expr$]'
+
- displays $expr$ using TeX math mode commands.
+
+
+ >> TeXForm[HoldForm[Sqrt[a^3]]]
+ = \sqrt{a^3}
+
+ #> {"hi","you"} //InputForm //TeXForm
+ = \left\{\text{``hi''}, \text{``you''}\right\}
+
+ #> TeXForm[a+b*c]
+ = a+b c
+ #> TeXForm[InputForm[a+b*c]]
+ = a\text{ + }b*c
+ """
+
+ in_outputforms = True
+ in_printforms = True
+ summary_text = "formatted expression as TeX commands"
+
+ def eval_tex(self, expr, evaluation) -> Expression:
+ "MakeBoxes[expr_, TeXForm]"
+ boxes = MakeBoxes(expr).evaluate(evaluation)
+ try:
+ # Here we set ``show_string_characters`` to False, to reproduce
+ # the standard behaviour in WMA. Remove this parameter to recover the
+ # quotes in InputForm and FullForm
+ tex = boxes.boxes_to_tex(
+ show_string_characters=False, evaluation=evaluation
+ )
+
+ # Replace multiple newlines by a single one e.g. between asy-blocks
+ tex = MULTI_NEWLINE_RE.sub("\n", tex)
+
+ tex = tex.replace(" \uF74c", " \\, d") # tmp hack for Integrate
+ except BoxError:
+ evaluation.message(
+ "General",
+ "notboxes",
+ Expression(SymbolFullForm, boxes).evaluate(evaluation),
+ )
+ tex = ""
+ return Expression(SymbolRowBox, ListExpression(String(tex)))
+
+
+class TableForm(FormBaseClass):
+ """
+
+ :WMA link:
+ https://reference.wolfram.com/language/ref/TableForm.html
+
+
+ - 'TableForm[$expr$]'
+
- displays $expr$ as a table.
+
+
+ >> TableForm[Array[a, {3,2}],TableDepth->1]
+ = {a[1, 1], a[1, 2]}
+ .
+ . {a[2, 1], a[2, 2]}
+ .
+ . {a[3, 1], a[3, 2]}
+
+ A table of Graphics:
+ >> Table[Style[Graphics[{EdgeForm[{Black}], RGBColor[r,g,b], Rectangle[]}], ImageSizeMultipliers->{0.2, 1}], {r,0,1,1/2}, {g,0,1,1/2}, {b,0,1,1/2}] // TableForm
+ = -Graphics- -Graphics- -Graphics-
+ .
+ . -Graphics- -Graphics- -Graphics-
+ .
+ . -Graphics- -Graphics- -Graphics-
+ .
+ . -Graphics- -Graphics- -Graphics-
+ .
+ . -Graphics- -Graphics- -Graphics-
+ .
+ . -Graphics- -Graphics- -Graphics-
+ .
+ . -Graphics- -Graphics- -Graphics-
+ .
+ . -Graphics- -Graphics- -Graphics-
+ .
+ . -Graphics- -Graphics- -Graphics-
+
+ #> TableForm[{}]
+ = #<--#
+ """
+
+ in_outputforms = True
+ in_printforms = False
+ options = {"TableDepth": "Infinity"}
+ summary_text = "format as a table"
+
+ def eval_makeboxes(self, table, f, evaluation, options):
+ """MakeBoxes[%(name)s[table_, OptionsPattern[%(name)s]],
+ f:StandardForm|TraditionalForm|OutputForm]"""
+ dims = len(get_dimensions(table, head=SymbolList))
+ depth = self.get_option(options, "TableDepth", evaluation, pop=True)
+ options["System`TableDepth"] = depth
+ depth = expr_min((Integer(dims), depth))
+ depth = depth.value
+ if depth is None:
+ evaluation.message(self.get_name(), "int")
+ return
+
+ if depth <= 0:
+ return format_element(table, evaluation, f)
+ elif depth == 1:
+ return GridBox(
+ ListExpression(
+ *(
+ ListExpression(format_element(item, evaluation, f))
+ for item in table.elements
+ ),
+ )
+ )
+ # return Expression(
+ # 'GridBox', Expression('List', *(
+ # Expression('List', Expression('MakeBoxes', item, f))
+ # for item in table.elements)))
+ else:
+ options["System`TableDepth"] = Integer(depth - 2)
+
+ def transform_item(item):
+ if depth > 2:
+ return self.eval_makeboxes(item, f, evaluation, options)
+ else:
+ return format_element(item, evaluation, f)
+
+ result = GridBox(
+ ListExpression(
+ *(
+ ListExpression(
+ *(transform_item(item) for item in row.elements),
+ )
+ for row in table.elements
+ ),
+ )
+ )
+ options["System`TableDepth"] = Integer(depth)
+ return result
+
+
+class MatrixForm(TableForm):
+ """
+
+ :WMA link:
+ https://reference.wolfram.com/language/ref/MatrixForm.html
+
+
+ - 'MatrixForm[$m$]'
+
- displays a matrix $m$, hiding the underlying list structure.
+
+
+ >> Array[a,{4,3}]//MatrixForm
+ = a[1, 1] a[1, 2] a[1, 3]
+ .
+ . a[2, 1] a[2, 2] a[2, 3]
+ .
+ . a[3, 1] a[3, 2] a[3, 3]
+ .
+ . a[4, 1] a[4, 2] a[4, 3]
+
+ ## Issue #182
+ #> {{2*a, 0},{0,0}}//MatrixForm
+ = 2 a 0
+ .
+ . 0 0
+ """
+
+ in_outputforms = True
+ in_printforms = False
+ summary_text = "format as a matrix"
+
+ def eval_makeboxes_matrix(self, table, f, evaluation, options):
+ """MakeBoxes[%(name)s[table_, OptionsPattern[%(name)s]],
+ f:StandardForm|TraditionalForm]"""
+
+ result = super(MatrixForm, self).eval_makeboxes(table, f, evaluation, options)
+ if result.get_head_name() == "System`GridBox":
+ return RowBox(String("("), result, String(")"))
+
+ return result
diff --git a/mathics/builtin/forms/variables.py b/mathics/builtin/forms/variables.py
new file mode 100644
index 000000000..0b9b1e3a4
--- /dev/null
+++ b/mathics/builtin/forms/variables.py
@@ -0,0 +1,60 @@
+"""
+Form variables
+
+"""
+
+from mathics.builtin.base import (
+ Predefined,
+)
+
+from mathics.core.attributes import A_LOCKED, A_PROTECTED
+from mathics.core.list import ListExpression
+
+
+class PrintForms_(Predefined):
+ r"""
+
+ - '$PrintForms'
+
- contains the list of basic print forms. It is updated automatically when new 'PrintForms' are defined by setting format values.
+
+
+ >> $PrintForms
+ = ...
+
+ Suppose now that we want to add a new format 'MyForm'. Initially, it does not belong to '$PrintForms':
+ >> MemberQ[$PrintForms, MyForm]
+ = False
+ Now, let's define a format rule:
+ >> Format[MyForm[F[x_]]]:= "F<<" <> ToString[x] <> ">>"
+ >> Format[F[x_], MyForm]:= MyForm[F[x]]
+ Now, the new format belongs to the '$PrintForms' list
+ >> MemberQ[$PrintForms, MyForm]
+ = True
+
+ """
+
+ attributes = A_LOCKED | A_PROTECTED
+ name = "$PrintForms"
+ summary_text = "list common print forms"
+
+ def evaluate(self, evaluation):
+ return ListExpression(*evaluation.definitions.printforms)
+
+
+class OutputForms_(Predefined):
+ r"""
+
+ - '$OutputForms'
+
- contains the list of all output forms. It is updated automatically when new 'OutputForms' are defined by setting format values.
+
+
+ >> $OutputForms
+ = ...
+ """
+
+ attributes = A_LOCKED | A_PROTECTED
+ name = "$OutputForms"
+ summary_text = "list all output forms"
+
+ def evaluate(self, evaluation):
+ return ListExpression(*evaluation.definitions.outputforms)
diff --git a/mathics/builtin/layout.py b/mathics/builtin/layout.py
index bacc56223..3e05f8175 100644
--- a/mathics/builtin/layout.py
+++ b/mathics/builtin/layout.py
@@ -23,8 +23,8 @@
from mathics.core.atoms import Real, String
from mathics.core.expression import Expression
-from mathics.core.formatter import format_element
from mathics.core.list import ListExpression
+from mathics.core.makeboxes import format_element
from mathics.core.symbols import Symbol
from mathics.core.systemsymbols import SymbolMakeBoxes
diff --git a/mathics/builtin/makeboxes.py b/mathics/builtin/makeboxes.py
index 290eb2e1f..06e446fd9 100644
--- a/mathics/builtin/makeboxes.py
+++ b/mathics/builtin/makeboxes.py
@@ -10,14 +10,17 @@
from mathics.builtin.base import Builtin, Predefined
-from mathics.builtin.box.layout import _boxed_string, RowBox, to_boxes
+from mathics.builtin.box.layout import (
+ _boxed_string,
+ RowBox,
+ to_boxes,
+)
+
from mathics.core.convert.op import operator_to_unicode, operator_to_ascii
from mathics.core.atoms import (
Integer,
Integer1,
Real,
- PrecisionReal,
- MachineReal,
String,
)
@@ -27,8 +30,8 @@
)
from mathics.core.element import BaseElement, BoxElementMixin
from mathics.core.expression import Expression
-from mathics.core.formatter import format_element
from mathics.core.list import ListExpression
+from mathics.core.makeboxes import format_element
from mathics.core.number import (
dps,
convert_base,
@@ -38,27 +41,15 @@
from mathics.core.symbols import (
Atom,
Symbol,
- SymbolFalse,
- SymbolNull,
- SymbolTrue,
)
from mathics.core.systemsymbols import (
- SymbolAutomatic,
- SymbolInfinity,
SymbolInputForm,
- SymbolMakeBoxes,
SymbolOutputForm,
SymbolRowBox,
- SymbolRuleDelayed,
)
-SymbolNumberForm = Symbol("System`NumberForm")
-SymbolSuperscriptBox = Symbol("System`SuperscriptBox")
-SymbolSubscriptBox = Symbol("System`SubscriptBox")
-
-
def int_to_s_exp(expr, n):
n = expr.get_int_value()
if n < 0:
@@ -585,567 +576,3 @@ def apply(self, expr, form, evaluation):
evaluation.message("ToBoxes", "boxfmt", form)
boxes = format_element(expr, evaluation, form)
return boxes
-
-
-# ### The following classes maybe deserves another module...
-
-
-class _NumberForm(Builtin):
- """
- Base class for NumberForm, AccountingForm, EngineeringForm, and ScientificForm.
- """
-
- default_ExponentFunction = None
- default_NumberFormat = None
-
- messages = {
- "npad": "Value for option NumberPadding -> `1` should be a string or a pair of strings.",
- "dblk": "Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.",
- "npt": "Value for option `1` -> `2` is expected to be a string.",
- "nsgn": "Value for option NumberSigns -> `1` should be a pair of strings or two pairs of strings.",
- "nspr": "Value for option NumberSeparator -> `1` should be a string or a pair of strings.",
- "opttf": "Value of option `1` -> `2` should be True or False.",
- "estep": "Value of option `1` -> `2` is not a positive integer.",
- "iprf": "Formatting specification `1` should be a positive integer or a pair of positive integers.", # NumberFormat only
- "sigz": "In addition to the number of digits requested, one or more zeros will appear as placeholders.",
- }
-
- def check_options(self, options, evaluation):
- """
- Checks options are valid and converts them to python.
- """
- result = {}
- for option_name in self.options:
- method = getattr(self, "check_" + option_name)
- arg = options["System`" + option_name]
- value = method(arg, evaluation)
- if value is None:
- return None
- result[option_name] = value
- return result
-
- def check_DigitBlock(self, value, evaluation):
- py_value = value.get_int_value()
- if value.sameQ(SymbolInfinity):
- return [0, 0]
- elif py_value is not None and py_value > 0:
- return [py_value, py_value]
- elif value.has_form("List", 2):
- nleft, nright = value.elements
- py_left, py_right = nleft.get_int_value(), nright.get_int_value()
- if nleft.sameQ(SymbolInfinity):
- nleft = 0
- elif py_left is not None and py_left > 0:
- nleft = py_left
- else:
- nleft = None
- if nright.sameQ(SymbolInfinity):
- nright = 0
- elif py_right is not None and py_right > 0:
- nright = py_right
- else:
- nright = None
- result = [nleft, nright]
- if None not in result:
- return result
- return evaluation.message(self.get_name(), "dblk", value)
-
- def check_ExponentFunction(self, value, evaluation):
- if value.sameQ(SymbolAutomatic):
- return self.default_ExponentFunction
-
- def exp_function(x):
- return Expression(value, x).evaluate(evaluation)
-
- return exp_function
-
- def check_NumberFormat(self, value, evaluation):
- if value.sameQ(SymbolAutomatic):
- return self.default_NumberFormat
-
- def num_function(man, base, exp, options):
- return Expression(value, man, base, exp).evaluate(evaluation)
-
- return num_function
-
- def check_NumberMultiplier(self, value, evaluation):
- result = value.get_string_value()
- if result is None:
- evaluation.message(self.get_name(), "npt", "NumberMultiplier", value)
- return result
-
- def check_NumberPoint(self, value, evaluation):
- result = value.get_string_value()
- if result is None:
- evaluation.message(self.get_name(), "npt", "NumberPoint", value)
- return result
-
- def check_ExponentStep(self, value, evaluation):
- result = value.get_int_value()
- if result is None or result <= 0:
- return evaluation.message(self.get_name(), "estep", "ExponentStep", value)
- return result
-
- def check_SignPadding(self, value, evaluation):
- if value.sameQ(SymbolTrue):
- return True
- elif value.sameQ(SymbolFalse):
- return False
- return evaluation.message(self.get_name(), "opttf", value)
-
- def _check_List2str(self, value, msg, evaluation):
- if value.has_form("List", 2):
- result = [element.get_string_value() for element in value.elements]
- if None not in result:
- return result
- return evaluation.message(self.get_name(), msg, value)
-
- def check_NumberSigns(self, value, evaluation):
- return self._check_List2str(value, "nsgn", evaluation)
-
- def check_NumberPadding(self, value, evaluation):
- return self._check_List2str(value, "npad", evaluation)
-
- def check_NumberSeparator(self, value, evaluation):
- py_str = value.get_string_value()
- if py_str is not None:
- return [py_str, py_str]
- return self._check_List2str(value, "nspr", evaluation)
-
-
-class NumberForm(_NumberForm):
- """
-
- - 'NumberForm[$expr$, $n$]'
-
- prints a real number $expr$ with $n$-digits of precision.
-
-
- 'NumberForm[$expr$, {$n$, $f$}]'
-
- prints with $n$-digits and $f$ digits to the right of the decimal point.
-
-
- >> NumberForm[N[Pi], 10]
- = 3.141592654
-
- >> NumberForm[N[Pi], {10, 5}]
- = 3.14159
-
-
- ## Undocumented edge cases
- #> NumberForm[Pi, 20]
- = Pi
- #> NumberForm[2/3, 10]
- = 2 / 3
-
- ## No n or f
- #> NumberForm[N[Pi]]
- = 3.14159
- #> NumberForm[N[Pi, 20]]
- = 3.1415926535897932385
- #> NumberForm[14310983091809]
- = 14310983091809
-
- ## Zero case
- #> z0 = 0.0;
- #> z1 = 0.0000000000000000000000000000;
- #> NumberForm[{z0, z1}, 10]
- = {0., 0.×10^-28}
- #> NumberForm[{z0, z1}, {10, 4}]
- = {0.0000, 0.0000×10^-28}
-
- ## Trailing zeros
- #> NumberForm[1.0, 10]
- = 1.
- #> NumberForm[1.000000000000000000000000, 10]
- = 1.000000000
- #> NumberForm[1.0, {10, 8}]
- = 1.00000000
- #> NumberForm[N[Pi, 33], 33]
- = 3.14159265358979323846264338327950
-
- ## Correct rounding - see sympy/issues/11472
- #> NumberForm[0.645658509, 6]
- = 0.645659
- #> NumberForm[N[1/7], 30]
- = 0.1428571428571428
-
- ## Integer case
- #> NumberForm[{0, 2, -415, 83515161451}, 5]
- = {0, 2, -415, 83515161451}
- #> NumberForm[{2^123, 2^123.}, 4, ExponentFunction -> ((#1) &)]
- = {10633823966279326983230456482242756608, 1.063×10^37}
- #> NumberForm[{0, 10, -512}, {10, 3}]
- = {0.000, 10.000, -512.000}
-
- ## Check arguments
- #> NumberForm[1.5, -4]
- : Formatting specification -4 should be a positive integer or a pair of positive integers.
- = 1.5
- #> NumberForm[1.5, {1.5, 2}]
- : Formatting specification {1.5, 2} should be a positive integer or a pair of positive integers.
- = 1.5
- #> NumberForm[1.5, {1, 2.5}]
- : Formatting specification {1, 2.5} should be a positive integer or a pair of positive integers.
- = 1.5
-
- ## Right padding
- #> NumberForm[153., 2]
- : In addition to the number of digits requested, one or more zeros will appear as placeholders.
- = 150.
- #> NumberForm[0.00125, 1]
- = 0.001
- #> NumberForm[10^5 N[Pi], {5, 3}]
- : In addition to the number of digits requested, one or more zeros will appear as placeholders.
- = 314160.000
- #> NumberForm[10^5 N[Pi], {6, 3}]
- = 314159.000
- #> NumberForm[10^5 N[Pi], {6, 10}]
- = 314159.0000000000
- #> NumberForm[1.0000000000000000000, 10, NumberPadding -> {"X", "Y"}]
- = X1.000000000
-
- ## Check options
-
- ## DigitBlock
- #> NumberForm[12345.123456789, 14, DigitBlock -> 3]
- = 12,345.123 456 789
- #> NumberForm[12345.12345678, 14, DigitBlock -> 3]
- = 12,345.123 456 78
- #> NumberForm[N[10^ 5 Pi], 15, DigitBlock -> {4, 2}]
- = 31,4159.26 53 58 97 9
- #> NumberForm[1.2345, 3, DigitBlock -> -4]
- : Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.
- = 1.2345
- #> NumberForm[1.2345, 3, DigitBlock -> x]
- : Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.
- = 1.2345
- #> NumberForm[1.2345, 3, DigitBlock -> {x, 3}]
- : Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.
- = 1.2345
- #> NumberForm[1.2345, 3, DigitBlock -> {5, -3}]
- : Value for option DigitBlock should be a positive integer, Infinity, or a pair of positive integers.
- = 1.2345
-
- ## ExponentFunction
- #> NumberForm[12345.123456789, 14, ExponentFunction -> ((#) &)]
- = 1.2345123456789×10^4
- #> NumberForm[12345.123456789, 14, ExponentFunction -> (Null&)]
- = 12345.123456789
- #> y = N[Pi^Range[-20, 40, 15]];
- #> NumberForm[y, 10, ExponentFunction -> (3 Quotient[#, 3] &)]
- = {114.0256472×10^-12, 3.267763643×10^-3, 93.64804748×10^3, 2.683779414×10^12, 76.91214221×10^18}
- #> NumberForm[y, 10, ExponentFunction -> (Null &)]
- : In addition to the number of digits requested, one or more zeros will appear as placeholders.
- : In addition to the number of digits requested, one or more zeros will appear as placeholders.
- = {0.0000000001140256472, 0.003267763643, 93648.04748, 2683779414000., 76912142210000000000.}
-
- ## ExponentStep
- #> NumberForm[10^8 N[Pi], 10, ExponentStep -> 3]
- = 314.1592654×10^6
- #> NumberForm[1.2345, 3, ExponentStep -> x]
- : Value of option ExponentStep -> x is not a positive integer.
- = 1.2345
- #> NumberForm[1.2345, 3, ExponentStep -> 0]
- : Value of option ExponentStep -> 0 is not a positive integer.
- = 1.2345
- #> NumberForm[y, 10, ExponentStep -> 6]
- = {114.0256472×10^-12, 3267.763643×10^-6, 93648.04748, 2.683779414×10^12, 76.91214221×10^18}
-
- ## NumberFormat
- #> NumberForm[y, 10, NumberFormat -> (#1 &)]
- = {1.140256472, 0.003267763643, 93648.04748, 2.683779414, 7.691214221}
-
- ## NumberMultiplier
- #> NumberForm[1.2345, 3, NumberMultiplier -> 0]
- : Value for option NumberMultiplier -> 0 is expected to be a string.
- = 1.2345
- #> NumberForm[N[10^ 7 Pi], 15, NumberMultiplier -> "*"]
- = 3.14159265358979*10^7
-
- ## NumberPoint
- #> NumberForm[1.2345, 5, NumberPoint -> ","]
- = 1,2345
- #> NumberForm[1.2345, 3, NumberPoint -> 0]
- : Value for option NumberPoint -> 0 is expected to be a string.
- = 1.2345
-
- ## NumberPadding
- #> NumberForm[1.41, {10, 5}]
- = 1.41000
- #> NumberForm[1.41, {10, 5}, NumberPadding -> {"", "X"}]
- = 1.41XXX
- #> NumberForm[1.41, {10, 5}, NumberPadding -> {"X", "Y"}]
- = XXXXX1.41YYY
- #> NumberForm[1.41, 10, NumberPadding -> {"X", "Y"}]
- = XXXXXXXX1.41
- #> NumberForm[1.2345, 3, NumberPadding -> 0]
- : Value for option NumberPadding -> 0 should be a string or a pair of strings.
- = 1.2345
- #> NumberForm[1.41, 10, NumberPadding -> {"X", "Y"}, NumberSigns -> {"-------------", ""}]
- = XXXXXXXXXXXXXXXXXXXX1.41
- #> NumberForm[{1., -1., 2.5, -2.5}, {4, 6}, NumberPadding->{"X", "Y"}]
- = {X1.YYYYYY, -1.YYYYYY, X2.5YYYYY, -2.5YYYYY}
-
- ## NumberSeparator
- #> NumberForm[N[10^ 5 Pi], 15, DigitBlock -> 3, NumberSeparator -> " "]
- = 314 159.265 358 979
- #> NumberForm[N[10^ 5 Pi], 15, DigitBlock -> 3, NumberSeparator -> {" ", ","}]
- = 314 159.265,358,979
- #> NumberForm[N[10^ 5 Pi], 15, DigitBlock -> 3, NumberSeparator -> {",", " "}]
- = 314,159.265 358 979
- #> NumberForm[N[10^ 7 Pi], 15, DigitBlock -> 3, NumberSeparator -> {",", " "}]
- = 3.141 592 653 589 79×10^7
- #> NumberForm[1.2345, 3, NumberSeparator -> 0]
- : Value for option NumberSeparator -> 0 should be a string or a pair of strings.
- = 1.2345
-
- ## NumberSigns
- #> NumberForm[1.2345, 5, NumberSigns -> {"-", "+"}]
- = +1.2345
- #> NumberForm[-1.2345, 5, NumberSigns -> {"- ", ""}]
- = - 1.2345
- #> NumberForm[1.2345, 3, NumberSigns -> 0]
- : Value for option NumberSigns -> 0 should be a pair of strings or two pairs of strings.
- = 1.2345
-
- ## SignPadding
- #> NumberForm[1.234, 6, SignPadding -> True, NumberPadding -> {"X", "Y"}]
- = XXX1.234
- #> NumberForm[-1.234, 6, SignPadding -> True, NumberPadding -> {"X", "Y"}]
- = -XX1.234
- #> NumberForm[-1.234, 6, SignPadding -> False, NumberPadding -> {"X", "Y"}]
- = XX-1.234
- #> NumberForm[-1.234, {6, 4}, SignPadding -> False, NumberPadding -> {"X", "Y"}]
- = X-1.234Y
-
- ## 1-arg, Option case
- #> NumberForm[34, ExponentFunction->(Null&)]
- = 34
-
- ## zero padding integer x0.0 case
- #> NumberForm[50.0, {5, 1}]
- = 50.0
- #> NumberForm[50, {5, 1}]
- = 50.0
-
- ## Rounding correctly
- #> NumberForm[43.157, {10, 1}]
- = 43.2
- #> NumberForm[43.15752525, {10, 5}, NumberSeparator -> ",", DigitBlock -> 1]
- = 4,3.1,5,7,5,3
- #> NumberForm[80.96, {16, 1}]
- = 81.0
- #> NumberForm[142.25, {10, 1}]
- = 142.3
- """
-
- options = {
- "DigitBlock": "Infinity",
- "ExponentFunction": "Automatic",
- "ExponentStep": "1",
- "NumberFormat": "Automatic",
- "NumberMultiplier": '"×"',
- "NumberPadding": '{"", "0"}',
- "NumberPoint": '"."',
- "NumberSeparator": '{",", " "}',
- "NumberSigns": '{"-", ""}',
- "SignPadding": "False",
- }
- summary_text = "print at most a number of digits of all approximate real numbers in the expression"
-
- @staticmethod
- def default_ExponentFunction(value):
- n = value.get_int_value()
- if -5 <= n <= 5:
- return SymbolNull
- else:
- return value
-
- @staticmethod
- def default_NumberFormat(man, base, exp, options):
- py_exp = exp.get_string_value()
- if py_exp:
- mul = String(options["NumberMultiplier"])
- return Expression(
- SymbolRowBox,
- ListExpression(man, mul, Expression(SymbolSuperscriptBox, base, exp)),
- )
- else:
- return man
-
- def apply_list_n(self, expr, n, evaluation, options) -> Expression:
- "NumberForm[expr_List, n_, OptionsPattern[NumberForm]]"
- options = [
- Expression(SymbolRuleDelayed, Symbol(key), value)
- for key, value in options.items()
- ]
- return ListExpression(
- *[
- Expression(SymbolNumberForm, element, n, *options)
- for element in expr.elements
- ]
- )
-
- def apply_list_nf(self, expr, n, f, evaluation, options) -> Expression:
- "NumberForm[expr_List, {n_, f_}, OptionsPattern[NumberForm]]"
- options = [
- Expression(SymbolRuleDelayed, Symbol(key), value)
- for key, value in options.items()
- ]
- return ListExpression(
- *[
- Expression(SymbolNumberForm, element, ListExpression(n, f), *options)
- for element in expr.elements
- ],
- )
-
- def apply_makeboxes(self, expr, form, evaluation, options={}):
- """MakeBoxes[NumberForm[expr_, OptionsPattern[NumberForm]],
- form:StandardForm|TraditionalForm|OutputForm]"""
-
- fallback = Expression(SymbolMakeBoxes, expr, form)
-
- py_options = self.check_options(options, evaluation)
- if py_options is None:
- return fallback
-
- if isinstance(expr, Integer):
- py_n = len(str(abs(expr.get_int_value())))
- elif isinstance(expr, Real):
- if expr.is_machine_precision():
- py_n = 6
- else:
- py_n = dps(expr.get_precision())
- else:
- py_n = None
-
- if py_n is not None:
- py_options["_Form"] = form.get_name()
- return number_form(expr, py_n, None, evaluation, py_options)
- return Expression(SymbolMakeBoxes, expr, form)
-
- def apply_makeboxes_n(self, expr, n, form, evaluation, options={}):
- """MakeBoxes[NumberForm[expr_, n_?NotOptionQ, OptionsPattern[NumberForm]],
- form:StandardForm|TraditionalForm|OutputForm]"""
-
- fallback = Expression(SymbolMakeBoxes, expr, form)
-
- py_n = n.get_int_value()
- if py_n is None or py_n <= 0:
- evaluation.message("NumberForm", "iprf", n)
- return fallback
-
- py_options = self.check_options(options, evaluation)
- if py_options is None:
- return fallback
-
- if isinstance(expr, (Integer, Real)):
- py_options["_Form"] = form.get_name()
- return number_form(expr, py_n, None, evaluation, py_options)
- return Expression(SymbolMakeBoxes, expr, form)
-
- def apply_makeboxes_nf(self, expr, n, f, form, evaluation, options={}):
- """MakeBoxes[NumberForm[expr_, {n_, f_}, OptionsPattern[NumberForm]],
- form:StandardForm|TraditionalForm|OutputForm]"""
-
- fallback = Expression(SymbolMakeBoxes, expr, form)
-
- nf = ListExpression(n, f)
- py_n = n.get_int_value()
- py_f = f.get_int_value()
- if py_n is None or py_n <= 0 or py_f is None or py_f < 0:
- evaluation.message("NumberForm", "iprf", nf)
- return fallback
-
- py_options = self.check_options(options, evaluation)
- if py_options is None:
- return fallback
-
- if isinstance(expr, (Integer, Real)):
- py_options["_Form"] = form.get_name()
- return number_form(expr, py_n, py_f, evaluation, py_options)
- return Expression(SymbolMakeBoxes, expr, form)
-
-
-class BaseForm(Builtin):
- """
-
- - 'BaseForm[$expr$, $n$]'
-
- prints numbers in $expr$ in base $n$.
-
-
- >> BaseForm[33, 2]
- = 100001_2
-
- >> BaseForm[234, 16]
- = ea_16
-
- >> BaseForm[12.3, 2]
- = 1100.01001100110011001_2
-
- >> BaseForm[-42, 16]
- = -2a_16
-
- >> BaseForm[x, 2]
- = x
-
- >> BaseForm[12, 3] // FullForm
- = BaseForm[12, 3]
-
- Bases must be between 2 and 36:
- >> BaseForm[12, -3]
- : Positive machine-sized integer expected at position 2 in BaseForm[12, -3].
- = BaseForm[12, -3]
- >> BaseForm[12, 100]
- : Requested base 100 must be between 2 and 36.
- = BaseForm[12, 100]
-
- #> BaseForm[0, 2]
- = 0_2
- #> BaseForm[0.0, 2]
- = 0.0_2
-
- #> BaseForm[N[Pi, 30], 16]
- = 3.243f6a8885a308d313198a2e_16
- """
-
- summary_text = "print with all numbers given in a base"
- messages = {
- "intpm": (
- "Positive machine-sized integer expected at position 2 in "
- "BaseForm[`1`, `2`]."
- ),
- "basf": "Requested base `1` must be between 2 and 36.",
- }
-
- def apply_makeboxes(self, expr, n, f, evaluation):
- """MakeBoxes[BaseForm[expr_, n_],
- f:StandardForm|TraditionalForm|OutputForm]"""
-
- base = n.get_int_value()
- if base <= 0:
- evaluation.message("BaseForm", "intpm", expr, n)
- return None
-
- if isinstance(expr, PrecisionReal):
- x = expr.to_sympy()
- p = reconstruct_digits(expr.get_precision())
- elif isinstance(expr, MachineReal):
- x = expr.value
- p = reconstruct_digits(machine_precision)
- elif isinstance(expr, Integer):
- x = expr.value
- p = 0
- else:
- return to_boxes(Expression(SymbolMakeBoxes, expr, f), evaluation)
-
- try:
- val = convert_base(x, base, p)
- except ValueError:
- return evaluation.message("BaseForm", "basf", n)
-
- if f is SymbolOutputForm:
- return to_boxes(String("%s_%d" % (val, base)), evaluation)
- else:
- return to_boxes(
- Expression(SymbolSubscriptBox, String(val), String(base)), evaluation
- )
diff --git a/mathics/builtin/numbers/calculus.py b/mathics/builtin/numbers/calculus.py
index cdf3dbf61..6ff8bdfc8 100644
--- a/mathics/builtin/numbers/calculus.py
+++ b/mathics/builtin/numbers/calculus.py
@@ -58,8 +58,8 @@
from mathics.core.evaluation import Evaluation
from mathics.core.evaluators import eval_N
from mathics.core.expression import Expression
-from mathics.core.formatter import format_element
from mathics.core.list import ListExpression
+from mathics.core.makeboxes import format_element
from mathics.core.number import dps, machine_epsilon
from mathics.core.rules import Pattern
diff --git a/mathics/builtin/patterns.py b/mathics/builtin/patterns.py
index 7faba7731..95f8f358b 100644
--- a/mathics/builtin/patterns.py
+++ b/mathics/builtin/patterns.py
@@ -1702,7 +1702,7 @@ def __repr__(self):
def atom_to_boxes(self, f, evaluation):
from mathics.builtin.box.layout import RowBox
- from mathics.core.formatter import format_element
+ from mathics.core.makeboxes import format_element
box_element = format_element(self.src, evaluation, f)
return RowBox(String("Dispatch"), String("["), box_element, String("]"))
diff --git a/mathics/builtin/statistics/dependency.py b/mathics/builtin/statistics/dependency.py
index c3ba6b581..4a4ba22e2 100644
--- a/mathics/builtin/statistics/dependency.py
+++ b/mathics/builtin/statistics/dependency.py
@@ -20,7 +20,11 @@
SymbolConjugate = Symbol("Conjugate")
SymbolCovariance = Symbol("Covariance")
+
+# Something is weird here. No System`. And we can't use what is in
+# SymbolSqrt from systemsymbols?
SymbolSqrt = Symbol("Sqrt")
+
SymbolStandardDeviation = Symbol("StandardDeviation")
SymbolVariance = Symbol("Variance")
diff --git a/mathics/builtin/string/operations.py b/mathics/builtin/string/operations.py
index 47887bc58..79bd932a2 100644
--- a/mathics/builtin/string/operations.py
+++ b/mathics/builtin/string/operations.py
@@ -40,8 +40,8 @@
)
from mathics.core.convert.python import from_python
from mathics.core.expression import Expression, string_list
-from mathics.core.formatter import format_element
from mathics.core.list import ListExpression
+from mathics.core.makeboxes import format_element
from mathics.core.symbols import (
Symbol,
SymbolFalse,
diff --git a/mathics/core/atoms.py b/mathics/core/atoms.py
index 9134b16d9..daa11dccb 100644
--- a/mathics/core/atoms.py
+++ b/mathics/core/atoms.py
@@ -238,7 +238,7 @@ def __new__(cls, numerator, denominator=1) -> "Rational":
return self
def atom_to_boxes(self, f, evaluation):
- from mathics.core.formatter import format_element
+ from mathics.core.makeboxes import format_element
return format_element(self, evaluation, f)
@@ -671,7 +671,7 @@ def __new__(cls, real, imag):
return self
def atom_to_boxes(self, f, evaluation):
- from mathics.core.formatter import format_element
+ from mathics.core.makeboxes import format_element
return format_element(self, evaluation, f)
@@ -936,7 +936,7 @@ def __new__(cls, numerator, denominator=1) -> "Rational":
return self
def atom_to_boxes(self, f, evaluation):
- from mathics.core.formatter import format_element
+ from mathics.core.makeboxes import format_element
return format_element(self, evaluation, f)
diff --git a/mathics/core/definitions.py b/mathics/core/definitions.py
index 90e0732d8..def6b1a93 100644
--- a/mathics/core/definitions.py
+++ b/mathics/core/definitions.py
@@ -112,17 +112,23 @@ def __init__(
"System`",
"Global`",
)
+
+ # Importing "mathics.format" populates the Symbol of the
+ # PrintForms and OutputForms sets.
+ #
+ # If "importlib" is used instead of "import", then we get:
+ # TypeError: boxes_to_text() takes 1 positional argument but
+ # 2 were given
+ # Rocky: this smells of something not quite right in terms of
+ # modularity.
+
+ import mathics.format # noqa
+
self.printforms = list(PrintForms)
self.outputforms = list(OutputForms)
self.trace_evaluation = False
self.timing_trace_evaluation = False
- # This loads all the formatting functions.
- # It needs to be early because it can be used in
- # messages during the builtins loading.
- # Rocky: this smells of something not quite right in terms of modularity.
- import mathics.format
-
if add_builtin:
from mathics.builtin import modules, contribute
from mathics.settings import ROOT_DIR
diff --git a/mathics/core/element.py b/mathics/core/element.py
index 26f525311..692aab0ad 100644
--- a/mathics/core/element.py
+++ b/mathics/core/element.py
@@ -246,7 +246,7 @@ def equal2(self, rhs: Any) -> Optional[bool]:
return None
def format(self, evaluation, form, **kwargs) -> "BoxElementMixin":
- from mathics.core.formatter import format_element
+ from mathics.core.makeboxes import format_element
from mathics.core.symbols import Symbol
if isinstance(form, str):
diff --git a/mathics/core/evaluation.py b/mathics/core/evaluation.py
index 3c1dfa174..d84266484 100644
--- a/mathics/core/evaluation.py
+++ b/mathics/core/evaluation.py
@@ -455,7 +455,7 @@ def stop(self) -> None:
self.stopped = True
def format_output(self, expr, format=None):
- from mathics.core.formatter import format_element
+ from mathics.core.makeboxes import format_element
if format is None:
format = self.format
diff --git a/mathics/core/formatter.py b/mathics/core/formatter.py
index 6ce617b58..eb0f8f247 100644
--- a/mathics/core/formatter.py
+++ b/mathics/core/formatter.py
@@ -1,40 +1,9 @@
import inspect
-import typing
-from typing import Any, Callable
+from typing import Callable
import re
-from mathics.core.atoms import SymbolI, String, Integer, Rational, Complex
-from mathics.core.element import BaseElement, BoxElementMixin, EvalMixin
-from mathics.core.convert.expression import to_expression_with_specialization
-from mathics.core.definitions import OutputForms
-from mathics.core.evaluation import Evaluation
-from mathics.core.expression import Expression
-from mathics.core.list import ListExpression
-from mathics.core.symbols import (
- Symbol,
- SymbolMakeBoxes,
- Atom,
- SymbolDivide,
- SymbolFullForm,
- SymbolGraphics,
- SymbolGraphics3D,
- SymbolHoldForm,
- SymbolList,
- SymbolNumberForm,
- SymbolPostfix,
- SymbolPlus,
- SymbolRepeated,
- SymbolRepeatedNull,
- SymbolTimes,
-)
-from mathics.core.systemsymbols import (
- SymbolComplex,
- SymbolMinus,
- SymbolOutputForm,
- SymbolRational,
- SymbolStandardForm,
-)
+from mathics.core.element import BoxElementMixin
# key is str: (to_xxx name, value) is formatter function to call
format2fn: dict = {}
@@ -171,230 +140,3 @@ def add_conversion_fn(cls, module_fn_name=None) -> None:
# Finally register the mapping: (Builtin-class, conversion name) -> conversion_function.
format2fn[(conversion_type, cls)] = module_dict[module_fn_name]
-
-
-element_formatters = {}
-
-
-def format_element(
- element: BaseElement, evaluation: Evaluation, form: Symbol, **kwargs
-) -> BaseElement:
- """
- Applies formats associated to the expression, and then calls Makeboxes
- """
- expr = do_format(element, evaluation, form)
- result = Expression(SymbolMakeBoxes, expr, form)
- result_box = result.evaluate(evaluation)
- if isinstance(result_box, String):
- return result_box
- if isinstance(result_box, BoxElementMixin):
- return result_box
- else:
- return format_element(element, evaluation, SymbolFullForm, **kwargs)
-
-
-# do_format_*
-
-
-def do_format(
- element: BaseElement, evaluation: Evaluation, form: Symbol
-) -> BaseElement:
- do_format_method = element_formatters.get(type(element), do_format_element)
- return do_format_method(element, evaluation, form)
-
-
-def do_format_element(
- element: BaseElement, evaluation: Evaluation, form: Symbol
-) -> BaseElement:
- """
- Applies formats associated to the expression and removes
- superfluous enclosing formats.
- """
-
- evaluation.inc_recursion_depth()
- try:
- expr = element
- head = element.get_head() # use element.head
- elements = element.get_elements()
- include_form = False
- # If the expression is enclosed by a Format
- # takes the form from the expression and
- # removes the format from the expression.
- if head in OutputForms and len(expr.elements) == 1:
- expr = elements[0]
- if not (form is SymbolOutputForm and head is SymbolStandardForm):
- form = head
- include_form = True
-
- # If form is Fullform, return it without changes
- if form is SymbolFullForm:
- if include_form:
- expr = Expression(form, expr)
- return expr
- # Repeated and RepeatedNull confuse the formatter,
- # so we need to hardlink their format rules:
- if head is SymbolRepeated:
- if len(elements) == 1:
- return Expression(
- SymbolHoldForm,
- Expression(
- SymbolPostfix,
- ListExpression(elements[0]),
- String(".."),
- Integer(170),
- ),
- )
- else:
- return Expression(SymbolHoldForm, expr)
- elif head is SymbolRepeatedNull:
- if len(elements) == 1:
- return Expression(
- SymbolHoldForm,
- Expression(
- SymbolPostfix,
- Expression(SymbolList, elements[0]),
- String("..."),
- Integer(170),
- ),
- )
- else:
- return Expression(SymbolHoldForm, expr)
-
- # If expr is not an atom, looks for formats in its definition
- # and apply them.
- def format_expr(expr):
- if not (isinstance(expr, Atom)) and not (isinstance(expr.head, Atom)):
- # expr is of the form f[...][...]
- return None
- name = expr.get_lookup_name()
- format_rules = evaluation.definitions.get_formats(name, form.get_name())
- for rule in format_rules:
- result = rule.apply(expr, evaluation)
- if result is not None and result != expr:
- return result.evaluate(evaluation)
- return None
-
- formatted = format_expr(expr) if isinstance(expr, EvalMixin) else None
- if formatted is not None:
- do_format = element_formatters.get(type(formatted), do_format_element)
- result = do_format(formatted, evaluation, form)
- if include_form:
- result = Expression(form, result)
- return result
-
- # If the expression is still enclosed by a Format,
- # iterate.
- # If the expression is not atomic or of certain
- # specific cases, iterate over the elements.
- head = expr.get_head()
- if head in OutputForms:
- # If the expression was of the form
- # Form[expr, opts]
- # then the format was not stripped. Then,
- # just return it as it is.
- if len(expr.elements) != 1:
- return expr
- do_format = element_formatters.get(type(element), do_format_element)
- expr = do_format(expr, evaluation, form)
-
- elif (
- head is not SymbolNumberForm
- and not isinstance(expr, (Atom, BoxElementMixin))
- and head not in (SymbolGraphics, SymbolGraphics3D)
- ):
- # print("Not inside graphics or numberform, and not is atom")
- new_elements = [
- element_formatters.get(type(element), do_format_element)(
- element, evaluation, form
- )
- for element in expr.elements
- ]
- expr_head = expr.head
- do_format = element_formatters.get(type(expr_head), do_format_element)
- head = do_format(expr_head, evaluation, form)
- expr = to_expression_with_specialization(head, *new_elements)
-
- if include_form:
- expr = Expression(form, expr)
- return expr
- finally:
- evaluation.dec_recursion_depth()
-
-
-def do_format_rational(
- element: BaseElement, evaluation: Evaluation, form: Symbol
-) -> BaseElement:
- if form is SymbolFullForm:
- return do_format_expression(
- Expression(
- Expression(SymbolHoldForm, SymbolRational),
- element.numerator(),
- element.denominator(),
- ),
- evaluation,
- form,
- )
- else:
- numerator = element.numerator()
- minus = numerator.value < 0
- if minus:
- numerator = Integer(-numerator.value)
- result = Expression(SymbolDivide, numerator, element.denominator())
- if minus:
- result = Expression(SymbolMinus, result)
- result = Expression(SymbolHoldForm, result)
- return do_format_expression(result, evaluation, form)
-
-
-def do_format_complex(
- element: BaseElement, evaluation: Evaluation, form: Symbol
-) -> BaseElement:
- if form is SymbolFullForm:
- return do_format_expression(
- Expression(
- Expression(SymbolHoldForm, SymbolComplex), element.real, element.imag
- ),
- evaluation,
- form,
- )
-
- parts: typing.List[Any] = []
- if element.is_machine_precision() or not element.real.is_zero:
- parts.append(element.real)
- if element.imag.sameQ(Integer(1)):
- parts.append(SymbolI)
- else:
- parts.append(Expression(SymbolTimes, element.imag, SymbolI))
-
- if len(parts) == 1:
- result = parts[0]
- else:
- result = Expression(SymbolPlus, *parts)
-
- return do_format_expression(Expression(SymbolHoldForm, result), evaluation, form)
-
-
-def do_format_expression(
- element: BaseElement, evaluation: Evaluation, form: Symbol
-) -> BaseElement:
- # # not sure how much useful is this format_cache
- # if element._format_cache is None:
- # element._format_cache = {}
-
- # last_evaluated_time, expr = element._format_cache.get(form, (None, None))
- # if last_evaluated_time is not None and expr is not None:
- # if True
- # symbolname = expr.get_name()
- # if symbolname != "":
- # if not evaluation.definitions.is_uncertain_final_value(
- # last_evaluated_time, set((symbolname,))
- # ):
- # return expr
- expr = do_format_element(element, evaluation, form)
- # element._format_cache[form] = (evaluation.definitions.now, expr)
- return expr
-
-
-element_formatters[Rational] = do_format_rational
-element_formatters[Complex] = do_format_complex
-element_formatters[Expression] = do_format_expression
diff --git a/mathics/core/makeboxes.py b/mathics/core/makeboxes.py
new file mode 100644
index 000000000..0534f120b
--- /dev/null
+++ b/mathics/core/makeboxes.py
@@ -0,0 +1,284 @@
+import typing
+from typing import Any
+
+
+from mathics.core.atoms import SymbolI, String, Integer, Rational, Complex
+from mathics.core.element import BaseElement, BoxElementMixin, EvalMixin
+from mathics.core.convert.expression import to_expression_with_specialization
+from mathics.core.definitions import OutputForms
+from mathics.core.evaluation import Evaluation
+from mathics.core.expression import Expression
+from mathics.core.list import ListExpression
+from mathics.core.symbols import (
+ Symbol,
+ SymbolMakeBoxes,
+ Atom,
+ SymbolDivide,
+ SymbolFullForm,
+ SymbolGraphics,
+ SymbolGraphics3D,
+ SymbolHoldForm,
+ SymbolList,
+ SymbolNumberForm,
+ SymbolPostfix,
+ SymbolPlus,
+ SymbolRepeated,
+ SymbolRepeatedNull,
+ SymbolTimes,
+)
+from mathics.core.systemsymbols import (
+ SymbolComplex,
+ SymbolMinus,
+ SymbolOutputForm,
+ SymbolRational,
+ SymbolStandardForm,
+)
+
+
+element_formatters = {}
+
+
+def do_makeboxes(element: BaseElement, evaluation: Evaluation, f: Symbol):
+ """
+ This function applies makeboxes rules associated to `element` in
+ the form `f`.
+ """
+ # TODO:
+ # Currently, MakeBoxes rules are stored as downvalues of
+ # `MakeBoxes`. In WMA, these rules are stored as
+ # formatvalues. The evaluation of a MakeBoxes expression
+ # consists then in calling `format_element`.
+ # `format_element` then has two steps. First, applying
+ # high-level format rules (defined by setting
+ # `Format[...]` expressions)
+ # and then a second step, (in charge of this routine)
+ # that applies `MakeBoxes` low-level rules.
+ #
+ # In a next round, instead of calling `evaluate`,
+ # a customized loop of applying rules will be implemented
+ # here.
+ #
+ mb_expression = Expression(SymbolMakeBoxes, element, f)
+ return mb_expression.evaluate(evaluation)
+
+
+def format_element(
+ element: BaseElement, evaluation: Evaluation, form: Symbol, **kwargs
+) -> BaseElement:
+ """
+ Applies formats associated to the expression, and then calls Makeboxes
+ """
+ expr = do_format(element, evaluation, form)
+ result_box = do_makeboxes(expr, evaluation, form)
+
+ if isinstance(result_box, BoxElementMixin):
+ return result_box
+ else:
+ return format_element(element, evaluation, SymbolFullForm, **kwargs)
+
+
+# do_format_*
+
+
+def do_format(
+ element: BaseElement, evaluation: Evaluation, form: Symbol
+) -> BaseElement:
+ do_format_method = element_formatters.get(type(element), do_format_element)
+ return do_format_method(element, evaluation, form)
+
+
+def do_format_element(
+ element: BaseElement, evaluation: Evaluation, form: Symbol
+) -> BaseElement:
+ """
+ Applies formats associated to the expression and removes
+ superfluous enclosing formats.
+ """
+
+ evaluation.inc_recursion_depth()
+ try:
+ expr = element
+ head = element.get_head() # use element.head
+ elements = element.get_elements()
+ include_form = False
+ # If the expression is enclosed by a Format
+ # takes the form from the expression and
+ # removes the format from the expression.
+ if head in OutputForms and len(expr.elements) == 1:
+ expr = elements[0]
+ if not (form is SymbolOutputForm and head is SymbolStandardForm):
+ form = head
+ include_form = True
+
+ # If form is Fullform, return it without changes
+ if form is SymbolFullForm:
+ if include_form:
+ expr = Expression(form, expr)
+ return expr
+ # Repeated and RepeatedNull confuse the formatter,
+ # so we need to hardlink their format rules:
+ if head is SymbolRepeated:
+ if len(elements) == 1:
+ return Expression(
+ SymbolHoldForm,
+ Expression(
+ SymbolPostfix,
+ ListExpression(elements[0]),
+ String(".."),
+ Integer(170),
+ ),
+ )
+ else:
+ return Expression(SymbolHoldForm, expr)
+ elif head is SymbolRepeatedNull:
+ if len(elements) == 1:
+ return Expression(
+ SymbolHoldForm,
+ Expression(
+ SymbolPostfix,
+ Expression(SymbolList, elements[0]),
+ String("..."),
+ Integer(170),
+ ),
+ )
+ else:
+ return Expression(SymbolHoldForm, expr)
+
+ # If expr is not an atom, looks for formats in its definition
+ # and apply them.
+ def format_expr(expr):
+ if not (isinstance(expr, Atom)) and not (isinstance(expr.head, Atom)):
+ # expr is of the form f[...][...]
+ return None
+ name = expr.get_lookup_name()
+ format_rules = evaluation.definitions.get_formats(name, form.get_name())
+ for rule in format_rules:
+ result = rule.apply(expr, evaluation)
+ if result is not None and result != expr:
+ return result.evaluate(evaluation)
+ return None
+
+ formatted = format_expr(expr) if isinstance(expr, EvalMixin) else None
+ if formatted is not None:
+ do_format = element_formatters.get(type(formatted), do_format_element)
+ result = do_format(formatted, evaluation, form)
+ if include_form:
+ result = Expression(form, result)
+ return result
+
+ # If the expression is still enclosed by a Format,
+ # iterate.
+ # If the expression is not atomic or of certain
+ # specific cases, iterate over the elements.
+ head = expr.get_head()
+ if head in OutputForms:
+ # If the expression was of the form
+ # Form[expr, opts]
+ # then the format was not stripped. Then,
+ # just return it as it is.
+ if len(expr.elements) != 1:
+ return expr
+ do_format = element_formatters.get(type(element), do_format_element)
+ expr = do_format(expr, evaluation, form)
+
+ elif (
+ head is not SymbolNumberForm
+ and not isinstance(expr, (Atom, BoxElementMixin))
+ and head not in (SymbolGraphics, SymbolGraphics3D)
+ ):
+ # print("Not inside graphics or numberform, and not is atom")
+ new_elements = [
+ element_formatters.get(type(element), do_format_element)(
+ element, evaluation, form
+ )
+ for element in expr.elements
+ ]
+ expr_head = expr.head
+ do_format = element_formatters.get(type(expr_head), do_format_element)
+ head = do_format(expr_head, evaluation, form)
+ expr = to_expression_with_specialization(head, *new_elements)
+
+ if include_form:
+ expr = Expression(form, expr)
+ return expr
+ finally:
+ evaluation.dec_recursion_depth()
+
+
+def do_format_rational(
+ element: BaseElement, evaluation: Evaluation, form: Symbol
+) -> BaseElement:
+ if form is SymbolFullForm:
+ return do_format_expression(
+ Expression(
+ Expression(SymbolHoldForm, SymbolRational),
+ element.numerator(),
+ element.denominator(),
+ ),
+ evaluation,
+ form,
+ )
+ else:
+ numerator = element.numerator()
+ minus = numerator.value < 0
+ if minus:
+ numerator = Integer(-numerator.value)
+ result = Expression(SymbolDivide, numerator, element.denominator())
+ if minus:
+ result = Expression(SymbolMinus, result)
+ result = Expression(SymbolHoldForm, result)
+ return do_format_expression(result, evaluation, form)
+
+
+def do_format_complex(
+ element: BaseElement, evaluation: Evaluation, form: Symbol
+) -> BaseElement:
+ if form is SymbolFullForm:
+ return do_format_expression(
+ Expression(
+ Expression(SymbolHoldForm, SymbolComplex), element.real, element.imag
+ ),
+ evaluation,
+ form,
+ )
+
+ parts: typing.List[Any] = []
+ if element.is_machine_precision() or not element.real.is_zero:
+ parts.append(element.real)
+ if element.imag.sameQ(Integer(1)):
+ parts.append(SymbolI)
+ else:
+ parts.append(Expression(SymbolTimes, element.imag, SymbolI))
+
+ if len(parts) == 1:
+ result = parts[0]
+ else:
+ result = Expression(SymbolPlus, *parts)
+
+ return do_format_expression(Expression(SymbolHoldForm, result), evaluation, form)
+
+
+def do_format_expression(
+ element: BaseElement, evaluation: Evaluation, form: Symbol
+) -> BaseElement:
+ # # not sure how much useful is this format_cache
+ # if element._format_cache is None:
+ # element._format_cache = {}
+
+ # last_evaluated_time, expr = element._format_cache.get(form, (None, None))
+ # if last_evaluated_time is not None and expr is not None:
+ # if True
+ # symbolname = expr.get_name()
+ # if symbolname != "":
+ # if not evaluation.definitions.is_uncertain_final_value(
+ # last_evaluated_time, set((symbolname,))
+ # ):
+ # return expr
+ expr = do_format_element(element, evaluation, form)
+ # element._format_cache[form] = (evaluation.definitions.now, expr)
+ return expr
+
+
+element_formatters[Rational] = do_format_rational
+element_formatters[Complex] = do_format_complex
+element_formatters[Expression] = do_format_expression
diff --git a/mathics/core/systemsymbols.py b/mathics/core/systemsymbols.py
index 77db8b83c..b9d9c47fb 100644
--- a/mathics/core/systemsymbols.py
+++ b/mathics/core/systemsymbols.py
@@ -71,6 +71,7 @@
SymbolFailed = Symbol("System`$Failed")
SymbolFloor = Symbol("System`Floor")
SymbolFormat = Symbol("System`Format")
+SymbolFractionBox = Symbol("System`FractionBox")
SymbolFullForm = Symbol("System`FullForm")
SymbolFunction = Symbol("System`Function")
SymbolGamma = Symbol("System`Gamma")
@@ -120,6 +121,7 @@
SymbolNone = Symbol("System`None")
SymbolNorm = Symbol("System`Norm")
SymbolNot = Symbol("System`Not")
+SymbolNumberForm = Symbol("System`NumberForm")
SymbolNumberQ = Symbol("System`NumberQ")
SymbolNumericQ = Symbol("System`NumericQ")
SymbolO = Symbol("System`O")
@@ -131,6 +133,7 @@
SymbolOverflow = Symbol("System`Overflow")
SymbolPackages = Symbol("System`$Packages")
SymbolPattern = Symbol("System`Pattern")
+SymbolPower = Symbol("System`Power")
SymbolPi = Symbol("System`Pi")
SymbolPiecewise = Symbol("System`Piecewise")
SymbolPoint = Symbol("System`Point")
@@ -158,12 +161,16 @@
SymbolSimplify = Symbol("System`Simplify")
SymbolSin = Symbol("System`Sin")
SymbolSlot = Symbol("System`Slot")
+SymbolSqrt = Symbol("System'Sqrt")
+SymbolSqrtBox = Symbol("System`SqrtBox")
SymbolStandardForm = Symbol("System`StandardForm")
SymbolStringForm = Symbol("System`StringForm")
SymbolStringQ = Symbol("System`StringQ")
SymbolStyle = Symbol("System`Style")
SymbolSubsetQ = Symbol("System`SubsetQ")
SymbolSubtract = Symbol("System`Subtract")
+SymbolSubscriptBox = Symbol("System`SubscriptBox")
+SymbolSubsuperscriptBox = Symbol("System`SubsuperscriptBox")
SymbolSuperscriptBox = Symbol("System`SuperscriptBox")
SymbolTable = Symbol("System`Table")
SymbolTeXForm = Symbol("System`TeXForm")
diff --git a/setup.py b/setup.py
index 001479f98..49628bc5c 100644
--- a/setup.py
+++ b/setup.py
@@ -176,6 +176,7 @@ def subdirs(root, file="*.*", depth=10):
"mathics.builtin.drawing",
"mathics.builtin.fileformats",
"mathics.builtin.files_io",
+ "mathics.builtin.forms",
"mathics.builtin.functional",
"mathics.builtin.intfns",
"mathics.builtin.list",