guppylang-internals 0.21.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- guppylang_internals/__init__.py +3 -0
- guppylang_internals/ast_util.py +350 -0
- guppylang_internals/cfg/__init__.py +0 -0
- guppylang_internals/cfg/analysis.py +230 -0
- guppylang_internals/cfg/bb.py +221 -0
- guppylang_internals/cfg/builder.py +606 -0
- guppylang_internals/cfg/cfg.py +117 -0
- guppylang_internals/checker/__init__.py +0 -0
- guppylang_internals/checker/cfg_checker.py +388 -0
- guppylang_internals/checker/core.py +550 -0
- guppylang_internals/checker/errors/__init__.py +0 -0
- guppylang_internals/checker/errors/comptime_errors.py +106 -0
- guppylang_internals/checker/errors/generic.py +45 -0
- guppylang_internals/checker/errors/linearity.py +300 -0
- guppylang_internals/checker/errors/type_errors.py +344 -0
- guppylang_internals/checker/errors/wasm.py +34 -0
- guppylang_internals/checker/expr_checker.py +1413 -0
- guppylang_internals/checker/func_checker.py +269 -0
- guppylang_internals/checker/linearity_checker.py +821 -0
- guppylang_internals/checker/stmt_checker.py +447 -0
- guppylang_internals/compiler/__init__.py +0 -0
- guppylang_internals/compiler/cfg_compiler.py +233 -0
- guppylang_internals/compiler/core.py +613 -0
- guppylang_internals/compiler/expr_compiler.py +989 -0
- guppylang_internals/compiler/func_compiler.py +97 -0
- guppylang_internals/compiler/hugr_extension.py +224 -0
- guppylang_internals/compiler/qtm_platform_extension.py +0 -0
- guppylang_internals/compiler/stmt_compiler.py +212 -0
- guppylang_internals/decorator.py +246 -0
- guppylang_internals/definition/__init__.py +0 -0
- guppylang_internals/definition/common.py +214 -0
- guppylang_internals/definition/const.py +74 -0
- guppylang_internals/definition/custom.py +492 -0
- guppylang_internals/definition/declaration.py +171 -0
- guppylang_internals/definition/extern.py +89 -0
- guppylang_internals/definition/function.py +302 -0
- guppylang_internals/definition/overloaded.py +150 -0
- guppylang_internals/definition/parameter.py +82 -0
- guppylang_internals/definition/pytket_circuits.py +405 -0
- guppylang_internals/definition/struct.py +392 -0
- guppylang_internals/definition/traced.py +151 -0
- guppylang_internals/definition/ty.py +51 -0
- guppylang_internals/definition/value.py +115 -0
- guppylang_internals/definition/wasm.py +61 -0
- guppylang_internals/diagnostic.py +523 -0
- guppylang_internals/dummy_decorator.py +76 -0
- guppylang_internals/engine.py +295 -0
- guppylang_internals/error.py +107 -0
- guppylang_internals/experimental.py +92 -0
- guppylang_internals/ipython_inspect.py +28 -0
- guppylang_internals/nodes.py +427 -0
- guppylang_internals/py.typed +0 -0
- guppylang_internals/span.py +150 -0
- guppylang_internals/std/__init__.py +0 -0
- guppylang_internals/std/_internal/__init__.py +0 -0
- guppylang_internals/std/_internal/checker.py +573 -0
- guppylang_internals/std/_internal/compiler/__init__.py +0 -0
- guppylang_internals/std/_internal/compiler/arithmetic.py +136 -0
- guppylang_internals/std/_internal/compiler/array.py +569 -0
- guppylang_internals/std/_internal/compiler/either.py +131 -0
- guppylang_internals/std/_internal/compiler/frozenarray.py +68 -0
- guppylang_internals/std/_internal/compiler/futures.py +30 -0
- guppylang_internals/std/_internal/compiler/list.py +348 -0
- guppylang_internals/std/_internal/compiler/mem.py +13 -0
- guppylang_internals/std/_internal/compiler/option.py +78 -0
- guppylang_internals/std/_internal/compiler/prelude.py +271 -0
- guppylang_internals/std/_internal/compiler/qsystem.py +48 -0
- guppylang_internals/std/_internal/compiler/quantum.py +118 -0
- guppylang_internals/std/_internal/compiler/tket_bool.py +55 -0
- guppylang_internals/std/_internal/compiler/tket_exts.py +59 -0
- guppylang_internals/std/_internal/compiler/wasm.py +135 -0
- guppylang_internals/std/_internal/compiler.py +0 -0
- guppylang_internals/std/_internal/debug.py +95 -0
- guppylang_internals/std/_internal/util.py +271 -0
- guppylang_internals/tracing/__init__.py +0 -0
- guppylang_internals/tracing/builtins_mock.py +62 -0
- guppylang_internals/tracing/frozenlist.py +57 -0
- guppylang_internals/tracing/function.py +186 -0
- guppylang_internals/tracing/object.py +551 -0
- guppylang_internals/tracing/state.py +69 -0
- guppylang_internals/tracing/unpacking.py +194 -0
- guppylang_internals/tracing/util.py +86 -0
- guppylang_internals/tys/__init__.py +0 -0
- guppylang_internals/tys/arg.py +115 -0
- guppylang_internals/tys/builtin.py +382 -0
- guppylang_internals/tys/common.py +110 -0
- guppylang_internals/tys/const.py +114 -0
- guppylang_internals/tys/errors.py +178 -0
- guppylang_internals/tys/param.py +251 -0
- guppylang_internals/tys/parsing.py +425 -0
- guppylang_internals/tys/printing.py +174 -0
- guppylang_internals/tys/subst.py +112 -0
- guppylang_internals/tys/ty.py +876 -0
- guppylang_internals/tys/var.py +49 -0
- guppylang_internals-0.21.0.dist-info/METADATA +253 -0
- guppylang_internals-0.21.0.dist-info/RECORD +98 -0
- guppylang_internals-0.21.0.dist-info/WHEEL +4 -0
- guppylang_internals-0.21.0.dist-info/licenses/LICENCE +201 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from typing import Any, TypeVar
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class _DummyGuppy:
|
|
5
|
+
"""A dummy class with the same interface as `@guppy` that does nothing when used to
|
|
6
|
+
decorate functions.
|
|
7
|
+
|
|
8
|
+
We use this during sphinx builds as a mock for the decorator, to ensure that Guppy
|
|
9
|
+
functions are recognised as regular functions and included in docs.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __call__(self, f: Any) -> Any:
|
|
13
|
+
return f
|
|
14
|
+
|
|
15
|
+
def comptime(self, f: Any) -> Any:
|
|
16
|
+
return f
|
|
17
|
+
|
|
18
|
+
def extend_type(self, *args: Any, **kwargs: Any) -> Any:
|
|
19
|
+
return lambda cls: cls
|
|
20
|
+
|
|
21
|
+
def type(self, *args: Any, **kwargs: Any) -> Any:
|
|
22
|
+
return lambda cls: cls
|
|
23
|
+
|
|
24
|
+
def struct(self, cls: Any) -> Any:
|
|
25
|
+
return cls
|
|
26
|
+
|
|
27
|
+
def type_var(self, name: str, *args: Any, **kwargs: Any) -> Any:
|
|
28
|
+
return TypeVar(name)
|
|
29
|
+
|
|
30
|
+
def nat_var(self, name: str, *args: Any, **kwargs: Any) -> Any:
|
|
31
|
+
return TypeVar(name)
|
|
32
|
+
|
|
33
|
+
def custom(self, *args: Any, **kwargs: Any) -> Any:
|
|
34
|
+
return lambda f: f
|
|
35
|
+
|
|
36
|
+
def hugr_op(self, *args: Any, **kwargs: Any) -> Any:
|
|
37
|
+
return lambda f: f
|
|
38
|
+
|
|
39
|
+
def declare(self, f: Any) -> Any:
|
|
40
|
+
return f
|
|
41
|
+
|
|
42
|
+
def overload(self, *args: Any, **kwargs: Any) -> Any:
|
|
43
|
+
return lambda f: f
|
|
44
|
+
|
|
45
|
+
def constant(self, *args: Any, **kwargs: Any) -> Any:
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
def extern(self, *args: Any, **kwargs: Any) -> Any:
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
def check(self, *args: Any, **kwargs: Any) -> None:
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
def compile(self, *args: Any, **kwargs: Any) -> Any:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
def compile_function(self, *args: Any, **kwargs: Any) -> Any:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
def pytket(self, *args: Any, **kwargs: Any) -> Any:
|
|
61
|
+
return lambda f: f
|
|
62
|
+
|
|
63
|
+
def load_pytket(self, *args: Any, **kwargs: Any) -> Any:
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def sphinx_running() -> bool:
|
|
68
|
+
"""Checks if guppylang was imported during a sphinx build."""
|
|
69
|
+
# This is the most general solution available at the moment.
|
|
70
|
+
# See: https://github.com/sphinx-doc/sphinx/issues/9805
|
|
71
|
+
try:
|
|
72
|
+
import sphinx # type: ignore[import-untyped, import-not-found, unused-ignore]
|
|
73
|
+
|
|
74
|
+
return hasattr(sphinx, "application")
|
|
75
|
+
except ImportError:
|
|
76
|
+
return False
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from types import FrameType
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
import hugr.build.function as hf
|
|
7
|
+
import hugr.std.collections.array
|
|
8
|
+
import hugr.std.float
|
|
9
|
+
import hugr.std.int
|
|
10
|
+
import hugr.std.logic
|
|
11
|
+
import hugr.std.prelude
|
|
12
|
+
from hugr import ops
|
|
13
|
+
from hugr.ext import Extension
|
|
14
|
+
from hugr.package import ModulePointer, Package
|
|
15
|
+
|
|
16
|
+
import guppylang_internals
|
|
17
|
+
from guppylang_internals.definition.common import (
|
|
18
|
+
CheckableDef,
|
|
19
|
+
CheckedDef,
|
|
20
|
+
CompiledDef,
|
|
21
|
+
DefId,
|
|
22
|
+
ParsableDef,
|
|
23
|
+
ParsedDef,
|
|
24
|
+
RawDef,
|
|
25
|
+
)
|
|
26
|
+
from guppylang_internals.definition.ty import TypeDef
|
|
27
|
+
from guppylang_internals.definition.value import (
|
|
28
|
+
CompiledCallableDef,
|
|
29
|
+
CompiledHugrNodeDef,
|
|
30
|
+
)
|
|
31
|
+
from guppylang_internals.error import pretty_errors
|
|
32
|
+
from guppylang_internals.span import SourceMap
|
|
33
|
+
from guppylang_internals.tys.builtin import (
|
|
34
|
+
array_type_def,
|
|
35
|
+
bool_type_def,
|
|
36
|
+
callable_type_def,
|
|
37
|
+
float_type_def,
|
|
38
|
+
frozenarray_type_def,
|
|
39
|
+
int_type_def,
|
|
40
|
+
list_type_def,
|
|
41
|
+
nat_type_def,
|
|
42
|
+
none_type_def,
|
|
43
|
+
option_type_def,
|
|
44
|
+
sized_iter_type_def,
|
|
45
|
+
string_type_def,
|
|
46
|
+
tuple_type_def,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if TYPE_CHECKING:
|
|
50
|
+
from guppylang_internals.compiler.core import MonoDefId
|
|
51
|
+
|
|
52
|
+
BUILTIN_DEFS_LIST: list[RawDef] = [
|
|
53
|
+
callable_type_def,
|
|
54
|
+
tuple_type_def,
|
|
55
|
+
none_type_def,
|
|
56
|
+
bool_type_def,
|
|
57
|
+
nat_type_def,
|
|
58
|
+
int_type_def,
|
|
59
|
+
float_type_def,
|
|
60
|
+
string_type_def,
|
|
61
|
+
list_type_def,
|
|
62
|
+
array_type_def,
|
|
63
|
+
frozenarray_type_def,
|
|
64
|
+
sized_iter_type_def,
|
|
65
|
+
option_type_def,
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
BUILTIN_DEFS = {defn.name: defn for defn in BUILTIN_DEFS_LIST}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class CoreMetadataKeys(Enum):
|
|
72
|
+
"""Core HUGR metadata keys used by Guppy."""
|
|
73
|
+
|
|
74
|
+
USED_EXTENSIONS = "core.used_extensions"
|
|
75
|
+
GENERATOR = "core.generator"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class DefinitionStore:
|
|
79
|
+
"""Storage class holding references to all Guppy definitions created in the current
|
|
80
|
+
interpreter session.
|
|
81
|
+
|
|
82
|
+
See `DEF_STORE` for the singleton instance of this class.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
raw_defs: dict[DefId, RawDef]
|
|
86
|
+
impls: defaultdict[DefId, dict[str, DefId]]
|
|
87
|
+
frames: dict[DefId, FrameType]
|
|
88
|
+
sources: SourceMap
|
|
89
|
+
|
|
90
|
+
def __init__(self) -> None:
|
|
91
|
+
self.raw_defs = {defn.id: defn for defn in BUILTIN_DEFS_LIST}
|
|
92
|
+
self.impls = defaultdict(dict)
|
|
93
|
+
self.frames = {}
|
|
94
|
+
self.sources = SourceMap()
|
|
95
|
+
|
|
96
|
+
def register_def(self, defn: RawDef, frame: FrameType | None) -> None:
|
|
97
|
+
self.raw_defs[defn.id] = defn
|
|
98
|
+
if frame:
|
|
99
|
+
self.frames[defn.id] = frame
|
|
100
|
+
|
|
101
|
+
def register_impl(self, ty_id: DefId, name: str, impl_id: DefId) -> None:
|
|
102
|
+
self.impls[ty_id][name] = impl_id
|
|
103
|
+
# Update the frame of the definition to the frame of the defining class
|
|
104
|
+
if impl_id in self.frames:
|
|
105
|
+
frame = self.frames[impl_id].f_back
|
|
106
|
+
if frame:
|
|
107
|
+
self.frames[impl_id] = frame
|
|
108
|
+
# For Python 3.12 generic functions and classes, there is an additional
|
|
109
|
+
# inserted frame for the annotation scope. We can detect this frame by
|
|
110
|
+
# looking for the special ".generic_base" variable in the frame locals
|
|
111
|
+
# that is implicitly inserted by CPython. See
|
|
112
|
+
# - https://docs.python.org/3/reference/executionmodel.html#annotation-scopes
|
|
113
|
+
# - https://docs.python.org/3/reference/compound_stmts.html#generic-functions
|
|
114
|
+
# - https://jellezijlstra.github.io/pep695.html
|
|
115
|
+
if ".generic_base" in frame.f_locals:
|
|
116
|
+
frame = frame.f_back
|
|
117
|
+
assert frame is not None
|
|
118
|
+
self.frames[impl_id] = frame
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
DEF_STORE: DefinitionStore = DefinitionStore()
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class CompilationEngine:
|
|
125
|
+
"""Main compiler driver handling checking and compiling of definitions.
|
|
126
|
+
|
|
127
|
+
The engine maintains a worklist of definitions that still need to be checked and
|
|
128
|
+
makes sure that all dependencies are compiled.
|
|
129
|
+
|
|
130
|
+
See `ENGINE` for the singleton instance of this class.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
parsed: dict[DefId, ParsedDef]
|
|
134
|
+
checked: dict[DefId, CheckedDef]
|
|
135
|
+
compiled: dict["MonoDefId", CompiledDef]
|
|
136
|
+
additional_extensions: list[Extension]
|
|
137
|
+
|
|
138
|
+
types_to_check_worklist: dict[DefId, ParsedDef]
|
|
139
|
+
to_check_worklist: dict[DefId, ParsedDef]
|
|
140
|
+
|
|
141
|
+
def reset(self) -> None:
|
|
142
|
+
"""Resets the compilation cache."""
|
|
143
|
+
self.parsed = {}
|
|
144
|
+
self.checked = {}
|
|
145
|
+
self.compiled = {}
|
|
146
|
+
self.additional_extensions = []
|
|
147
|
+
self.to_check_worklist = {}
|
|
148
|
+
self.types_to_check_worklist = {}
|
|
149
|
+
|
|
150
|
+
@pretty_errors
|
|
151
|
+
def register_extension(self, extension: Extension) -> None:
|
|
152
|
+
self.additional_extensions.append(extension)
|
|
153
|
+
|
|
154
|
+
@pretty_errors
|
|
155
|
+
def get_parsed(self, id: DefId) -> ParsedDef:
|
|
156
|
+
"""Look up the parsed version of a definition by its id.
|
|
157
|
+
|
|
158
|
+
Parses the definition if it hasn't been parsed yet. Also makes sure that the
|
|
159
|
+
definition will be checked and compiled later on.
|
|
160
|
+
"""
|
|
161
|
+
from guppylang_internals.checker.core import Globals
|
|
162
|
+
|
|
163
|
+
if id in self.parsed:
|
|
164
|
+
return self.parsed[id]
|
|
165
|
+
defn = DEF_STORE.raw_defs[id]
|
|
166
|
+
if isinstance(defn, ParsableDef):
|
|
167
|
+
defn = defn.parse(Globals(DEF_STORE.frames[defn.id]), DEF_STORE.sources)
|
|
168
|
+
self.parsed[id] = defn
|
|
169
|
+
if isinstance(defn, TypeDef):
|
|
170
|
+
self.types_to_check_worklist[id] = defn
|
|
171
|
+
else:
|
|
172
|
+
self.to_check_worklist[id] = defn
|
|
173
|
+
return defn
|
|
174
|
+
|
|
175
|
+
@pretty_errors
|
|
176
|
+
def get_checked(self, id: DefId) -> CheckedDef:
|
|
177
|
+
"""Look up the checked version of a definition by its id.
|
|
178
|
+
|
|
179
|
+
Parses and checks the definition if it hasn't been parsed/checked yet. Also
|
|
180
|
+
makes sure that the definition will be compiled to Hugr later on.
|
|
181
|
+
"""
|
|
182
|
+
from guppylang_internals.checker.core import Globals
|
|
183
|
+
|
|
184
|
+
if id in self.checked:
|
|
185
|
+
return self.checked[id]
|
|
186
|
+
defn = self.get_parsed(id)
|
|
187
|
+
if isinstance(defn, CheckableDef):
|
|
188
|
+
defn = defn.check(Globals(DEF_STORE.frames[defn.id]))
|
|
189
|
+
self.checked[id] = defn
|
|
190
|
+
|
|
191
|
+
from guppylang_internals.definition.struct import CheckedStructDef
|
|
192
|
+
|
|
193
|
+
if isinstance(defn, CheckedStructDef):
|
|
194
|
+
for method_def in defn.generated_methods():
|
|
195
|
+
DEF_STORE.register_def(method_def, None)
|
|
196
|
+
DEF_STORE.register_impl(defn.id, method_def.name, method_def.id)
|
|
197
|
+
|
|
198
|
+
return defn
|
|
199
|
+
|
|
200
|
+
@pretty_errors
|
|
201
|
+
def check(self, id: DefId) -> None:
|
|
202
|
+
"""Top-level function to kick of checking of a definition.
|
|
203
|
+
|
|
204
|
+
This is the main driver behind `guppy.check()`.
|
|
205
|
+
"""
|
|
206
|
+
from guppylang_internals.checker.core import Globals
|
|
207
|
+
|
|
208
|
+
# Clear previous compilation cache.
|
|
209
|
+
# TODO: In order to maintain results from the previous `check` call we would
|
|
210
|
+
# need to store and check if any dependencies have changed.
|
|
211
|
+
self.reset()
|
|
212
|
+
|
|
213
|
+
defn = DEF_STORE.raw_defs[id]
|
|
214
|
+
self.to_check_worklist = {
|
|
215
|
+
defn.id: (
|
|
216
|
+
defn.parse(Globals(DEF_STORE.frames[defn.id]), DEF_STORE.sources)
|
|
217
|
+
if isinstance(defn, ParsableDef)
|
|
218
|
+
else defn
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
while self.types_to_check_worklist or self.to_check_worklist:
|
|
222
|
+
# Types need to be checked first. This is because parsing e.g. a function
|
|
223
|
+
# definition requires instantiating the types in its signature which can
|
|
224
|
+
# only be done if the types have already been checked.
|
|
225
|
+
if self.types_to_check_worklist:
|
|
226
|
+
id, _ = self.types_to_check_worklist.popitem()
|
|
227
|
+
else:
|
|
228
|
+
id, _ = self.to_check_worklist.popitem()
|
|
229
|
+
self.checked[id] = self.get_checked(id)
|
|
230
|
+
|
|
231
|
+
@pretty_errors
|
|
232
|
+
def compile(self, id: DefId) -> ModulePointer:
|
|
233
|
+
"""Top-level function to kick of Hugr compilation of a definition.
|
|
234
|
+
|
|
235
|
+
This is the function that is invoked by `guppy.compile`.
|
|
236
|
+
"""
|
|
237
|
+
self.check(id)
|
|
238
|
+
|
|
239
|
+
# Prepare Hugr for this module
|
|
240
|
+
graph = hf.Module()
|
|
241
|
+
graph.metadata["name"] = "__main__" # entrypoint metadata
|
|
242
|
+
|
|
243
|
+
# Lower definitions to Hugr
|
|
244
|
+
from guppylang_internals.compiler.core import CompilerContext
|
|
245
|
+
|
|
246
|
+
ctx = CompilerContext(graph)
|
|
247
|
+
compiled_def = ctx.compile(self.checked[id])
|
|
248
|
+
self.compiled = ctx.compiled
|
|
249
|
+
|
|
250
|
+
if (
|
|
251
|
+
isinstance(compiled_def, CompiledHugrNodeDef)
|
|
252
|
+
and isinstance(compiled_def, CompiledCallableDef)
|
|
253
|
+
and not isinstance(graph.hugr[compiled_def.hugr_node].op, ops.FuncDecl)
|
|
254
|
+
):
|
|
255
|
+
# if compiling a region set it as the HUGR entrypoint
|
|
256
|
+
# can be loosened after https://github.com/CQCL/hugr/issues/2501 is fixed
|
|
257
|
+
graph.hugr.entrypoint = compiled_def.hugr_node
|
|
258
|
+
|
|
259
|
+
# TODO: Currently the list of extensions is manually managed by the user.
|
|
260
|
+
# We should compute this dynamically from the imported dependencies instead.
|
|
261
|
+
#
|
|
262
|
+
# The hugr prelude and std_extensions are implicit.
|
|
263
|
+
from guppylang_internals.std._internal.compiler.tket_exts import TKET_EXTENSIONS
|
|
264
|
+
|
|
265
|
+
extensions = [
|
|
266
|
+
*TKET_EXTENSIONS,
|
|
267
|
+
guppylang_internals.compiler.hugr_extension.EXTENSION,
|
|
268
|
+
*self.additional_extensions,
|
|
269
|
+
]
|
|
270
|
+
# TODO replace with computed extensions after https://github.com/CQCL/guppylang/issues/550
|
|
271
|
+
all_used_extensions = [
|
|
272
|
+
*extensions,
|
|
273
|
+
hugr.std.prelude.PRELUDE_EXTENSION,
|
|
274
|
+
hugr.std.collections.array.EXTENSION,
|
|
275
|
+
hugr.std.float.FLOAT_OPS_EXTENSION,
|
|
276
|
+
hugr.std.float.FLOAT_TYPES_EXTENSION,
|
|
277
|
+
hugr.std.int.INT_OPS_EXTENSION,
|
|
278
|
+
hugr.std.int.INT_TYPES_EXTENSION,
|
|
279
|
+
hugr.std.logic.EXTENSION,
|
|
280
|
+
]
|
|
281
|
+
graph.hugr.module_root.metadata[CoreMetadataKeys.USED_EXTENSIONS.value] = [
|
|
282
|
+
{
|
|
283
|
+
"name": ext.name,
|
|
284
|
+
"version": str(ext.version),
|
|
285
|
+
}
|
|
286
|
+
for ext in all_used_extensions
|
|
287
|
+
]
|
|
288
|
+
graph.hugr.module_root.metadata[CoreMetadataKeys.GENERATOR.value] = {
|
|
289
|
+
"name": "guppylang",
|
|
290
|
+
"version": guppylang_internals.__version__,
|
|
291
|
+
}
|
|
292
|
+
return ModulePointer(Package(modules=[graph.hugr], extensions=extensions), 0)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
ENGINE: CompilationEngine = CompilationEngine()
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import sys
|
|
3
|
+
from collections.abc import Callable, Iterator
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from types import TracebackType
|
|
7
|
+
from typing import TYPE_CHECKING, Any, TypeVar, cast
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from guppylang_internals.diagnostic import Error, Fatal
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class GuppyError(Exception):
|
|
15
|
+
"""An error that occurs during compilation."""
|
|
16
|
+
|
|
17
|
+
error: "Error | Fatal"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class GuppyTypeError(GuppyError):
|
|
21
|
+
"""Special Guppy exception for type errors."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class GuppyTypeInferenceError(GuppyError):
|
|
25
|
+
"""Special Guppy exception for type inference errors."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class MissingModuleError(Exception):
|
|
29
|
+
"""Special Guppy exception for operations that require a guppy module."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class GuppyComptimeError(Exception):
|
|
33
|
+
"""Exception for type and linearity errors that are caught in a comptime context."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class InternalGuppyError(Exception):
|
|
37
|
+
"""Exception for internal problems during compilation."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
ExceptHook = Callable[[type[BaseException], BaseException, TracebackType | None], Any]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@contextmanager
|
|
44
|
+
def exception_hook(hook: ExceptHook) -> Iterator[None]:
|
|
45
|
+
"""Sets a custom `excepthook` for the scope of a 'with' block."""
|
|
46
|
+
try:
|
|
47
|
+
# Check if we're inside a jupyter notebook since it uses its own exception
|
|
48
|
+
# hook. If we're in a regular interpreter, this line will raise a `NameError`
|
|
49
|
+
ipython_shell = get_ipython() # type: ignore[name-defined]
|
|
50
|
+
|
|
51
|
+
def ipython_excepthook(
|
|
52
|
+
shell: Any,
|
|
53
|
+
etype: type[BaseException],
|
|
54
|
+
value: BaseException,
|
|
55
|
+
tb: TracebackType | None,
|
|
56
|
+
tb_offset: Any = None,
|
|
57
|
+
) -> Any:
|
|
58
|
+
return hook(etype, value, tb)
|
|
59
|
+
|
|
60
|
+
old_hook = ipython_shell.CustomTB
|
|
61
|
+
old_exc_tuple = ipython_shell.custom_exceptions
|
|
62
|
+
ipython_shell.set_custom_exc((Exception,), ipython_excepthook)
|
|
63
|
+
yield
|
|
64
|
+
ipython_shell.set_custom_exc(
|
|
65
|
+
old_exc_tuple, lambda shell, *args, **kwargs: old_hook(*args, **kwargs)
|
|
66
|
+
)
|
|
67
|
+
except NameError:
|
|
68
|
+
pass
|
|
69
|
+
else:
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
# Otherwise, override the regular sys.excepthook
|
|
73
|
+
old_hook = sys.excepthook
|
|
74
|
+
sys.excepthook = hook
|
|
75
|
+
yield
|
|
76
|
+
sys.excepthook = old_hook
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
FuncT = TypeVar("FuncT", bound=Callable[..., Any])
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def pretty_errors(f: FuncT) -> FuncT:
|
|
83
|
+
"""Decorator to print custom error banners when a `GuppyError` occurs."""
|
|
84
|
+
|
|
85
|
+
def hook(
|
|
86
|
+
excty: type[BaseException], err: BaseException, traceback: TracebackType | None
|
|
87
|
+
) -> None:
|
|
88
|
+
"""Custom `excepthook` that intercepts `GuppyExceptions` for pretty printing."""
|
|
89
|
+
if isinstance(err, GuppyError):
|
|
90
|
+
from guppylang_internals.diagnostic import DiagnosticsRenderer
|
|
91
|
+
from guppylang_internals.engine import DEF_STORE
|
|
92
|
+
|
|
93
|
+
renderer = DiagnosticsRenderer(DEF_STORE.sources)
|
|
94
|
+
renderer.render_diagnostic(err.error)
|
|
95
|
+
sys.stderr.write("\n".join(renderer.buffer))
|
|
96
|
+
sys.stderr.write("\n\nGuppy compilation failed due to 1 previous error\n")
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
# If it's not a GuppyError, fall back to default hook
|
|
100
|
+
sys.__excepthook__(excty, err, traceback)
|
|
101
|
+
|
|
102
|
+
@functools.wraps(f)
|
|
103
|
+
def pretty_errors_wrapped(*args: Any, **kwargs: Any) -> Any:
|
|
104
|
+
with exception_hook(hook):
|
|
105
|
+
return f(*args, **kwargs)
|
|
106
|
+
|
|
107
|
+
return cast(FuncT, pretty_errors_wrapped)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from ast import expr
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from types import TracebackType
|
|
4
|
+
from typing import ClassVar
|
|
5
|
+
|
|
6
|
+
from guppylang_internals.ast_util import AstNode
|
|
7
|
+
from guppylang_internals.checker.errors.generic import UnsupportedError
|
|
8
|
+
from guppylang_internals.diagnostic import Error, Help
|
|
9
|
+
from guppylang_internals.error import GuppyError
|
|
10
|
+
|
|
11
|
+
EXPERIMENTAL_FEATURES_ENABLED = False
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class enable_experimental_features:
|
|
15
|
+
"""Enables experimental Guppy features.
|
|
16
|
+
|
|
17
|
+
Can be used as a context manager to enable experimental features in a `with` block.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
global EXPERIMENTAL_FEATURES_ENABLED
|
|
22
|
+
self.original = EXPERIMENTAL_FEATURES_ENABLED
|
|
23
|
+
EXPERIMENTAL_FEATURES_ENABLED = True
|
|
24
|
+
|
|
25
|
+
def __enter__(self) -> None:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
def __exit__(
|
|
29
|
+
self,
|
|
30
|
+
exc_type: type[BaseException] | None,
|
|
31
|
+
exc_val: BaseException | None,
|
|
32
|
+
exc_tb: TracebackType | None,
|
|
33
|
+
) -> None:
|
|
34
|
+
global EXPERIMENTAL_FEATURES_ENABLED
|
|
35
|
+
EXPERIMENTAL_FEATURES_ENABLED = self.original
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class disable_experimental_features:
|
|
39
|
+
"""Disables experimental Guppy features.
|
|
40
|
+
|
|
41
|
+
Can be used as a context manager to enable experimental features in a `with` block.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(self) -> None:
|
|
45
|
+
global EXPERIMENTAL_FEATURES_ENABLED
|
|
46
|
+
self.original = EXPERIMENTAL_FEATURES_ENABLED
|
|
47
|
+
EXPERIMENTAL_FEATURES_ENABLED = False
|
|
48
|
+
|
|
49
|
+
def __enter__(self) -> None:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
def __exit__(
|
|
53
|
+
self,
|
|
54
|
+
exc_type: type[BaseException] | None,
|
|
55
|
+
exc_val: BaseException | None,
|
|
56
|
+
exc_tb: TracebackType | None,
|
|
57
|
+
) -> None:
|
|
58
|
+
global EXPERIMENTAL_FEATURES_ENABLED
|
|
59
|
+
EXPERIMENTAL_FEATURES_ENABLED = self.original
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass(frozen=True)
|
|
63
|
+
class ExperimentalFeatureError(Error):
|
|
64
|
+
title: ClassVar[str] = "Experimental feature"
|
|
65
|
+
span_label: ClassVar[str] = "{things} are an experimental feature"
|
|
66
|
+
things: str
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True)
|
|
69
|
+
class Suggestion(Help):
|
|
70
|
+
message: ClassVar[str] = (
|
|
71
|
+
"Experimental features are currently disabled. You can enable them by "
|
|
72
|
+
"calling `guppylang.enable_experimental_features()`, however note that "
|
|
73
|
+
"these features are unstable and might break in the future."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def __post_init__(self) -> None:
|
|
77
|
+
self.add_sub_diagnostic(ExperimentalFeatureError.Suggestion(None))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def check_function_tensors_enabled(node: expr | None = None) -> None:
|
|
81
|
+
if not EXPERIMENTAL_FEATURES_ENABLED:
|
|
82
|
+
raise GuppyError(ExperimentalFeatureError(node, "Function tensors"))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def check_lists_enabled(loc: AstNode | None = None) -> None:
|
|
86
|
+
if not EXPERIMENTAL_FEATURES_ENABLED:
|
|
87
|
+
raise GuppyError(ExperimentalFeatureError(loc, "Lists"))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def check_capturing_closures_enabled(loc: AstNode | None = None) -> None:
|
|
91
|
+
if not EXPERIMENTAL_FEATURES_ENABLED:
|
|
92
|
+
raise GuppyError(UnsupportedError(loc, "Capturing closures"))
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Tools for inspecting source code when running in IPython."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def is_running_ipython() -> bool:
|
|
5
|
+
"""Checks if we are currently running in IPython"""
|
|
6
|
+
try:
|
|
7
|
+
return get_ipython() is not None # type: ignore[name-defined]
|
|
8
|
+
except NameError:
|
|
9
|
+
return False
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def normalize_ipython_dummy_files(filename: str) -> str:
|
|
13
|
+
"""Turns dummy file names generated for IPython cells into readable names like
|
|
14
|
+
"<In[42]>".
|
|
15
|
+
|
|
16
|
+
In vanilla IPython, cells have filenames like "<ipython-input-3-3e9b5833de21>" and
|
|
17
|
+
in Jupyter, cells have filenames like "/var/{...}/ipykernel_82076/61218616.py".
|
|
18
|
+
"""
|
|
19
|
+
try:
|
|
20
|
+
if shell := get_ipython(): # type: ignore[name-defined]
|
|
21
|
+
res = shell.compile.format_code_name(filename)
|
|
22
|
+
if res is None:
|
|
23
|
+
return filename
|
|
24
|
+
return f"<{res[1]}>"
|
|
25
|
+
else:
|
|
26
|
+
return filename
|
|
27
|
+
except NameError:
|
|
28
|
+
return filename
|