Skip to content

Commit 5c2ef07

Browse files
committed
add case insensitive symbol lookup
adds feature described in issue #9
1 parent e297bef commit 5c2ef07

2 files changed

Lines changed: 39 additions & 5 deletions

File tree

pylasu/model/naming.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from dataclasses import dataclass, field
2+
from itertools import chain
23
from typing import TypeVar, Generic, Optional, List, Dict
34

45

@@ -33,14 +34,14 @@ def __hash__(self):
3334
def resolved(self):
3435
return self.referred is not None
3536

36-
def resolve(self, scope: 'Scope') -> bool:
37-
self.referred = scope.lookup(symbol_name=self.name)
37+
def resolve(self, scope: 'Scope', case_insensitive: bool = False) -> bool:
38+
self.referred = scope.lookup(symbol_name=self.name, case_insensitive=case_insensitive)
3839
return self.resolved()
3940

4041
def try_to_resolve(self, candidates: List[T], case_insensitive: bool = False) -> bool:
4142
"""
4243
Try to resolve the reference by finding a named element with a matching name.
43-
The name match is performed in a case sensitive or insensitive way depending on the value of caseInsensitive.
44+
The name match is performed in a case sensitive or insensitive way depending on the value of case_insensitive.
4445
"""
4546

4647
def check_name(candidate: T) -> bool:
@@ -60,10 +61,36 @@ class Symbol(PossiblyNamed):
6061
class Scope:
6162
symbols: Dict[str, List[Symbol]] = field(default_factory=list)
6263
parent: Optional['Scope'] = field(default=None)
64+
insensitive_map: Optional[Dict[str, List[str]]] = field(default=None)
65+
66+
def lookup(self, symbol_name: str, symbol_type: type = Symbol, case_insensitive: bool = False) -> Optional[Symbol]:
67+
if case_insensitive:
68+
if self.insensitive_map is None:
69+
self.insensitive_map = {}
70+
for key in self.symbols.keys():
71+
key_lower: str = key.lower()
72+
self.insensitive_map[key_lower] = self.insensitive_map.get(key_lower, []) + [key]
73+
74+
symbol_name_lower: str = symbol_name.lower()
75+
76+
return next(
77+
(
78+
symbol
79+
for symbol in chain.from_iterable(
80+
self.symbols.get(orig_symbol_name, [])
81+
for orig_symbol_name in self.insensitive_map[symbol_name_lower]
82+
)
83+
if isinstance(symbol, symbol_type)
84+
),
85+
self.parent.lookup(symbol_name, symbol_type) if self.parent is not None else None
86+
)
6387

64-
def lookup(self, symbol_name: str, symbol_type: type = Symbol) -> Optional[Symbol]:
6588
return next((symbol for symbol in self.symbols.get(symbol_name, []) if isinstance(symbol, symbol_type)),
6689
self.parent.lookup(symbol_name, symbol_type) if self.parent is not None else None)
6790

6891
def add(self, symbol: Symbol):
6992
self.symbols[symbol.name] = self.symbols.get(symbol.name, []) + [symbol]
93+
94+
if self.insensitive_map is not None:
95+
symbol_name_lower: str = symbol.name.lower()
96+
self.insensitive_map[symbol_name_lower] = self.insensitive_map.get(symbol_name_lower, []) + [symbol.name]

tests/model/test_model.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ def test_scope_lookup_7(self):
136136
result = scope.lookup(symbol_name='a', symbol_type=AnotherSymbol)
137137
self.assertIsNone(result)
138138

139+
def test_scope_case_insensitive_lookup(self):
140+
local_symbol = SomeSymbol(name='a', index=0)
141+
scope = Scope(symbols={'a': [local_symbol]}, parent=Scope(symbols={'a': [SomeSymbol(name='a', index=1)]}))
142+
result = scope.lookup(symbol_name='A', case_insensitive=True)
143+
self.assertEqual(result, local_symbol)
144+
self.assertIsInstance(result, Symbol)
145+
139146
def test_node_properties_meta(self):
140147
pds = [pd for pd in sorted(SomeNode.node_properties, key=lambda x: x.name)]
141148
self.assertEqual(5, len(pds))
@@ -149,4 +156,4 @@ def test_node_properties_meta(self):
149156
self.assertEqual("name", pds[3].name)
150157
self.assertFalse(pds[3].provides_nodes)
151158
self.assertEqual("ref", pds[4].name)
152-
self.assertTrue(pds[4].provides_nodes)
159+
self.assertTrue(pds[4].provides_nodes)

0 commit comments

Comments
 (0)