Skip to content

Commit 0f46422

Browse files
authored
Independent FullForm (#1316)
MakeBoxes[FullForm[...],...] for general expressions is now evaluated in an specific function. This is useful to handle the FullForm outside the evaluation process, for example, in debugging, or as a last resource with the standard evaluation process fails.
1 parent 9b48a1f commit 0f46422

4 files changed

Lines changed: 86 additions & 52 deletions

File tree

mathics/builtin/makeboxes.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from mathics.eval.makeboxes import (
1212
eval_generic_makeboxes,
1313
eval_infix,
14+
eval_makeboxes_fullform,
1415
eval_postprefix,
1516
format_element,
1617
parenthesize,
@@ -107,9 +108,13 @@ class MakeBoxes(Builtin):
107108
}
108109
summary_text = "settable low-level translator from expression to display boxes"
109110

111+
def eval_fullform(self, expr, evaluation):
112+
"""MakeBoxes[expr_, FullForm]"""
113+
return eval_makeboxes_fullform(expr, evaluation)
114+
110115
def eval_general(self, expr, f, evaluation):
111116
"""MakeBoxes[expr_,
112-
f:TraditionalForm|StandardForm|OutputForm|InputForm|FullForm]"""
117+
f:TraditionalForm|StandardForm|OutputForm|InputForm]"""
113118
return eval_generic_makeboxes(self, expr, f, evaluation)
114119

115120
def eval_outerprecedenceform(self, expr, precedence, form, evaluation):

mathics/eval/makeboxes/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
_boxed_string,
88
eval_generic_makeboxes,
99
eval_makeboxes,
10+
eval_makeboxes_fullform,
1011
format_element,
1112
int_to_string_shorter_repr,
1213
to_boxes,
@@ -31,6 +32,7 @@
3132
"eval_generic_makeboxes",
3233
"eval_infix",
3334
"eval_makeboxes",
35+
"eval_makeboxes_fullform",
3436
"eval_mathmlform",
3537
"eval_postprefix",
3638
"eval_tableform",

mathics/eval/makeboxes/formatvalues.py

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -182,42 +182,24 @@ def do_format_rational(
182182
) -> Optional[BaseElement]:
183183
if not isinstance(element, Rational):
184184
return None
185-
if form is SymbolFullForm:
186-
return do_format_expression(
187-
Expression(
188-
Expression(SymbolHoldForm, SymbolRational),
189-
element.numerator(),
190-
element.denominator(),
191-
),
192-
evaluation,
193-
form,
194-
)
195-
else:
196-
numerator = element.numerator()
197-
minus = numerator.value < 0
198-
if minus:
199-
numerator = Integer(-numerator.value)
200-
result = Expression(SymbolDivide, numerator, element.denominator())
201-
if minus:
202-
result = Expression(SymbolMinus, result)
203-
result = Expression(SymbolHoldForm, result)
204-
result = do_format_expression(result, evaluation, form)
205-
return result
185+
186+
numerator = element.numerator()
187+
minus = numerator.value < 0
188+
if minus:
189+
numerator = Integer(-numerator.value)
190+
result = Expression(SymbolDivide, numerator, element.denominator())
191+
if minus:
192+
result = Expression(SymbolMinus, result)
193+
result = Expression(SymbolHoldForm, result)
194+
result = do_format_expression(result, evaluation, form)
195+
return result
206196

207197

208198
def do_format_complex(
209199
element: BaseElement, evaluation: Evaluation, form: Symbol
210200
) -> Optional[BaseElement]:
211201
if not isinstance(element, Complex):
212202
return None
213-
if form is SymbolFullForm:
214-
return do_format_expression(
215-
Expression(
216-
Expression(SymbolHoldForm, SymbolComplex), element.real, element.imag
217-
),
218-
evaluation,
219-
form,
220-
)
221203

222204
parts: List[Any] = []
223205
if element.is_machine_precision() or not element.real.is_zero:

mathics/eval/makeboxes/makeboxes.py

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,22 @@
88

99
from typing import Optional, Union
1010

11-
from mathics.core.atoms import String
11+
from mathics.core.atoms import Complex, Integer, Rational, String
1212
from mathics.core.element import BaseElement, BoxElementMixin
1313
from mathics.core.evaluation import Evaluation
1414
from mathics.core.expression import Expression
15-
from mathics.core.symbols import Atom, Symbol, SymbolFullForm, SymbolMakeBoxes
16-
from mathics.core.systemsymbols import SymbolStandardForm
15+
from mathics.core.symbols import (
16+
Atom,
17+
Symbol,
18+
SymbolFullForm,
19+
SymbolList,
20+
SymbolMakeBoxes,
21+
)
22+
from mathics.core.systemsymbols import ( # SymbolRule, SymbolRuleDelayed,
23+
SymbolComplex,
24+
SymbolRational,
25+
SymbolStandardForm,
26+
)
1727
from mathics.eval.makeboxes.formatvalues import do_format
1828
from mathics.eval.makeboxes.precedence import parenthesize
1929

@@ -38,7 +48,7 @@ def to_boxes(x, evaluation: Evaluation, options={}) -> BoxElementMixin:
3848
return x_boxed
3949
if isinstance(x_boxed, Atom):
4050
return to_boxes(x_boxed, evaluation, options)
41-
return eval_makeboxes(Expression(SymbolFullForm, x), evaluation)
51+
return eval_makeboxes_fullform(x, evaluation)
4252

4353

4454
# this temporarily replaces the _BoxedString class
@@ -126,23 +136,58 @@ def int_to_string_shorter_repr(value: int, form: Symbol, max_digits=640):
126136
return String(value_str)
127137

128138

129-
def eval_fullform_makeboxes(
130-
self, expr, evaluation: Evaluation, form=SymbolStandardForm
131-
) -> Optional[BaseElement]:
132-
"""
133-
This function takes the definitions provided by the evaluation
134-
object, and produces a boxed form for expr.
139+
# TODO: evaluation is needed because `atom_to_boxes` uses it. Can we remove this
140+
# argument?
141+
def eval_makeboxes_fullform(
142+
expr: BaseElement, evaluation: Evaluation
143+
) -> BoxElementMixin:
144+
"""Same as MakeBoxes[FullForm[expr_], f_]"""
145+
from mathics.builtin.box.layout import RowBox
135146

