Skip to content

Commit e80d74f

Browse files
authored
Merge pull request #714 from Mathics3/Symbol-init-cleanup-revised
Remove "value" from Symbol; add SymbolConstant...
2 parents 7b4f260 + e0ffe6b commit e80d74f

File tree

4 files changed

+137
-73
lines changed

4 files changed

+137
-73
lines changed

mathics/builtin/makeboxes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ def format_operator(operator) -> Union[String, BaseElement]:
485485
return op
486486
return operator
487487

488-
precedence = prec.value
488+
precedence = prec.value if hasattr(prec, "value") else 0
489489
grouping = grouping.get_name()
490490

491491
if isinstance(expr, Atom):

mathics/core/symbols.py

+105-62
Original file line numberDiff line numberDiff line change
@@ -329,57 +329,78 @@ def replace_slots(self, slots, evaluation) -> "Atom":
329329

330330

331331
class Symbol(Atom, NumericOperators, EvalMixin):
332-
"""
333-
Note: Symbol is right now used in a couple of ways which in the
334-
future may be separated.
332+
"""A Symbol is a kind of Atom that acts as a symbolic variable.
335333
336-
A Symbol is a kind of Atom that acts as a symbolic variable or
337-
symbolic constant.
334+
All Symbols have a name that can be converted to string.
338335
339-
All Symbols have a name that can be converted to string form.
336+
A Variable Symbol is a ``Symbol`` that is associated with a
337+
``Definition`` that has an ``OwnValue`` that determines its
338+
evaluation value.
340339
341-
Inside a session, a Symbol can be associated with a ``Definition``
342-
that determines its evaluation value.
340+
A Function Symbol, like a Variable Symbol, is a ``Symbol`` that is
341+
also associated with a ``Definition``. But it has a ``DownValue``
342+
that is used in its evaluation.
343343
344-
We also have Symbols which are immutable or constant; here the
345-
definitions are fixed. The predefined Symbols ``True``, ``False``,
346-
and ``Null`` are like this.
344+
We also have Symbols which in contrast to Variables Symbols have
345+
a constant value that cannot change. System`True and System`False
346+
are like this.
347347
348-
Also there are situations where the Symbol acts like Python's
349-
intern() built-in function or Lisp's Symbol without its modifyable
350-
property list. Here, the only attribute we care about is the name
351-
which is unique across all mentions and uses, and therefore
352-
needs it only to be stored as a single object in the system.
348+
These however are in class SymbolConstant. See that class for
349+
more information.
353350
354-
Note that the mathics.core.parser.Symbol works exactly this way.
351+
Symbol acts like Python's intern() built-in function or Lisp's
352+
Symbol without its modifyable property list. Here, the only
353+
attribute we care about is the value which is unique across all
354+
mentions and uses, and therefore needs it only to be stored as a
355+
single object in the system.
355356
356-
This aspect may or may not be true for the Symbolic Variable use case too.
357+
Note that the mathics.core.parser.Symbol works exactly this way.
357358
"""
358359

359360
name: str
360361
hash: str
361362
sympy_dummy: Any
362-
defined_symbols = {}
363+
364+
# Dictionary of Symbols defined so far.
365+
# We use this for object uniqueness.
366+
# The key is the Symbol object's string name, and the
367+
# diectionary's value is the Mathics object for the Symbol.
368+
_symbols = {}
369+
363370
class_head_name = "System`Symbol"
364371

365372
# __new__ instead of __init__ is used here because we want
366373
# to return the same object for a given "name" value.
367-
def __new__(cls, name: str, sympy_dummy=None, value=None):
374+
def __new__(cls, name: str, sympy_dummy=None):
368375
"""
369-
Allocate an object ensuring that for a given `name` we get back the same object.
376+
Allocate an object ensuring that for a given ``name`` and ``cls`` we get back the same object,
377+
id(object) is the same and its object.__hash__() is the same.
378+
379+
SymbolConstant's like System`True and System`False set
380+
``value`` to something other than ``None``.
381+
370382
"""
371383
name = ensure_context(name)
372-
self = cls.defined_symbols.get(name, None)
384+
385+
# A lot of the below code is similar to
386+
# the corresponding for numeric constants like Integer, Real.
387+
self = cls._symbols.get(name)
388+
373389
if self is None:
374-
self = super(Symbol, cls).__new__(cls)
390+
self = super().__new__(cls)
375391
self.name = name
376392

393+
# Cache object so we don't allocate again.
394+
cls._symbols[name] = self
395+
377396
# Set a value for self.__hash__() once so that every time
378-
# it is used this is fast.
379-
# This tuple with "Symbol" is used to give a different hash
380-
# than the hash that would be returned if just string name were
381-
# used.
382-
self.hash = hash(("Symbol", name))
397+
# it is used this is fast. Note that in contrast to the
398+
# cached object key, the hash key needs to be unique across *all*
399+
# Python objects, so we include the class in the
400+
# event that different objects have the same Python value.
401+
# For example, this can happen with String constants.
402+
403+
self.hash = hash((cls, name))
383404

384405
# TODO: revise how we convert sympy.Dummy
385406
# symbols.
@@ -392,25 +413,8 @@ def __new__(cls, name: str, sympy_dummy=None, value=None):
392413
# value attribute.
393414
self.sympy_dummy = sympy_dummy
394415

395-
# This is something that still I do not undestand:
396-
# here we are adding another attribute to this class,
397-
# which is not clear where is it going to be used, but
398-
# which can be different to None just three specific instances:
399-
# * ``System`True`` -> True
400-
# * ``System`False`` -> False
401-
# * ``System`Null`` -> None
402-
#
403-
# My guess is that this property should be set for
404-
# ``PredefinedSymbol`` but not for general symbols.
405-
#
406-
# Like it is now, it looks so misterious as
407-
# self.sympy_dummy, for which I have to dig into the
408-
# code to see even what type of value should be expected
409-
# for it.
410-
self._value = value
411416
self._short_name = strip_context(name)
412417

413-
cls.defined_symbols[name] = self
414418
return self
415419

416420
def __eq__(self, other) -> bool:
@@ -631,24 +635,59 @@ def to_sympy(self, **kwargs):
631635
return sympy.Symbol(sympy_symbol_prefix + self.name)
632636
return builtin.to_sympy(self, **kwargs)
633637

634-
@property
635-
def value(self) -> Any:
636-
return self._value
637-
638638

639-
class PredefinedSymbol(Symbol):
639+
class SymbolConstant(Symbol):
640640
"""
641-
A Predefined Symbol of the Mathics system.
641+
A Symbol Constant is Symbol of the Mathics system whose value can't
642+
be changed and has a corresponding Python representation.
642643
643-
A Symbol which is defined because it is used somewhere in the
644-
Mathics system as a built-in name, Attribute, Property, Option,
645-
or a Symbolic Constant.
644+
Therefore, like an ``Integer`` constant such as ``Integer0``, we don't
645+
need to go through ``Definitions`` to get its Python-equivalent value.
646646
647-
In contrast to Symbol where the name might not have been added to
648-
a list of known Symbol names or where the name might get deleted,
649-
this never occurs here.
647+
For example for the ``SymbolConstant`` ``System`True``, has its
648+
value set to the Python ``True`` value.
649+
650+
Note this is not the same thing as a Symbolic Constant like ``Pi``,
651+
which doesn't have an (exact) Python equivalent representation.
652+
Also, Pi *can* be Unprotected and changed, while True, cannot.
653+
654+
Also note that ``SymbolConstant`` differs from ``Symbol`` in that
655+
Symbol has no value field (even when its value happens to be
656+
representable in Python. Symbols need to go through Definitions
657+
get a Symbol's current value, based on the current context and the
658+
state of prior operations on that Symbol/Definition binding.
659+
660+
In sum, SymbolConstant is partly like Symbol, and partly like
661+
Numeric constants.
650662
"""
651663

664+
# Dictionary of SymbolConstants defined so far.
665+
# We use this for object uniqueness.
666+
# The key is the SymbolConstant's value, and the
667+
# diectionary's value is the Mathics object representing that Python value.
668+
_symbol_constants = {}
669+
670+
# We use __new__ here to unsure that two Integer's that have the same value
671+
# return the same object.
672+
def __new__(cls, name, value):
673+
674+
name = ensure_context(name)
675+
self = cls._symbol_constants.get(name)
676+
if self is None:
677+
self = super().__new__(cls, name)
678+
self._value = value
679+
680+
# Cache object so we don't allocate again.
681+
self._symbol_constants[name] = self
682+
683+
# Set a value for self.__hash__() once so that every time
684+
# it is used this is fast. Note that in contrast to the
685+
# cached object key, the hash key needs to be unique across all
686+
# Python objects, so we include the class in the
687+
# event that different objects have the same Python value
688+
self.hash = hash((cls, name))
689+
return self
690+
652691
@property
653692
def is_literal(self) -> bool:
654693
"""
@@ -676,6 +715,10 @@ def is_uncertain_final_definitions(self, definitions) -> bool:
676715
"""
677716
return False
678717

718+
@property
719+
def value(self):
720+
return self._value
721+
679722

680723
def symbol_set(*symbols: Tuple[Symbol]) -> FrozenSet[Symbol]:
681724
"""
@@ -689,10 +732,10 @@ def symbol_set(*symbols: Tuple[Symbol]) -> FrozenSet[Symbol]:
689732

690733
# Symbols used in this module.
691734

692-
# Note, below we are only setting PredefinedSymbol for Symbols which
735+
# Note, below we are only setting SymbolConstant for Symbols which
693736
# are both predefined and have the Locked attribute.
694737

695-
# An experiment using PredefinedSymbol("Pi") in the Python code and
738+
# An experiment using SymbolConstant("Pi") in the Python code and
696739
# running:
697740
# {Pi, Unprotect[Pi];Pi=4; Pi, Pi=.; Pi }
698741
# show that this does not change the output in any way.
@@ -702,9 +745,9 @@ def symbol_set(*symbols: Tuple[Symbol]) -> FrozenSet[Symbol]:
702745
# more of the below and in systemsymbols
703746
# PredefineSymbol.
704747

705-
SymbolFalse = PredefinedSymbol("System`False", value=False)
706-
SymbolList = PredefinedSymbol("System`List")
707-
SymbolTrue = PredefinedSymbol("System`True", value=True)
748+
SymbolFalse = SymbolConstant("System`False", value=False)
749+
SymbolList = SymbolConstant("System`List", value=list)
750+
SymbolTrue = SymbolConstant("System`True", value=True)
708751

