Skip to content

environment

Context

Bases: ABC

Source code in src/finch/symbolic/environment.py
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
class Context(ABC):
    def __init__(self, namespace=None, preamble=None, epilogue=None, mode=None):
        self.namespace = namespace if namespace is not None else Namespace()
        self.preamble = preamble if preamble is not None else []
        self.epilogue = epilogue if epilogue is not None else []
        self.mode = mode if mode is not None else CompilerMode()

    def exec(self, thunk: Any):
        self.preamble.append(thunk)

    def post(self, thunk: Any):
        self.epilogue.append(thunk)

    def freshen(self, *tags):
        return self.namespace.freshen(*tags)

    def resolve(self, *names: str):
        return self.namespace.resolve(*names)

    def block(self):
        """
        Create a new block. Preambles and epilogues will stay within this block.
        """
        blk = self.__class__()
        blk.namespace = self.namespace
        blk.preamble = []
        blk.epilogue = []
        blk.mode = self.mode
        return blk

    @abstractmethod
    def emit(self):
        """
        Emit the code in this context.
        """
        ...

block()

Create a new block. Preambles and epilogues will stay within this block.

Source code in src/finch/symbolic/environment.py
149
150
151
152
153
154
155
156
157
158
def block(self):
    """
    Create a new block. Preambles and epilogues will stay within this block.
    """
    blk = self.__class__()
    blk.namespace = self.namespace
    blk.preamble = []
    blk.epilogue = []
    blk.mode = self.mode
    return blk

emit() abstractmethod

Emit the code in this context.

Source code in src/finch/symbolic/environment.py
160
161
162
163
164
165
@abstractmethod
def emit(self):
    """
    Emit the code in this context.
    """
    ...

NamedTerm

Bases: Term, ABC

A named program node, e.g. a variable, function, or index.

Source code in src/finch/symbolic/environment.py
11
12
13
14
15
16
17
18
class NamedTerm(Term, ABC):
    """
    A named program node, e.g. a variable, function, or index.
    """

    @property
    @abstractmethod
    def symbol(self) -> str: ...

Namespace

Namespace

A namespace for managing variable names and aesthetic fresh variable generation. You can construct a namespace from an existing tree of Terms to avoid name collisions.

Source code in src/finch/symbolic/environment.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
class Namespace:
    """
        Namespace

    A namespace for managing variable names and aesthetic fresh variable
    generation.  You can construct a namespace from an existing tree of `Term`s
    to avoid name collisions.
    """

    def __init__(self, root=None):
        self.counts = defaultdict(int)
        self.resolutions = {}
        if root is not None:
            for node in PostOrderDFS(root):
                if isinstance(node, NamedTerm):
                    self.freshen(node.symbol)

    def freshen(self, *tags) -> str:
        """
        Generate a fresh variable name based on the provided tags.
        """
        name = "_".join(str(tag) for tag in tags)
        m = re.match(r"^(.*)_(\d*)$", name)
        if m is None:
            tag = name
            n = 1
        else:
            tag = m.group(1)
            n = int(m.group(2))
        n = max(self.counts[tag] + 1, n)
        self.counts[tag] = n
        if n == 1:
            return tag
        return f"{tag}_{n}"

    def resolve(self, *names: str):
        """
        Resolve a list of namespaced variable names to a unique name.
        e.g. `resolve("a", "b")` might return `a_b_1` if `a_b` has already been
        used in scope.
        """
        self.resolutions.setdefault(names, lambda: self.freshen(*names))

freshen(*tags)

Generate a fresh variable name based on the provided tags.

Source code in src/finch/symbolic/environment.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
def freshen(self, *tags) -> str:
    """
    Generate a fresh variable name based on the provided tags.
    """
    name = "_".join(str(tag) for tag in tags)
    m = re.match(r"^(.*)_(\d*)$", name)
    if m is None:
        tag = name
        n = 1
    else:
        tag = m.group(1)
        n = int(m.group(2))
    n = max(self.counts[tag] + 1, n)
    self.counts[tag] = n
    if n == 1:
        return tag
    return f"{tag}_{n}"

resolve(*names)

Resolve a list of namespaced variable names to a unique name. e.g. resolve("a", "b") might return a_b_1 if a_b has already been used in scope.

Source code in src/finch/symbolic/environment.py
56
57
58
59
60
61
62
def resolve(self, *names: str):
    """
    Resolve a list of namespaced variable names to a unique name.
    e.g. `resolve("a", "b")` might return `a_b_1` if `a_b` has already been
    used in scope.
    """
    self.resolutions.setdefault(names, lambda: self.freshen(*names))

ScopedDict

Bases: Generic[T]

A dictionary that allows for scoped variable resolution.

Source code in src/finch/symbolic/environment.py
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
class ScopedDict(Generic[T]):
    """
    A dictionary that allows for scoped variable resolution.
    """

    def __init__(
        self,
        bindings: dict[str, T] | None = None,
        parent: Optional["ScopedDict[T]"] = None,
    ):
        if bindings is None:
            bindings = {}
        self.bindings: dict[str, T] = bindings
        self.parent: ScopedDict[T] | None = parent

    def __getitem__(self, key: str) -> T:
        if key in self.bindings:
            return self.bindings[key]
        if self.parent is not None:
            return self.parent[key]
        raise KeyError(f"Key '{key}' not found in scoped dictionary.")

    def set_in_ancestor(self, leaf, key: str, value: T) -> None:
        if key in self.bindings:
            self.bindings[key] = value
        elif self.parent is not None:
            self.parent.set_in_ancestor(leaf, key, value)
        else:
            leaf.bindings[key] = value

    def del_in_ancestor(self, leaf, key: str) -> None:
        if key in self.bindings:
            del self.bindings[key]
        elif self.parent is not None:
            self.parent.del_in_ancestor(leaf, key)
        else:
            del leaf.bindings[key]

    def __setitem__(self, key: str, value: T) -> None:
        self.set_in_ancestor(self, key, value)

    def __contains__(self, key: str) -> bool:
        return key in self.bindings or (self.parent is not None and key in self.parent)

    def __delitem__(self, key: str) -> None:
        self.del_in_ancestor(self, key)

    def scope(self) -> "ScopedDict[T]":
        """
        Create a new scoped dictionary that inherits from this one.
        """
        return ScopedDict(parent=self)

scope()

Create a new scoped dictionary that inherits from this one.

Source code in src/finch/symbolic/environment.py
115
116
117
118
119
def scope(self) -> "ScopedDict[T]":
    """
    Create a new scoped dictionary that inherits from this one.
    """
    return ScopedDict(parent=self)