136-
Basically: MakeBoxes[expr // FullForm]
137-
"""
138-
# This is going to be reimplemented.
139-
expr = Expression(SymbolFullForm, expr)
140-
return Expression(SymbolMakeBoxes, expr, form).evaluate(evaluation)
147+
if isinstance(expr, BoxElementMixin):
148+
expr = expr.to_expression()
149+
if isinstance(expr, Atom):
150+
if isinstance(expr, Rational):
151+
expr = Expression(SymbolRational, expr.numerator(), expr.denominator())
152+
elif isinstance(expr, Complex):
153+
expr = Expression(SymbolComplex, expr.real, expr.imag)
154+
else:
155+
return expr.atom_to_boxes(SymbolFullForm, evaluation)
156+
head, elements = expr.head, expr.elements
157+
boxed_elements = tuple(
158+
(eval_makeboxes_fullform(element, evaluation) for element in elements)
159+
)
160+
# In some places it would be less verbose to use special outputs for
161+
# `List`, `Rule` and `RuleDelayed`. WMA does not that, but we do it for
162+
# `List`.
163+
#
164+
# if head is SymbolRule and len(elements) == 2:
165+
# return RowBox(boxed_elements[0], String("->"), boxed_elements[1])
166+
# if head is SymbolRuleDelayed and len(elements) == 2:
167+
# return RowBox(boxed_elements[0], String(":>"), boxed_elements[1])
168+
if head is SymbolList:
169+
left, right, sep = (String(ch) for ch in ("{", "}", ","))
170+
result_elements = [left]
171+
else:
172+
left, right, sep = (String(ch) for ch in ("[", "]", ", "))
173+
result_elements = [eval_makeboxes_fullform(head, evaluation), left]
174+
175+
if len(boxed_elements) > 1:
176+
arguments = []
177+
for b_elem in boxed_elements:
178+
if len(arguments) > 0:
179+
arguments.append(sep)
180+
arguments.append(b_elem)
181+
result_elements.append(RowBox(*arguments))
182+
elif len(boxed_elements) == 1:
183+
result_elements.append(boxed_elements[0])
184+
result_elements.append(right)
185+
return RowBox(*result_elements)
141186

142187

143188
def eval_generic_makeboxes(self, expr, f, evaluation):
144189
"""MakeBoxes[expr_,
145-
f:TraditionalForm|StandardForm|OutputForm|InputForm|FullForm]"""
190+
f:TraditionalForm|StandardForm|OutputForm|InputForm]"""
146191
from mathics.builtin.box.layout import RowBox
147192

148193
if isinstance(expr, BoxElementMixin):
@@ -174,7 +219,6 @@ def eval_generic_makeboxes(self, expr, f, evaluation):
174219
if f_name in (
175220
"System`InputForm",
176221
"System`OutputForm",
177-
"System`FullForm",
178222
):
179223
sep = ", "
180224
else:
@@ -210,6 +254,8 @@ def eval_makeboxes(
210254
Basically: MakeBoxes[expr // form]
211255
"""
212256
# This is going to be reimplemented.
257+
if form is SymbolFullForm:
258+
return eval_makeboxes_fullform(expr, evaluation)
213259
return Expression(SymbolMakeBoxes, expr, form).evaluate(evaluation)
214260

215261

@@ -220,14 +266,13 @@ def format_element(
220266
Applies formats associated to the expression, and then calls Makeboxes
221267
"""
222268
evaluation.is_boxing = True
223-
expr = do_format(element, evaluation, form)
224-
if expr is None:
269+
formatted_expr = do_format(element, evaluation, form)
270+
if formatted_expr is None:
225271
return None
226-
result = Expression(SymbolMakeBoxes, expr, form)
227-
result_box = result.evaluate(evaluation)
272+
result_box = eval_makeboxes(formatted_expr, evaluation, form)
228273
if isinstance(result_box, String):
229274
return result_box
230275
if isinstance(result_box, BoxElementMixin):
231276
return result_box
232277
else:
233-
return format_element(element, evaluation, SymbolFullForm, **kwargs)
278+
return eval_makeboxes_fullform(element, evaluation)

0 commit comments

Comments
 (0)