709752
SymbolAbs = Symbol("Abs")
710753
SymbolDivide = Symbol("Divide")

mathics/core/systemsymbols.py

+1
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
SymbolRepeated = Symbol("System`Repeated")
174174
SymbolRepeatedNull = Symbol("System`RepeatedNull")
175175
SymbolReturn = Symbol("System`Return")
176+
SymbolRight = Symbol("System`Right")
176177
SymbolRound = Symbol("System`Round")
177178
SymbolRow = Symbol("System`Row")
178179
SymbolRowBox = Symbol("System`RowBox")

mathics/eval/makeboxes.py

+30-10
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88

99
import typing
10-
from typing import Any
10+
from typing import Any, Dict, Type
1111

1212
from mathics.core.atoms import Complex, Integer, Rational, String, SymbolI
1313
from mathics.core.convert.expression import to_expression_with_specialization
@@ -41,6 +41,8 @@
4141
SymbolStandardForm,
4242
)
4343

44+
builtins_precedence: Dict[Symbol, int] = {}
45+
4446
element_formatters = {}
4547

4648

@@ -51,18 +53,36 @@ def _boxed_string(string: str, **options):
5153
return StyleBox(String(string), **options)
5254

5355

54-
def eval_makeboxes(self, expr, evaluation, f=SymbolStandardForm):
56+
def eval_fullform_makeboxes(
57+
self, expr, evaluation: Evaluation, form=SymbolStandardForm
58+
) -> Expression:
5559
"""
56-
This function takes the definitions prodived by the evaluation
60+
This function takes the definitions provided by the evaluation
5761
object, and produces a boxed form for expr.
62+
63+
Basically: MakeBoxes[expr // FullForm]
64+
"""
65+
# This is going to be reimplemented.
66+
expr = Expression(SymbolFullForm, expr)
67+
return Expression(SymbolMakeBoxes, expr, form).evaluate(evaluation)
68+
69+
70+
def eval_makeboxes(
71+
self, expr, evaluation: Evaluation, form=SymbolStandardForm
72+
) -> Expression:
73+
"""
74+
This function takes the definitions provided by the evaluation
75+
object, and produces a boxed fullform for expr.
76+
77+
Basically: MakeBoxes[expr // form]
5878
"""
5979
# This is going to be reimplemented.
60-
return Expression(SymbolMakeBoxes, expr, f).evaluate(evaluation)
80+
return Expression(SymbolMakeBoxes, expr, form).evaluate(evaluation)
6181

