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/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/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..b516e0b06
--- /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.formatter import format_element
+from mathics.core.list import ListExpression
+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/makeboxes.py b/mathics/builtin/makeboxes.py
index 290eb2e1f..bc8ec6bbc 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,
)
@@ -29,36 +32,20 @@
from mathics.core.expression import Expression
from mathics.core.formatter import format_element
from mathics.core.list import ListExpression
-from mathics.core.number import (
- dps,
- convert_base,
- machine_precision,
- reconstruct_digits,
-)
+from mathics.core.number import dps
+
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 +572,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/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/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",