add proxy for the first part and try to develop the second part
This commit is contained in:
@@ -0,0 +1,320 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import operator
|
||||
import weakref
|
||||
from collections import defaultdict
|
||||
from copy import copy
|
||||
|
||||
from .exceptions import ParseException
|
||||
from .infos import LineIndexInfo
|
||||
from .util import asjson, join_lists
|
||||
|
||||
DEFAULT_SEPARATOR = '.'
|
||||
|
||||
|
||||
def join_symtables(tables):
|
||||
def join_namespaces(base, target):
|
||||
base = copy(base)
|
||||
for symbol in target.symbols:
|
||||
name = symbol.name
|
||||
if name not in base or base.duplicates:
|
||||
base.insert(symbol)
|
||||
else:
|
||||
join_namespaces(base[name], symbol)
|
||||
return base
|
||||
|
||||
if not tables:
|
||||
return {}
|
||||
|
||||
result = copy(tables[0])
|
||||
for table in tables[1:]:
|
||||
result = join_namespaces(table, result)
|
||||
return result
|
||||
|
||||
|
||||
class SymbolTableError(ParseException):
|
||||
pass
|
||||
|
||||
|
||||
class Namespace:
|
||||
def __init__(
|
||||
self, ignorecase=False, duplicates=False, separator=DEFAULT_SEPARATOR,
|
||||
):
|
||||
super().__init__()
|
||||
self.ignorecase = ignorecase
|
||||
self.duplicates = duplicates
|
||||
self.separator = separator
|
||||
self._entries = defaultdict(list)
|
||||
|
||||
@property
|
||||
def entries(self):
|
||||
return self._entries
|
||||
|
||||
@property
|
||||
def symbols(self):
|
||||
return join_lists(self._entries.values())
|
||||
|
||||
@property
|
||||
def names(self):
|
||||
return list(self._entries.keys())
|
||||
|
||||
def all_names(self):
|
||||
result = []
|
||||
for name, symbols in self.entries.items():
|
||||
result.append(name)
|
||||
for symbol in symbols:
|
||||
result.extend(symbol.all_names())
|
||||
return result
|
||||
|
||||
def __contains__(self, name):
|
||||
if self.ignorecase:
|
||||
name = name.upper()
|
||||
return name in self.entries
|
||||
|
||||
def __getitem__(self, name):
|
||||
if self.ignorecase:
|
||||
name = name.upper()
|
||||
if self.duplicates:
|
||||
return self.entries.get(name)
|
||||
elif name in self.entries:
|
||||
return self.entries[name][0]
|
||||
else:
|
||||
raise KeyError(name)
|
||||
|
||||
def get(self, name, default=None):
|
||||
return self.entries.get(name, default)
|
||||
|
||||
def insert(self, symbol):
|
||||
assert isinstance(
|
||||
symbol.name, str,
|
||||
), f'"{symbol.name!s}" is not a valid symbol name'
|
||||
name = symbol.name
|
||||
if self.ignorecase:
|
||||
name = name.upper()
|
||||
if name in self._entries and not self.duplicates:
|
||||
raise SymbolTableError(
|
||||
f'Symbol "{symbol.name!s}" already in namespace',
|
||||
)
|
||||
|
||||
self._entries[name].append(symbol)
|
||||
|
||||
def lookup_all(self, qualname, drill=True):
|
||||
if self.ignorecase:
|
||||
qualname = qualname.upper()
|
||||
return self._lookup_drilldown(
|
||||
qualname.split(self.separator), drill=drill,
|
||||
)
|
||||
|
||||
def lookup(self, qualname, drill=True):
|
||||
if self.ignorecase:
|
||||
qualname = qualname.upper()
|
||||
result = self._lookup_drilldown(
|
||||
qualname.split(self.separator), drill=drill, max=1,
|
||||
)
|
||||
return result[0] if result else None
|
||||
|
||||
def _lookup_drilldown(self, namelist, drill=True, max=None):
|
||||
if not namelist:
|
||||
return []
|
||||
|
||||
result = []
|
||||
for symbol in self.symbols:
|
||||
found = symbol._lookup_drilldown(namelist, drill=drill, max=max)
|
||||
result.extend(found)
|
||||
if max and len(result) >= max:
|
||||
break
|
||||
return result
|
||||
|
||||
def resolve(self, qualname):
|
||||
return self.lookup(qualname)
|
||||
|
||||
def filter(self, condition):
|
||||
return functools.reduce(
|
||||
operator.iadd,
|
||||
(symbol.filter(condition) for symbol in self.symbols),
|
||||
[],
|
||||
)
|
||||
|
||||
def filter_first(self, condition):
|
||||
for symbol in self.symbols:
|
||||
result = symbol.filter_first(condition)
|
||||
if result:
|
||||
return result
|
||||
return None
|
||||
|
||||
def all(self):
|
||||
return self.filter(lambda: True)
|
||||
|
||||
def asjson(self):
|
||||
return asjson(self)
|
||||
|
||||
def __repr__(self):
|
||||
return f'{type(self).__name__}[]'
|
||||
|
||||
def __json__(self, seen=None):
|
||||
return {
|
||||
name: asjson(symbols, seen=seen)
|
||||
for name, symbols in self.entries.items()
|
||||
}
|
||||
|
||||
|
||||
class SymbolTable(Namespace):
|
||||
def add_reference(self, qualname, from_node):
|
||||
symbol = self.lookup(qualname)
|
||||
symbol.add_reference(qualname, from_node)
|
||||
|
||||
|
||||
class Symbol(Namespace):
|
||||
def __init__(self, name, node, ignorecase=False, duplicates=False):
|
||||
super().__init__(ignorecase=ignorecase, duplicates=duplicates)
|
||||
if not isinstance(name, str):
|
||||
raise TypeError(f'"{name}" is not a valid symbol name')
|
||||
self.name = name
|
||||
self._node = node
|
||||
self._parent = None
|
||||
self._references = []
|
||||
|
||||
@property
|
||||
def node(self):
|
||||
return self._node
|
||||
|
||||
@property
|
||||
def line(self):
|
||||
return self.node.line
|
||||
|
||||
@property
|
||||
def endline(self):
|
||||
return self.node.endline
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
if self._parent is not None:
|
||||
return self._parent()
|
||||
return None
|
||||
|
||||
@property
|
||||
def references(self):
|
||||
return self._references
|
||||
|
||||
def insert(self, symbol):
|
||||
super().insert(symbol)
|
||||
symbol._parent = weakref.ref(self)
|
||||
|
||||
def qualpath(self):
|
||||
if self.parent:
|
||||
return [*self.parent.qualpath(), self.name]
|
||||
return [self.name]
|
||||
|
||||
def qualname(self, sep=DEFAULT_SEPARATOR):
|
||||
return sep.join(self.qualpath())
|
||||
|
||||
def _lookup_drilldown(self, namelist, drill=True, max=None):
|
||||
if self.ignorecase and [self.name.upper()] == namelist:
|
||||
return [self]
|
||||
elif not self.ignorecase and [self.name] == namelist:
|
||||
return [self]
|
||||
elif self.name == namelist[0]:
|
||||
return super()._lookup_drilldown(
|
||||
namelist[1:], drill=drill, max=max,
|
||||
)
|
||||
elif drill:
|
||||
return super()._lookup_drilldown(namelist, drill=drill, max=max)
|
||||
else:
|
||||
return []
|
||||
|
||||
def resolve(self, qualname):
|
||||
return (
|
||||
self.lookup(qualname) or
|
||||
(self.parent and self.parent.resolve(qualname))
|
||||
)
|
||||
|
||||
def filter(self, condition):
|
||||
this_case = [self] if condition(self) else []
|
||||
return this_case + super().filter(condition)
|
||||
|
||||
def filter_first(self, condition):
|
||||
if condition(self):
|
||||
return self
|
||||
return super().filter(condition)
|
||||
|
||||
def add_reference(self, qualname, node):
|
||||
# reference = SymbolReference(self, qualname, node)
|
||||
reference = node
|
||||
if reference not in self.references:
|
||||
self._references.append(reference)
|
||||
|
||||
def line_index(self, include_entries=False, include_references=False):
|
||||
result = set(self.node.line_index())
|
||||
|
||||
if include_references:
|
||||
result.update(self.reference_line_index())
|
||||
|
||||
if include_entries:
|
||||
for s in self.symbols:
|
||||
index = s.line_index(
|
||||
include_entries=include_entries,
|
||||
include_references=include_references,
|
||||
)
|
||||
result.update(index)
|
||||
assert isinstance(result, set)
|
||||
assert all(isinstance(i, LineIndexInfo) for i in result)
|
||||
return sorted(result)
|
||||
|
||||
def reference_line_index(self):
|
||||
result = set()
|
||||
for r in self.references:
|
||||
result.update(r.line_index())
|
||||
assert isinstance(result, set)
|
||||
assert all(isinstance(i, LineIndexInfo) for i in result)
|
||||
return result
|
||||
|
||||
def __repr__(self):
|
||||
return f'{self.name}[]'
|
||||
|
||||
def __json__(self, seen=None):
|
||||
return dict(
|
||||
('node', type(self.node).__name__),
|
||||
('entries', super().__json__(seen=seen)),
|
||||
('references', asjson(self._references, seen=seen)),
|
||||
)
|
||||
|
||||
def __getstate__(self):
|
||||
state = self.__dict__.copy()
|
||||
state.update(_parent=self.parent)
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.__dict__.update(state)
|
||||
if self._parent is not None:
|
||||
self._parent = weakref.ref(self._parent)
|
||||
|
||||
|
||||
class BasedSymbol(Namespace):
|
||||
def __init__(self, name, node, duplicates=False):
|
||||
super().__init__(duplicates=duplicates)
|
||||
self._bases = []
|
||||
|
||||
@property
|
||||
def bases(self):
|
||||
return self._bases
|
||||
|
||||
def add_base(self, base):
|
||||
assert isinstance(base, Symbol)
|
||||
self._bases.append(base)
|
||||
|
||||
def _lookup_drilldown(self, namelist, drill=True, max=max):
|
||||
result = super()._lookup_drilldown(namelist, drill=drill, max=max)
|
||||
if result:
|
||||
return result
|
||||
|
||||
for base in self.bases:
|
||||
result = base._lookup_drilldown(namelist, drill=drill, max=max)
|
||||
if result:
|
||||
return result
|
||||
return result
|
||||
|
||||
def __json__(self, seen=None):
|
||||
result = super().__json__(seen=seen)
|
||||
result['bases'] = asjson([b.qualname() for b in self.bases], seen=seen)
|
||||
return result
|
||||
Reference in New Issue
Block a user