From c258848447e30e6c23c7205d349a7e994cb06c7b Mon Sep 17 00:00:00 2001 From: Thomas Walker Lynch Date: Fri, 16 Jan 2026 11:45:20 +0000 Subject: [PATCH] adds Symbol.py --- developer/authored/Symbol.py | 153 ++++++++++++++++++ developer/authored/__init__.py | 3 +- .../{ => deprecated}/ObjectRegistry.py | 0 .../{ => deprecated}/ProcessLocalId.py | 0 .../authored/{ => deprecated}/Property.py | 0 .../{ => deprecated}/PropertyManager.py | 0 .../{ => deprecated}/PropertyStore.py | 0 .../authored/{ => deprecated}/SemanticSets.py | 0 developer/authored/{ => deprecated}/Syntax.py | 0 developer/authored/test_SymbolFactory.py | 116 +++++++++++++ document/Epimetheus.html | 6 +- 11 files changed, 274 insertions(+), 4 deletions(-) create mode 100644 developer/authored/Symbol.py rename developer/authored/{ => deprecated}/ObjectRegistry.py (100%) rename developer/authored/{ => deprecated}/ProcessLocalId.py (100%) rename developer/authored/{ => deprecated}/Property.py (100%) rename developer/authored/{ => deprecated}/PropertyManager.py (100%) rename developer/authored/{ => deprecated}/PropertyStore.py (100%) rename developer/authored/{ => deprecated}/SemanticSets.py (100%) rename developer/authored/{ => deprecated}/Syntax.py (100%) create mode 100755 developer/authored/test_SymbolFactory.py diff --git a/developer/authored/Symbol.py b/developer/authored/Symbol.py new file mode 100644 index 0000000..1240480 --- /dev/null +++ b/developer/authored/Symbol.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*- + +""" +Symbol + +The Definition of Identity. + +Architecture: + - Symbol: The namespace and manager (Factory). + - Symbol.Instance: The atomic unit of identity (The Product). + +Constraints: + - Distinctness: Instances are unique. + - Opaque: No internal state leakage. + - Immutable Metadata: Name and Doc can be set exactly once. + - Static Manager: The Symbol class cannot be instantiated. +""" + +from __future__ import annotations + +import weakref +from typing import Optional + + +class Symbol: + """ + The Manager class. + Maintains the global state for symbol metadata and token generation. + """ + + def __new__(cls): + """ + Prevent instantiation. Symbol is a static namespace/factory. + """ + raise TypeError("The Symbol class is a static namespace and cannot be instantiated.") + + # Global registry for metadata. + # We use class-level storage so Instances remain lightweight (no back-ref needed). + _names: weakref.WeakKeyDictionary[Symbol.Instance ,str] = weakref.WeakKeyDictionary() + _docs: weakref.WeakKeyDictionary[Symbol.Instance ,str] = weakref.WeakKeyDictionary() + + # Inclusive bounding: Start at 0 (The Null Token) + _current_token: int = 0 + + class Instance: + """ + The atomic unit of identity. + """ + # __weakref__ required for WeakKeyDictionary keys + __slots__ = ( + '_token' + ,'__weakref__' + ) + + def __init__(self ,token: int): + self._token = token + + def __repr__(self) -> str: + return "" + + def __eq__(self ,other: object) -> bool: + if( isinstance(other ,Symbol.Instance) ): + return self._token == other._token + return NotImplemented + + def __hash__(self) -> int: + return hash(self._token) + + # ---------------------------------------------------------------------- + # Metadata Accessors + # ---------------------------------------------------------------------- + + @property + def name(self) -> Optional[str]: + """ + Returns the name of the symbol, or None if anonymous. + """ + return Symbol._names.get(self) + + @name.setter + def name(self ,value: str): + """ + Sets the name. + Raises RuntimeError if the name has already been set. + """ + if( self in Symbol._names ): + raise RuntimeError(f"Symbol name is immutable. Already set to '{Symbol._names[self]}'.") + Symbol._names[self] = value + + @property + def doc(self) -> str: + """ + Returns the docstring of the symbol, or "" if none. + """ + return Symbol._docs.get(self ,"") + + @doc.setter + def doc(self ,value: str): + """ + Sets the docstring. + Raises RuntimeError if the docstring has already been set. + Ignores empty strings (setting to "" is a no-op). + """ + if( not value ): return + + if( self in Symbol._docs ): + raise RuntimeError("Symbol docstring is immutable. Already set.") + + Symbol._docs[self] = value + + # ------------------------------------------------------------------------ + # Factory Methods + # ------------------------------------------------------------------------ + + @classmethod + def make(cls ,name: Optional[str] = None ,doc: str = "") -> Instance: + """ + Mints a new, distinct Original Symbol Instance. + + Args: + name: Optional name. If provided, it becomes immutable. + doc: Optional docstring. If provided (non-empty), it becomes immutable. + """ + # The Rest: Increment first, so the first public symbol is 1. + cls._current_token += 1 + token = cls._current_token + + instance = cls.Instance(token) + + if( name is not None ): + # Direct injection to bypass the "check if set" logic of the setter + cls._names[instance] = name + + if( doc ): + cls._docs[instance] = doc + + return instance + + # ------------------------------------------------------------------------ + # Static Initialization (The First) + # ------------------------------------------------------------------------ + # We perform this inside the class body using local names. + # This creates the Null Symbol immediately upon class definition. + + # Note: We must use 'Instance' (local scope) not 'Symbol.Instance' + # because 'Symbol' is not yet bound. + null = Instance(0) + _names[null] = "Null" + _docs[null] = "The Null Symbol" + + +# LocalWords: Accessors diff --git a/developer/authored/__init__.py b/developer/authored/__init__.py index ce046ae..dce65e6 100644 --- a/developer/authored/__init__.py +++ b/developer/authored/__init__.py @@ -1 +1,2 @@ -from .PropertyManager import PropertyManager +from .Epimetheus import Epimetheus + diff --git a/developer/authored/ObjectRegistry.py b/developer/authored/deprecated/ObjectRegistry.py similarity index 100% rename from developer/authored/ObjectRegistry.py rename to developer/authored/deprecated/ObjectRegistry.py diff --git a/developer/authored/ProcessLocalId.py b/developer/authored/deprecated/ProcessLocalId.py similarity index 100% rename from developer/authored/ProcessLocalId.py rename to developer/authored/deprecated/ProcessLocalId.py diff --git a/developer/authored/Property.py b/developer/authored/deprecated/Property.py similarity index 100% rename from developer/authored/Property.py rename to developer/authored/deprecated/Property.py diff --git a/developer/authored/PropertyManager.py b/developer/authored/deprecated/PropertyManager.py similarity index 100% rename from developer/authored/PropertyManager.py rename to developer/authored/deprecated/PropertyManager.py diff --git a/developer/authored/PropertyStore.py b/developer/authored/deprecated/PropertyStore.py similarity index 100% rename from developer/authored/PropertyStore.py rename to developer/authored/deprecated/PropertyStore.py diff --git a/developer/authored/SemanticSets.py b/developer/authored/deprecated/SemanticSets.py similarity index 100% rename from developer/authored/SemanticSets.py rename to developer/authored/deprecated/SemanticSets.py diff --git a/developer/authored/Syntax.py b/developer/authored/deprecated/Syntax.py similarity index 100% rename from developer/authored/Syntax.py rename to developer/authored/deprecated/Syntax.py diff --git a/developer/authored/test_SymbolFactory.py b/developer/authored/test_SymbolFactory.py new file mode 100755 index 0000000..8bc21dc --- /dev/null +++ b/developer/authored/test_SymbolFactory.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*- + +""" +Tests for Symbol.Instance constraints. +""" + +import unittest +from Symbol import Symbol + +class TestSymbol(unittest.TestCase): + + def test_null_symbol_exists(self): + """ + Requirement: Symbol.null exists and is Token 0. + """ + null_sym = Symbol.null + self.assertIsNotNone(null_sym) + self.assertEqual(null_sym.name ,"Null") + self.assertEqual(null_sym.doc ,"The Null Symbol") + + def test_distinctness(self): + """ + Requirement: Two distinct originals will always be not equal. + """ + s1 = Symbol.make() + s2 = Symbol.make() + + self.assertNotEqual(s1 ,s2) + self.assertIsNot(s1 ,s2) + self.assertNotEqual(s1 ,Symbol.null) + + def test_name_immutability(self): + """ + Requirement: Name can be set once, but never changed. + """ + # Case 1: Set at creation + s1 = Symbol.make(name="Alpha") + self.assertEqual(s1.name ,"Alpha") + + # Attempt to change + with self.assertRaises(RuntimeError): + s1.name = "Beta" + + # Case 2: Late binding + s2 = Symbol.make() + self.assertIsNone(s2.name) + + s2.name = "Gamma" + self.assertEqual(s2.name ,"Gamma") + + # Attempt to change after late bind + with self.assertRaises(RuntimeError): + s2.name = "Delta" + + def test_doc_immutability(self): + """ + Requirement: Doc can be set once, but never changed. + """ + # Case 1: Set at creation + s1 = Symbol.make(doc="Original Doc") + self.assertEqual(s1.doc ,"Original Doc") + + with self.assertRaises(RuntimeError): + s1.doc = "New Doc" + + # Case 2: Late binding + s2 = Symbol.make() + self.assertEqual(s2.doc ,"") # Default is empty string + + s2.doc = "Late Doc" + self.assertEqual(s2.doc ,"Late Doc") + + with self.assertRaises(RuntimeError): + s2.doc = "Changed Doc" + + def test_doc_empty_string_behavior(self): + """ + Requirement: Setting doc to "" is ignored and does not count as 'setting' it. + """ + s1 = Symbol.make() + + # Setting empty string should be a no-op + s1.doc = "" + self.assertEqual(s1.doc ,"") + + # Should still be able to set it later because "" didn't lock it + s1.doc = "Real Doc" + self.assertEqual(s1.doc ,"Real Doc") + + # NOW it is locked + with self.assertRaises(RuntimeError): + s1.doc = "Trying to change" + + def test_property_access(self): + """ + Requirement: Read/Write via properties. + """ + s = Symbol.make() + s.name = "Velocity" + s.doc = "m/s" + + self.assertEqual(s.name ,"Velocity") + self.assertEqual(s.doc ,"m/s") + self.assertIsInstance(s ,Symbol.Instance) + + def test_opaque_representation(self): + """ + Requirement: repr() reveals no internal token. + """ + s1 = Symbol.make() + self.assertEqual(repr(s1) ,"") + + +if __name__ == '__main__': + unittest.main() diff --git a/document/Epimetheus.html b/document/Epimetheus.html index 5661740..85ee1ad 100644 --- a/document/Epimetheus.html +++ b/document/Epimetheus.html @@ -88,7 +88,7 @@ In Epimetheus, we use a dictionary to give a symbol a name. The names need not meet the requirements imposed on a symbol instance, but it can be confusing when they don't. This is avoided in other symbol systems by implementing symbols over character strings, and using the character string as both the name and the instance.

-

Required Properties

+

Required Properties

Any two, or by transitivity, any number of, symbol instances can be compared for equality, say x == y. A comparison between symbol instances will always return True or False. @@ -119,7 +119,7 @@

-

What is a Symbol?

+

What is a Symbol?

Only symbol instances exist at runtime, so a symbol is never manipulated directly. @@ -205,7 +205,7 @@ Think about it this way: you are sitting in a pub of an inn, and a stranger walks in. Though you know the stranger's current destination, you might not know where he came from. -

Discrete Function

+

Discrete Function

Now consider a tape, say called F, where each cell in F holds a path. As a path is a tape, the structure of F is that of a tape of tapes.

-- 2.20.1