add proxy for the first part and try to develop the second part

This commit is contained in:
Pierre Geeraert
2025-12-03 12:08:56 +00:00
parent 6c34d510e5
commit 460f18d85a
1095 changed files with 358799 additions and 5 deletions
@@ -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