-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
misc: add a ScopedDict util, similar to LLVM's ScopedHashTable #3355
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import pytest | ||
|
||
from xdsl.utils.scoped_dict import ScopedDict | ||
|
||
|
||
def test_simple(): | ||
table = ScopedDict[int, int]() | ||
|
||
table[1] = 2 | ||
|
||
assert table[1] == 2 | ||
|
||
table[2] = 3 | ||
|
||
assert table[2] == 3 | ||
|
||
with pytest.raises(ValueError, match="Cannot overwrite value 3 for key 2"): | ||
table[2] = 4 | ||
|
||
with pytest.raises(KeyError): | ||
table[3] | ||
|
||
inner = ScopedDict(table, name="inner") | ||
|
||
inner[2] = 4 | ||
|
||
assert inner[2] == 4 | ||
assert table[2] == 3 | ||
|
||
inner[3] = 5 | ||
|
||
assert 3 not in table | ||
assert 3 in inner | ||
assert 4 not in inner |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
from __future__ import annotations | ||
|
||
from typing import Generic, TypeVar | ||
|
||
_Key = TypeVar("_Key") | ||
_Value = TypeVar("_Value") | ||
|
||
|
||
class ScopedDict(Generic[_Key, _Value]): | ||
""" | ||
A tiered mapping from keys to values. | ||
Once a value is set for a key, it cannot be overwritten. | ||
A ScopedDict may have a parent dict, which is used as a fallback when a value for a | ||
key is not found. | ||
If a ScopedDict and its parent have values for the same key, the child value will be | ||
returned. | ||
This structure is useful for contexts where keys and values have a known scope, such | ||
as during IR construction from an Abstract Syntax Tree. | ||
ScopedDict instances may have a `name` property as a hint during debugging. | ||
""" | ||
|
||
_local_scope: dict[_Key, _Value] | ||
parent: ScopedDict[_Key, _Value] | None | ||
name: str | None | ||
|
||
def __init__( | ||
self, | ||
parent: ScopedDict[_Key, _Value] | None = None, | ||
*, | ||
name: str | None = None, | ||
) -> None: | ||
self._local_scope = {} | ||
self.parent = parent | ||
self.name = name | ||
|
||
def __getitem__(self, key: _Key) -> _Value: | ||
""" | ||
Fetch key from environment. Attempts to first fetch from current scope, | ||
then from parent scopes. Raises KeyError error if not found. | ||
""" | ||
local = self._local_scope.get(key) | ||
if local is not None: | ||
return local | ||
if self.parent is None: | ||
raise KeyError(f"No value for key {key}") | ||
return self.parent[key] | ||
|
||
def __setitem__(self, key: _Key, value: _Value): | ||
""" | ||
Assign key to current scope. Raises InterpretationError if key already | ||
assigned to. | ||
""" | ||
if key in self._local_scope: | ||
raise ValueError( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I considered it, but I have a feeling that this is not the intended purpose of KeyError, which to me signals that something does not exist for a given key, whereas this error is raised when something does already exist. In either case, I'm not sure what the user can do to recover. |
||
f"Cannot overwrite value {self._local_scope[key]} for key {key}" | ||
) | ||
self._local_scope[key] = value | ||
|
||
def __contains__(self, key: _Key) -> bool: | ||
return ( | ||
key in self._local_scope or self.parent is not None and key in self.parent | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be hashable or something? I'm not sure why pyright doesn't complain here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hashable isn't a real type, AFAIK, and the
dict
generic type has the sameTypeVar
definition