Skip to content

Commit 65cf63c

Browse files
committed
WIP - separate PredefinedSymbol from Symbol ..
PredefinedSymbol acts more like a numeric constant.
1 parent 16cb058 commit 65cf63c

File tree

2 files changed

+88
-56
lines changed

2 files changed

+88
-56
lines changed

mathics/builtin/makeboxes.py

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

473+
# FIXME: prec is sometimes not an Integer for a ListExpession.
474+
# And this is recent with respect to PredefinedSymbol revision.
475+
# How does this get set and why?
473476
def eval_infix(
474477
self, expr, operator, prec: Integer, grouping, form: Symbol, evaluation
475478
):
476479
"""MakeBoxes[Infix[expr_, operator_, prec_:None, grouping_:None],
477480
form:StandardForm|TraditionalForm|OutputForm|InputForm]"""
478481

482+
assert isinstance(prec, Integer)
483+
479484
## FIXME: this should go into a some formatter.
480485
def format_operator(operator) -> Union[String, BaseElement]:
481486
"""

mathics/core/symbols.py

Lines changed: 83 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -333,56 +333,73 @@ def replace_slots(self, slots, evaluation) -> "Atom":
333333

334334
class Symbol(Atom, NumericOperators, EvalMixin):
335335
"""
336-
Note: Symbol is right now used in a couple of ways which in the
337-
future may be separated.
336+
A Symbol is a kind of Atom that acts as a symbolic variable.
338337
339-
A Symbol is a kind of Atom that acts as a symbolic variable or
340-
symbolic constant.
338+
All Symbols have a name that can be converted to string.
341339
342-
All Symbols have a name that can be converted to string form.
343-
344-
Inside a session, a Symbol can be associated with a ``Definition``
340+
A Variable Symbol is a ``Symbol``` that is associated with a ``Definition``
345341
that determines its evaluation value.
346342
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.
343+
We also have Symbols which in contrast to Variables Symbols have
344+
a constant value that cannot change. System`True and System`False
345+
are like this.
350346
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.
347+
These however are in class PredefinedSymbol. See that class for
348+
more information.
356349
357-
Note that the mathics.core.parser.Symbol works exactly this way.
350+
Symbol acts like Python's intern() built-in function or Lisp's
351+
Symbol without its modifyable property list. Here, the only
352+
attribute we care about is the value which is unique across all
353+
mentions and uses, and therefore needs it only to be stored as a
354+
single object in the system.
358355
359-
This aspect may or may not be true for the Symbolic Variable use case too.
356+
Note that the mathics.core.parser.Symbol works exactly this way.
360357
"""
361358

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

368371
# __new__ instead of __init__ is used here because we want
369372
# to return the same object for a given "name" value.
370-
def __new__(cls, name: str, sympy_dummy=None, value=None):
373+
def __new__(cls, name: str, sympy_dummy=None):
371374
"""
372-
Allocate an object ensuring that for a given `name` we get back the same object.
375+
Allocate an object ensuring that for a given ``name`` and ``cls`` we get back the same object,
376+
id(object) is the same and its object.__hash__() is the same.
377+
378+
PredefinedSymbol's like System`True and System`False set
379+
``value`` to something other than ``None``.
380+
373381
"""
374382
name = ensure_context(name)
375-
self = cls.defined_symbols.get(name, None)
383+
384+
# A lot of the below code is similar to
385+
# the corresponding for numeric constants like Integer, Real.
386+
self = cls._symbols.get(name)
387+
376388
if self is None:
377-
self = super(Symbol, cls).__new__(cls)
389+
self = super().__new__(cls)
378390
self.name = name
379391

392+
# Cache object so we don't allocate again.
393+
cls._symbols[name] = self
394+
380395
# 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))
396+
# it is used this is fast. Note that in contrast to the
397+
# cached object key, the hash key needs to be unique across *all*
398+
# Python objects, so we include the class in the
399+
# event that different objects have the same Python value.
400+
# For example, this can happen with String constants.
401+
402+
self.hash = hash((cls, name))
386403

387404
# TODO: revise how we convert sympy.Dummy
388405
# symbols.
@@ -395,25 +412,8 @@ def __new__(cls, name: str, sympy_dummy=None, value=None):
395412
# value attribute.
396413
self.sympy_dummy = sympy_dummy
397414

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
414415
self._short_name = strip_context(name)
415416

416-
cls.defined_symbols[name] = self
417417
return self
418418

419419
def __eq__(self, other) -> bool:
@@ -634,24 +634,47 @@ def to_sympy(self, **kwargs):
634634
return sympy.Symbol(sympy_symbol_prefix + self.name)
635635
return builtin.to_sympy(self, **kwargs)
636636

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

642638
class PredefinedSymbol(Symbol):
643639
"""
644-
A Predefined Symbol of the Mathics system.
640+
A Predefined Symbol is a Constant Symbol of the Mathics system whose value can't
641+
be changed.
645642
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.
643+
Therefore, like say, an Integer constant, we don't need to go
644+
through Definitions to get its value.
649645
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.
646+
As we do in numeric constants, a PredefinedSymbols Python-equivalent value not its string name
647+
but to its Python-equivalent value. For example for the predefined System`True we
648+
we can set its value to the Python ``True`` value.
653649
"""
654650

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

705+
@property
706+
def value(self):
707+
return self._value
708+
682709

683710
def symbol_set(*symbols: Tuple[Symbol]) -> FrozenSet[Symbol]:
684711
"""
@@ -706,7 +733,7 @@ def symbol_set(*symbols: Tuple[Symbol]) -> FrozenSet[Symbol]:
706733
# PredefineSymbol.
707734

708735
SymbolFalse = PredefinedSymbol("System`False", value=False)
709-
SymbolList = PredefinedSymbol("System`List")
736+
SymbolList = PredefinedSymbol("System`List", value=list)
710737
SymbolTrue = PredefinedSymbol("System`True", value=True)
711738

712739
SymbolAbs = Symbol("Abs")

0 commit comments

Comments
 (0)