6282

6383
def format_element(
6484
element: BaseElement, evaluation: Evaluation, form: Symbol, **kwargs
65-
) -> BaseElement:
85+
) -> Type[BaseElement]:
6686
"""
6787
Applies formats associated to the expression, and then calls Makeboxes
6888
"""
@@ -82,14 +102,14 @@ def format_element(
82102

83103
def do_format(
84104
element: BaseElement, evaluation: Evaluation, form: Symbol
85-
) -> BaseElement:
105+
) -> Type[BaseElement]:
86106
do_format_method = element_formatters.get(type(element), do_format_element)
87107
return do_format_method(element, evaluation, form)
88108

89109

90110
def do_format_element(
91111
element: BaseElement, evaluation: Evaluation, form: Symbol
92-
) -> BaseElement:
112+
) -> Type[BaseElement]:
93113
"""
94114
Applies formats associated to the expression and removes
95115
superfluous enclosing formats.
@@ -207,7 +227,7 @@ def format_expr(expr):
207227

208228
def do_format_rational(
209229
element: BaseElement, evaluation: Evaluation, form: Symbol
210-
) -> BaseElement:
230+
) -> Type[BaseElement]:
211231
if form is SymbolFullForm:
212232
return do_format_expression(
213233
Expression(
@@ -232,7 +252,7 @@ def do_format_rational(
232252

233253
def do_format_complex(
234254
element: BaseElement, evaluation: Evaluation, form: Symbol
235-
) -> BaseElement:
255+
) -> Type[BaseElement]:
236256
if form is SymbolFullForm:
237257
return do_format_expression(
238258
Expression(
@@ -260,7 +280,7 @@ def do_format_complex(
260280

261281
def do_format_expression(
262282
element: BaseElement, evaluation: Evaluation, form: Symbol
263-
) -> BaseElement:
283+
) -> Type[BaseElement]:
264284
# # not sure how much useful is this format_cache
265285
# if element._format_cache is None:
266286
# element._format_cache = {}

0 commit comments

Comments
 (0)