1+ from typing import Callable
2+
13import mathics .core .definitions as definitions
24
35from mathics .builtin .base import Builtin
6+ from mathics .core .element import BaseElement
47from mathics .core .symbols import Symbol
58
9+ form_symbol_to_class = {}
10+
611
712class FormBaseClass (Builtin ):
813 """
@@ -11,6 +16,9 @@ class FormBaseClass(Builtin):
1116 All Forms should subclass this.
1217 """
1318
19+ # Mapping from Form and element type to a callable boxing method
20+ form_box_methods = {}
21+
1422 # Using "__new__" is not optimal for what we want.
1523 # We basically want to hook into class construction in order to
1624 # detect certain class attributes so we can add them to a list.
@@ -29,8 +37,60 @@ def __new__(cls, *args, **kwargs):
2937 if name in definitions .OutputForms :
3038 raise RuntimeError (f"{ name } already added to $OutputsForms" )
3139 definitions .OutputForms .add (Symbol (name ))
40+ form_symbol_to_class [Symbol (name )] = cls
3241 return instance
3342
43+ @classmethod
44+ def box (cls , element : BaseElement , evaluation ):
45+ """
46+ This method is is called for each element that can be boxed.
47+ ("box" here is a an active verb, not a noun).
48+
49+ This is a generic routine which calls the specific boxing routine
50+ that has been regstered in class variable ``form_box_methods`` previously.
51+
52+ If nothing has been registered, we just return ``element`` back unmodified
53+ as we do in evaluation.
54+
55+ Specific and custom method need to be implemented for each Form
56+ and element_type that perform some kind of boxing.
57+ """
58+ method = cls .form_box_methods .get ((cls , element .head ), None )
59+ if method is None :
60+ # The default class should have been registered under FormBaseClass
61+ method = cls .form_box_methods .get ((FormBaseClass , element .head ), None )
62+ if method is None :
63+ # Just return the element unmodified.
64+ # Note: this is what we do in evaluation when we don't have a match
65+ return element
66+
67+ return method (element , evaluation )
68+
69+ @classmethod
70+ def register_box_method (cls , symbol : Symbol , method : Callable ):
71+ """
72+ Register ``method`` so method(element, ...) is called when
73+ ``form.box(element, ...)`` is called.
74+
75+ "form" is something like ``StandardForm``, ``TraditionalForm``, etc.
76+
77+ To register the default boxing routine, register under the class
78+ ``FormBaseClass``
79+ """
80+
81+ cls .form_box_methods [cls , symbol ] = method
82+
83+
84+ def box (element : BaseElement , evaluation , form : Symbol ):
85+ """
86+ Basically redirects the "box" call from a form symbol name to the "box" method off of
87+ the Form class named by the symbol.
88+ """
89+ form_class = form_symbol_to_class .get (form , None )
90+ if form_class is None :
91+ return element
92+ return form_class .box (element , evaluation )
93+
3494
3595# FormBaseClass is a public Builtin class that
3696# should not get added as a definition (and therefore not added to
0 commit comments