Skip to content

Commit f984e8d

Browse files
committed
Merge branch 'Symbol-init-cleanup' into HEAD
2 parents f9aa60c + 20a1aa0 commit f984e8d

File tree

2 files changed

+93
-57
lines changed

2 files changed

+93
-57
lines changed

mathics/builtin/makeboxes.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,12 +451,17 @@ def eval_postprefix(self, p, expr, h, prec, f, evaluation):
451451
else:
452452
return MakeBoxes(expr, f).evaluate(evaluation)
453453

454+
# FIXME: prec is sometimes not an Integer for a ListExpession.
455+
# And this is recent with respect to PredefinedSymbol revision.
456+
# How does this get set and why?
454457
def eval_infix(
455458
self, expr, operator, prec: Integer, grouping, form: Symbol, evaluation
456459
):
457460
"""MakeBoxes[Infix[expr_, operator_, prec_:None, grouping_:None],
458461
form:StandardForm|TraditionalForm|OutputForm|InputForm]"""
459462

463+
assert isinstance(prec, Integer)
464+
460465
## FIXME: this should go into a some formatter.
461466
def format_operator(operator) -> Union[String, BaseElement]:
462467
"""

mathics/core/symbols.py

Lines changed: 88 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -332,57 +332,78 @@ def replace_slots(self, slots, evaluation) -> "Atom":
332332

333333

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

362363
name: str
363364
hash: str
364365
sympy_dummy: Any
365-
defined_symbols = {}
366+
367+
# Dictionary of Symbols defined so far.
368+
# We use this for object uniqueness.
369+
# The key is the Symbol object's string name, and the
370+
# diectionary's value is the Mathics object for the Symbol.
371+
_symbols = {}
372+
366373
class_head_name = "System`Symbol"
367374

368375
# __new__ instead of __init__ is used here because we want
369376
# to return the same object for a given "name" value.
370-
def __new__(cls, name: str, sympy_dummy=None, value=None):
377+
def __new__(cls, name: str, sympy_dummy=None):
371378
"""
372-
Allocate an object ensuring that for a given `name` we get back the same object.
379+
Allocate an object ensuring that for a given ``name`` and ``cls`` we get back the same object,
380+
id(object) is the same and its object.__hash__() is the same.
381+
382+
PredefinedSymbol's like System`True and System`False set
383+
``value`` to something other than ``None``.
384+
373385
"""
374386
name = ensure_context(name)
375-
self = cls.defined_symbols.get(name, None)
387+
388+
# A lot of the below code is similar to
389+
# the corresponding for numeric constants like Integer, Real.
390+
self = cls._symbols.get(name)
391+
376392
if self is None:
377-
self = super(Symbol, cls).__new__(cls)
393+
self = super().__new__(cls)
378394
self.name = name
379395

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

387408
# TODO: revise how we convert sympy.Dummy
388409
# symbols.
@@ -395,25 +416,8 @@ def __new__(cls, name: str, sympy_dummy=None, value=None):
395416
# value attribute.
396417
self.sympy_dummy = sympy_dummy
397418

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

416-
cls.defined_symbols[name] = self
417421
return self
418422

419423
def __eq__(self, other) -> bool:
@@ -634,24 +638,47 @@ def to_sympy(self, **kwargs):
634638
return sympy.Symbol(sympy_symbol_prefix + self.name)
635639
return builtin.to_sympy(self, **kwargs)
636640

637-
@property
638-
def value(self) -> Any:
639-
return self._value
640-
641641

642642
class PredefinedSymbol(Symbol):
643643
"""
644-
A Predefined Symbol of the Mathics system.
644+
A Predefined Symbol is a Constant Symbol of the Mathics system whose value can't
645+
be changed.
645646
646-
A Symbol which is defined because it is used somewhere in the
647-
Mathics system as a built-in name, Attribute, Property, Option,
648-
or a Symbolic Constant.
647+
Therefore, like say, an Integer constant, we don't need to go
648+
through Definitions to get its value.
649649
650-
In contrast to Symbol where the name might not have been added to
651-
a list of known Symbol names or where the name might get deleted,
652-
this never occurs here.
650+
As we do in numeric constants, a PredefinedSymbols Python-equivalent value not its string name
651+
but to its Python-equivalent value. For example for the predefined System`True we
652+
we can set its value to the Python ``True`` value.
653653
"""
654654

655+
# Dictionary of PredefinedSymbols defined so far.
656+
# We use this for object uniqueness.
657+
# The key is the PredefinedSymbol object's name, and the
658+
# diectionary's value is the Mathics object representing that Python value.
659+
_predefined_symbols = {}
660+
661+
# We use __new__ here to unsure that two Integer's that have the same value
662+
# return the same object.
663+
def __new__(cls, name, value):
664+
665+
name = ensure_context(name)
666+
self = cls._predefined_symbols.get(name)
667+
if self is None:
668+
self = super().__new__(cls, name)
669+
self._value = value
670+
671+
# Cache object so we don't allocate again.
672+
self._predefined_symbols[name] = self
673+
674+
# Set a value for self.__hash__() once so that every time
675+
# it is used this is fast. Note that in contrast to the
676+
# cached object key, the hash key needs to be unique across all
677+
# Python objects, so we include the class in the
678+
# event that different objects have the same Python value
679+
self.hash = hash((cls, name))
680+
return self
681+
655682
@property
656683
def is_literal(self) -> bool:
657684
"""
@@ -679,6 +706,10 @@ def is_uncertain_final_definitions(self, definitions) -> bool:
679706
"""
680707
return False
681708

709+
@property
710+
def value(self):
711+
return self._value
712+
682713

683714
def symbol_set(*symbols: Tuple[Symbol]) -> FrozenSet[Symbol]:
684715
"""
@@ -706,7 +737,7 @@ def symbol_set(*symbols: Tuple[Symbol]) -> FrozenSet[Symbol]:
706737
# PredefineSymbol.
707738

708739
SymbolFalse = PredefinedSymbol("System`False", value=False)
709-
SymbolList = PredefinedSymbol("System`List")
740+
SymbolList = PredefinedSymbol("System`List", value=list)
710741
SymbolTrue = PredefinedSymbol("System`True", value=True)
711742

712743
SymbolAbs = Symbol("Abs")

0 commit comments

Comments
 (0)