148 lines
5.8 KiB
Python
148 lines
5.8 KiB
Python
# This file is part of PeachPy package and is licensed under the Simplified BSD license.
|
|
# See license.rst for the full text of the license.
|
|
|
|
import six
|
|
|
|
|
|
class Name:
|
|
def __init__(self, name=None, prename=None):
|
|
assert name is None or isinstance(name, str)
|
|
assert prename is None or isinstance(prename, str)
|
|
assert name is None or prename is None, \
|
|
"Either name or prename, but not both, can be specified"
|
|
self.name = name
|
|
self.prename = prename
|
|
|
|
def __str__(self):
|
|
if self.name is not None:
|
|
return self.name
|
|
elif self.prename is not None:
|
|
return "<" + self.prename + ">"
|
|
else:
|
|
return "<?>"
|
|
|
|
def __repr__(self):
|
|
return str(self)
|
|
|
|
def __hash__(self):
|
|
return hash(self.name) ^ id(self)
|
|
|
|
def __eq__(self, other):
|
|
return isinstance(other, Name) and (self is other or self.name == other.name)
|
|
|
|
def __ne__(self, other):
|
|
return not isinstance(other, Name) or (self is not other and self.name != other.name)
|
|
|
|
@staticmethod
|
|
def check_name(name):
|
|
"""Verifies that the name is appropriate for a symbol"""
|
|
if not isinstance(name, str):
|
|
raise TypeError("Invalid name %s: string required" % str(name))
|
|
import re
|
|
if not re.match("^[_a-zA-Z]\\w*$", name):
|
|
raise ValueError("Invalid name: " + name)
|
|
if name.startswith("__"):
|
|
raise ValueError("Invalid name %s: names starting with __ are reserved for PeachPy purposes" % name)
|
|
|
|
|
|
class Namespace:
|
|
def __init__(self, scope_name):
|
|
assert scope_name is None or isinstance(scope_name, Name)
|
|
# Name of the namespace.
|
|
self.scope_name = scope_name
|
|
# Map from name string to either Name or Namespace object
|
|
self.names = dict()
|
|
# Map from prename string to a set of Name and Namespace objects
|
|
self.prenames = dict()
|
|
|
|
def __str__(self):
|
|
return str(self.scope_name)
|
|
|
|
def __hash__(self):
|
|
return hash(self.scope_name)
|
|
|
|
def __eq__(self, other):
|
|
return isinstance(other, Namespace) and self.scope_name == other.scope_name
|
|
|
|
def __ne__(self, other):
|
|
return not isinstance(other, Namespace) or self.scope_name != other.scope_name
|
|
|
|
def add_scoped_name(self, scoped_name):
|
|
assert isinstance(scoped_name, tuple)
|
|
scope_name, subscoped_name = scoped_name[0], scoped_name[1:]
|
|
assert isinstance(scope_name, Name)
|
|
scope = scope_name
|
|
if subscoped_name:
|
|
scope = Namespace(scope_name)
|
|
scope.add_scoped_name(subscoped_name)
|
|
if scope_name.name:
|
|
assert scope_name.prename is None
|
|
if scope_name.name in self.names:
|
|
if subscoped_name and isinstance(self.names[scope_name.name], Namespace):
|
|
self.names[scope_name.name].add_scoped_name(subscoped_name)
|
|
else:
|
|
raise ValueError("Name %s already exists" % scope_name.name)
|
|
else:
|
|
self.names[scope_name.name] = scope
|
|
else:
|
|
assert scope_name.name is None
|
|
self.prenames.setdefault(scope_name.prename, set())
|
|
if subscoped_name:
|
|
for subscope in iter(self.prenames[scope_name.prename]):
|
|
if isinstance(subscope, Namespace) and subscope.scope_name is scope_name:
|
|
subscope.add_scoped_name(subscoped_name)
|
|
return
|
|
self.prenames[scope_name.prename].add(scope)
|
|
|
|
def assign_names(self):
|
|
# Step 1: assign names to symbols with prenames with no conflicts
|
|
for prename in six.iterkeys(self.prenames):
|
|
if prename is not None:
|
|
if len(self.prenames[prename]) == 1 and prename not in self.names:
|
|
name_object = next(iter(self.prenames[prename]))
|
|
self.names[prename] = name_object
|
|
if isinstance(name_object, Namespace):
|
|
name_object = name_object.scope_name
|
|
name_object.name = prename
|
|
|
|
# Step 2: assign names to symbols with conflicting prenames
|
|
for prename, prename_objects in six.iteritems(self.prenames):
|
|
if prename is not None:
|
|
suffix = 0
|
|
suffixed_name = prename + str(suffix)
|
|
for name_object in iter(prename_objects):
|
|
# Check that the name wasn't already assigned at Step 1
|
|
if name_object.name is None:
|
|
# Generate a non-conflicting name by appending a suffix
|
|
while suffixed_name in self.names:
|
|
suffix += 1
|
|
suffixed_name = prename + str(suffix)
|
|
self.names[suffixed_name] = name_object
|
|
if isinstance(name_object, Namespace):
|
|
name_object = name_object.scope_name
|
|
name_object.name = suffixed_name
|
|
|
|
# Step 3: assign names to symbols without prenames
|
|
if None in self.prenames:
|
|
unnamed_objects = self.prenames[None]
|
|
suffix = 0
|
|
suffixed_name = "__local" + str(suffix)
|
|
for name_object in iter(unnamed_objects):
|
|
# Generate a non-conflicting name by appending a suffix
|
|
while suffixed_name in self.names:
|
|
suffix += 1
|
|
suffixed_name = "__local" + str(suffix)
|
|
self.names[suffixed_name] = name_object
|
|
if isinstance(name_object, Namespace):
|
|
name_object = name_object.scope_name
|
|
name_object.name = suffixed_name
|
|
pass
|
|
|
|
@property
|
|
def name(self):
|
|
return self.scope_name.name
|
|
|
|
@property
|
|
def prename(self):
|
|
return self.scope_name.prename
|