avrae-ls 0.5.1__py3-none-any.whl → 0.6.1__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.
- avrae_ls/__init__.py +3 -0
- avrae_ls/__main__.py +210 -0
- avrae_ls/alias_preview.py +371 -0
- avrae_ls/alias_tests.py +311 -0
- avrae_ls/api.py +2015 -0
- avrae_ls/argparser.py +430 -0
- avrae_ls/argument_parsing.py +67 -0
- avrae_ls/code_actions.py +282 -0
- avrae_ls/codes.py +3 -0
- avrae_ls/completions.py +1695 -0
- avrae_ls/config.py +474 -0
- avrae_ls/context.py +337 -0
- avrae_ls/cvars.py +115 -0
- avrae_ls/diagnostics.py +826 -0
- avrae_ls/dice.py +33 -0
- avrae_ls/parser.py +68 -0
- avrae_ls/runtime.py +750 -0
- avrae_ls/server.py +447 -0
- avrae_ls/signature_help.py +248 -0
- avrae_ls/symbols.py +274 -0
- {avrae_ls-0.5.1.dist-info → avrae_ls-0.6.1.dist-info}/METADATA +34 -4
- avrae_ls-0.6.1.dist-info/RECORD +34 -0
- {avrae_ls-0.5.1.dist-info → avrae_ls-0.6.1.dist-info}/WHEEL +1 -1
- draconic/__init__.py +4 -0
- draconic/exceptions.py +157 -0
- draconic/helpers.py +236 -0
- draconic/interpreter.py +1091 -0
- draconic/string.py +100 -0
- draconic/types.py +364 -0
- draconic/utils.py +78 -0
- draconic/versions.py +4 -0
- avrae_ls-0.5.1.dist-info/RECORD +0 -6
- {avrae_ls-0.5.1.dist-info → avrae_ls-0.6.1.dist-info}/entry_points.txt +0 -0
- {avrae_ls-0.5.1.dist-info → avrae_ls-0.6.1.dist-info}/licenses/LICENSE +0 -0
avrae_ls/symbols.py
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
import logging
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Dict, Iterable, List, Optional
|
|
7
|
+
|
|
8
|
+
import draconic
|
|
9
|
+
from lsprotocol import types
|
|
10
|
+
|
|
11
|
+
from .argument_parsing import apply_argument_parsing
|
|
12
|
+
from .parser import find_draconic_blocks
|
|
13
|
+
|
|
14
|
+
log = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class SymbolEntry:
|
|
19
|
+
name: str
|
|
20
|
+
kind: types.SymbolKind
|
|
21
|
+
range: types.Range
|
|
22
|
+
selection_range: types.Range
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SymbolTable:
|
|
26
|
+
def __init__(self, entries: List[SymbolEntry]):
|
|
27
|
+
self._entries = entries
|
|
28
|
+
self._index: Dict[str, SymbolEntry] = {entry.name: entry for entry in entries}
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def entries(self) -> List[SymbolEntry]:
|
|
32
|
+
return self._entries
|
|
33
|
+
|
|
34
|
+
def lookup(self, name: str) -> Optional[SymbolEntry]:
|
|
35
|
+
return self._index.get(name)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def build_symbol_table(source: str) -> SymbolTable:
|
|
39
|
+
entries: list[SymbolEntry] = []
|
|
40
|
+
parsed_source = apply_argument_parsing(source)
|
|
41
|
+
blocks = find_draconic_blocks(parsed_source)
|
|
42
|
+
if not blocks:
|
|
43
|
+
entries.extend(_symbols_from_code(parsed_source, 0, 0))
|
|
44
|
+
else:
|
|
45
|
+
for block in blocks:
|
|
46
|
+
entries.extend(_symbols_from_code(block.code, block.line_offset, block.char_offset))
|
|
47
|
+
return SymbolTable(entries)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def document_symbols(source: str) -> List[types.DocumentSymbol]:
|
|
51
|
+
table = build_symbol_table(source)
|
|
52
|
+
return [
|
|
53
|
+
types.DocumentSymbol(
|
|
54
|
+
name=entry.name,
|
|
55
|
+
kind=entry.kind,
|
|
56
|
+
range=entry.range,
|
|
57
|
+
selection_range=entry.selection_range,
|
|
58
|
+
)
|
|
59
|
+
for entry in table.entries
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def find_definition_range(table: SymbolTable, name: str) -> types.Range | None:
|
|
64
|
+
entry = table.lookup(name)
|
|
65
|
+
if entry:
|
|
66
|
+
return entry.selection_range
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def find_references(
|
|
71
|
+
table: SymbolTable, source: str, name: str, include_declaration: bool = True
|
|
72
|
+
) -> List[types.Range]:
|
|
73
|
+
parsed_source = apply_argument_parsing(source)
|
|
74
|
+
ranges: list[types.Range] = []
|
|
75
|
+
entry = table.lookup(name)
|
|
76
|
+
include_stores = include_declaration and entry is None
|
|
77
|
+
if include_declaration:
|
|
78
|
+
if entry:
|
|
79
|
+
ranges.append(entry.selection_range)
|
|
80
|
+
|
|
81
|
+
blocks = find_draconic_blocks(parsed_source)
|
|
82
|
+
if not blocks:
|
|
83
|
+
ranges.extend(_references_from_code(parsed_source, name, 0, 0, include_stores))
|
|
84
|
+
else:
|
|
85
|
+
for block in blocks:
|
|
86
|
+
ranges.extend(
|
|
87
|
+
_references_from_code(block.code, name, block.line_offset, block.char_offset, include_stores)
|
|
88
|
+
)
|
|
89
|
+
return _dedupe_ranges(ranges)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def range_for_word(source: str, position: types.Position) -> types.Range | None:
|
|
93
|
+
lines = source.splitlines()
|
|
94
|
+
if position.line >= len(lines):
|
|
95
|
+
return None
|
|
96
|
+
line = lines[position.line]
|
|
97
|
+
if position.character > len(line):
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
def _is_ident(ch: str) -> bool:
|
|
101
|
+
return ch.isalnum() or ch == "_"
|
|
102
|
+
|
|
103
|
+
start_idx = position.character
|
|
104
|
+
while start_idx > 0 and _is_ident(line[start_idx - 1]):
|
|
105
|
+
start_idx -= 1
|
|
106
|
+
|
|
107
|
+
end_idx = position.character
|
|
108
|
+
while end_idx < len(line) and _is_ident(line[end_idx]):
|
|
109
|
+
end_idx += 1
|
|
110
|
+
|
|
111
|
+
if start_idx == end_idx:
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
return types.Range(
|
|
115
|
+
start=types.Position(line=position.line, character=start_idx),
|
|
116
|
+
end=types.Position(line=position.line, character=end_idx),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _symbols_from_code(code: str, line_offset: int, char_offset: int) -> List[SymbolEntry]:
|
|
121
|
+
body, offset_adjust = _parse_draconic(code)
|
|
122
|
+
if not body:
|
|
123
|
+
return []
|
|
124
|
+
|
|
125
|
+
local_offset = line_offset + offset_adjust
|
|
126
|
+
|
|
127
|
+
entries: list[SymbolEntry] = []
|
|
128
|
+
for node in body:
|
|
129
|
+
entry = _entry_from_node(node, local_offset, char_offset)
|
|
130
|
+
if entry:
|
|
131
|
+
entries.append(entry)
|
|
132
|
+
return entries
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _entry_from_node(node: ast.AST, line_offset: int = 0, char_offset: int = 0) -> SymbolEntry | None:
|
|
136
|
+
if isinstance(node, ast.FunctionDef):
|
|
137
|
+
kind = types.SymbolKind.Function
|
|
138
|
+
name = node.name
|
|
139
|
+
elif isinstance(node, ast.ClassDef):
|
|
140
|
+
kind = types.SymbolKind.Class
|
|
141
|
+
name = node.name
|
|
142
|
+
elif isinstance(node, ast.Assign) and node.targets:
|
|
143
|
+
target = node.targets[0]
|
|
144
|
+
if isinstance(target, ast.Name):
|
|
145
|
+
kind = types.SymbolKind.Variable
|
|
146
|
+
name = target.id
|
|
147
|
+
node = target
|
|
148
|
+
else:
|
|
149
|
+
return None
|
|
150
|
+
elif isinstance(node, ast.AnnAssign):
|
|
151
|
+
target = node.target
|
|
152
|
+
if isinstance(target, ast.Name):
|
|
153
|
+
kind = types.SymbolKind.Variable
|
|
154
|
+
name = target.id
|
|
155
|
+
node = target
|
|
156
|
+
else:
|
|
157
|
+
return None
|
|
158
|
+
else:
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
rng = _range_from_positions(
|
|
162
|
+
getattr(node, "lineno", 1),
|
|
163
|
+
getattr(node, "col_offset", 0),
|
|
164
|
+
getattr(node, "end_lineno", None),
|
|
165
|
+
getattr(node, "end_col_offset", None),
|
|
166
|
+
)
|
|
167
|
+
rng = _shift_range(rng, line_offset, char_offset)
|
|
168
|
+
return SymbolEntry(name=name, kind=kind, range=rng, selection_range=rng)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class _ReferenceCollector(ast.NodeVisitor):
|
|
172
|
+
def __init__(self, target: str, include_stores: bool):
|
|
173
|
+
super().__init__()
|
|
174
|
+
self._target = target
|
|
175
|
+
self._include_stores = include_stores
|
|
176
|
+
self.ranges: list[types.Range] = []
|
|
177
|
+
|
|
178
|
+
def visit_Name(self, node: ast.Name): # type: ignore[override]
|
|
179
|
+
if node.id == self._target:
|
|
180
|
+
if isinstance(node.ctx, ast.Store) and not self._include_stores:
|
|
181
|
+
return
|
|
182
|
+
rng = _range_from_positions(
|
|
183
|
+
getattr(node, "lineno", 1),
|
|
184
|
+
getattr(node, "col_offset", 0),
|
|
185
|
+
getattr(node, "end_lineno", None),
|
|
186
|
+
getattr(node, "end_col_offset", None),
|
|
187
|
+
)
|
|
188
|
+
self.ranges.append(rng)
|
|
189
|
+
self.generic_visit(node)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _references_from_code(
|
|
193
|
+
code: str,
|
|
194
|
+
name: str,
|
|
195
|
+
line_offset: int,
|
|
196
|
+
char_offset: int,
|
|
197
|
+
include_stores: bool,
|
|
198
|
+
) -> List[types.Range]:
|
|
199
|
+
body, offset_adjust = _parse_draconic(code)
|
|
200
|
+
if not body:
|
|
201
|
+
return []
|
|
202
|
+
|
|
203
|
+
collector = _ReferenceCollector(name, include_stores)
|
|
204
|
+
for node in body:
|
|
205
|
+
collector.visit(node)
|
|
206
|
+
|
|
207
|
+
local_offset = line_offset + offset_adjust
|
|
208
|
+
return [_shift_range(rng, local_offset, char_offset) for rng in collector.ranges]
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _parse_draconic(code: str) -> tuple[list[ast.AST], int]:
|
|
212
|
+
parser = draconic.DraconicInterpreter()
|
|
213
|
+
try:
|
|
214
|
+
return parser.parse(code), 0
|
|
215
|
+
except draconic.DraconicSyntaxError:
|
|
216
|
+
wrapped, added = _wrap_draconic(code)
|
|
217
|
+
try:
|
|
218
|
+
return parser.parse(wrapped), -added
|
|
219
|
+
except draconic.DraconicSyntaxError:
|
|
220
|
+
return [], 0
|
|
221
|
+
except Exception as exc: # pragma: no cover - defensive
|
|
222
|
+
log.debug("Symbol extraction failed: %s", exc)
|
|
223
|
+
return [], 0
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _range_from_positions(
|
|
227
|
+
lineno: int | None,
|
|
228
|
+
col_offset: int | None,
|
|
229
|
+
end_lineno: int | None,
|
|
230
|
+
end_col_offset: int | None,
|
|
231
|
+
) -> types.Range:
|
|
232
|
+
start_line = max((lineno or 1) - 1, 0)
|
|
233
|
+
start_char = max(col_offset or 0, 0)
|
|
234
|
+
end_line = max(((end_lineno or lineno or 1) - 1), 0)
|
|
235
|
+
raw_end_char = end_col_offset if end_col_offset is not None else col_offset
|
|
236
|
+
end_char = max(raw_end_char or start_char, start_char)
|
|
237
|
+
if end_char <= start_char:
|
|
238
|
+
end_char = start_char + 1
|
|
239
|
+
return types.Range(
|
|
240
|
+
start=types.Position(line=start_line, character=start_char),
|
|
241
|
+
end=types.Position(line=end_line, character=end_char),
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _shift_range(rng: types.Range, line_offset: int, char_offset: int = 0) -> types.Range:
|
|
246
|
+
start_char = rng.start.character + (char_offset if rng.start.line == 0 else 0)
|
|
247
|
+
end_char = rng.end.character + (char_offset if rng.end.line == 0 else 0)
|
|
248
|
+
if line_offset == 0:
|
|
249
|
+
return types.Range(
|
|
250
|
+
start=types.Position(line=rng.start.line, character=start_char),
|
|
251
|
+
end=types.Position(line=rng.end.line, character=end_char),
|
|
252
|
+
)
|
|
253
|
+
return types.Range(
|
|
254
|
+
start=types.Position(line=max(rng.start.line + line_offset, 0), character=max(start_char, 0)),
|
|
255
|
+
end=types.Position(line=max(rng.end.line + line_offset, 0), character=max(end_char, 0)),
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _wrap_draconic(code: str) -> tuple[str, int]:
|
|
260
|
+
indented = "\n".join(f" {line}" for line in code.splitlines())
|
|
261
|
+
wrapped = f"def __alias_main__():\n{indented}\n__alias_main__()"
|
|
262
|
+
return wrapped, 1
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _dedupe_ranges(ranges: Iterable[types.Range]) -> List[types.Range]:
|
|
266
|
+
seen = set()
|
|
267
|
+
unique: list[types.Range] = []
|
|
268
|
+
for rng in ranges:
|
|
269
|
+
key = (rng.start.line, rng.start.character, rng.end.line, rng.end.character)
|
|
270
|
+
if key in seen:
|
|
271
|
+
continue
|
|
272
|
+
seen.add(key)
|
|
273
|
+
unique.append(rng)
|
|
274
|
+
return unique
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: avrae-ls
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Language server for Avrae draconic aliases
|
|
5
5
|
Author: 1drturtle
|
|
6
6
|
License: MIT License
|
|
@@ -30,6 +30,7 @@ Requires-Dist: d20>=1.1.2
|
|
|
30
30
|
Requires-Dist: httpx>=0.27
|
|
31
31
|
Requires-Dist: lsprotocol>=2023.0.1
|
|
32
32
|
Requires-Dist: pygls>=1.3.1
|
|
33
|
+
Requires-Dist: pyyaml>=6.0
|
|
33
34
|
Provides-Extra: dev
|
|
34
35
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
35
36
|
Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
|
|
@@ -66,18 +67,47 @@ Language Server Protocol (LSP) implementation targeting Avrae-style draconic ali
|
|
|
66
67
|
- Tests only (with coverage): `make test` or `uv run pytest tests --cov=src`.
|
|
67
68
|
- CLI smoke test without installing: `uv run python -m avrae_ls --analyze path/to/alias.txt`.
|
|
68
69
|
|
|
70
|
+
## Alias tests
|
|
71
|
+
|
|
72
|
+
- Create files ending with `.alias-test` (or `.aliastest`) next to your alias file. Each test starts with an invocation, followed by `---` and the expected result; you can stack multiple tests in one file by repeating this pattern (optional metadata after a second `---` per test).
|
|
73
|
+
```
|
|
74
|
+
!my-alias -b example args
|
|
75
|
+
---
|
|
76
|
+
expected text or number
|
|
77
|
+
```
|
|
78
|
+
- For embed aliases, put a YAML/JSON dictionary after the separator to compare against the embed preview (partial dictionaries are allowed).
|
|
79
|
+
```
|
|
80
|
+
!embedtest
|
|
81
|
+
---
|
|
82
|
+
title: Hello
|
|
83
|
+
description: World
|
|
84
|
+
```
|
|
85
|
+
- Embed fields lists can be partial: only the listed fields (in order) are matched; extra fields in the alias do not fail the test.
|
|
86
|
+
- Use regex expectations by wrapping strings in `/.../` (or `re:...`). You can also mix literals with regex segments (e.g., `Hello /world.*/`) so only the delimited part is treated as regex.
|
|
87
|
+
- Optional second `---` section can carry metadata:
|
|
88
|
+
```
|
|
89
|
+
name: critical-hit
|
|
90
|
+
vars:
|
|
91
|
+
cvars:
|
|
92
|
+
hp: 12
|
|
93
|
+
character:
|
|
94
|
+
name: Tester
|
|
95
|
+
```
|
|
96
|
+
`name` is a label for reporting, `vars` are merged into cvars/uvars/svars/gvars, and `character` keys are deep-merged into the mock character.
|
|
97
|
+
- Run them with `avrae-ls --run-tests [path]` (defaults to the current directory); non-zero exit codes indicate failures.
|
|
98
|
+
|
|
69
99
|
## Runtime differences (mock vs. live Avrae)
|
|
70
100
|
|
|
71
101
|
- Mock execution never writes back to Avrae: cvar/uvar/gvar mutations only live for the current run and reset before the next.
|
|
72
102
|
- Network is limited to gvar fetches (when `enableGvarFetch` is true) and `verify_signature`; other Avrae/Discord calls are replaced with mocked context data from `.avraels.json`.
|
|
73
103
|
- `get_gvar`/`using` values are pulled from local var files first; remote fetches go to `https://api.avrae.io/customizations/gvars/<id>` (or your `avraeService.baseUrl`) using `avraeService.token` and are cached for the session.
|
|
74
|
-
- `signature()` returns a mock string (`mock-signature:<int>`). `verify_signature()` POSTs to `/bot/signature/verify`,
|
|
104
|
+
- `signature()` returns a mock string (`mock-signature:<int>`). `verify_signature()` POSTs to `/bot/signature/verify`, reuses the last successful response per signature, and includes `avraeService.token` if present.
|
|
75
105
|
|
|
76
106
|
## Troubleshooting gvar fetch / verify_signature
|
|
77
107
|
|
|
78
108
|
- `get_gvar` returns `None` or `using(...)` raises `ModuleNotFoundError`: ensure the workspace `.avraels.json` sets `enableGvarFetch: true`, includes a valid `avraeService.token`, or seed the gvar in a var file referenced by `varFiles`.
|
|
79
109
|
- HTTP 401/403/404 from fetch/verify calls: check the token (401/403) and the gvar/signature id (404). Override `avraeService.baseUrl` if you mirror the API.
|
|
80
|
-
- Slow or flaky calls:
|
|
110
|
+
- Slow or flaky calls: disable remote fetches by flipping `enableGvarFetch` off to rely purely on local vars.
|
|
81
111
|
|
|
82
112
|
## Other editors (stdio)
|
|
83
113
|
|
|
@@ -98,7 +128,7 @@ Language Server Protocol (LSP) implementation targeting Avrae-style draconic ali
|
|
|
98
128
|
:major-modes '(fundamental-mode) ;; bind to your Avrae alias mode
|
|
99
129
|
:server-id 'avrae-ls))
|
|
100
130
|
```
|
|
101
|
-
- VS Code commands to mirror: `Avrae: Run Alias (Mock)`, `Avrae: Show Alias Preview`, and `Avrae: Reload Workspace Config` run against the same server binary.
|
|
131
|
+
- VS Code commands to mirror: `Avrae: Run Alias (Mock)`, `Avrae: Show Alias Preview`, `Avrae: Refresh GVARs`, and `Avrae: Reload Workspace Config` run against the same server binary.
|
|
102
132
|
|
|
103
133
|
## Releasing (maintainers)
|
|
104
134
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
avrae_ls/__init__.py,sha256=BmjrnksGkbG7TPqwbyQvgYj9uei8pFSFpfkRpaGVdJU,63
|
|
2
|
+
avrae_ls/__main__.py,sha256=YpYFFBYwYt0GTdZz0Hw4aL8iYDW1p5DN2eLoIXsWR9M,6934
|
|
3
|
+
avrae_ls/alias_preview.py,sha256=pSDTZVmcZZbUqCxyCryVWFSGdN8p0fLkR4IqbHxgNMs,12376
|
|
4
|
+
avrae_ls/alias_tests.py,sha256=XgaraYpOtGWQX68US-U4Zy3heVtajSxLg64oNZ3vooE,10439
|
|
5
|
+
avrae_ls/api.py,sha256=7QVJAmqgKkiS22qj39_aSVsDuO_kUkyl2Ho1IsD58yE,65110
|
|
6
|
+
avrae_ls/argparser.py,sha256=DRptXGyK4f0r7NsuV1Sg4apG-qihIClOX408jgk4HH4,13762
|
|
7
|
+
avrae_ls/argument_parsing.py,sha256=ezKl65VwuNEDxt6KlYwVQcpy1110UDvf4BqZqgZTcqk,2122
|
|
8
|
+
avrae_ls/code_actions.py,sha256=CYl3nMzCaE9k35p5fWAmzsTOVfcv2RrkA3SbtyCdcsI,9423
|
|
9
|
+
avrae_ls/codes.py,sha256=iPRPQ6i9DZheae4_ra1y29vCw3Y4SEu6Udf5WiZj_RY,136
|
|
10
|
+
avrae_ls/completions.py,sha256=FO9LCT1E5upSr23j0lUa_h_J1QN7ET9Yl_jLcWE97m0,64023
|
|
11
|
+
avrae_ls/config.py,sha256=vQoBtULKE1ujKJCkngrZgCQlgJPS9lg1jXTInKfJL8w,16634
|
|
12
|
+
avrae_ls/context.py,sha256=c18vdkuxKlPjPIogtWhJjbuynKLmgybD-VUkG46ooKM,12205
|
|
13
|
+
avrae_ls/cvars.py,sha256=0tcVbUHx_CKJ6aou3kEsKX37LRWAjkUWlqqIuSRFlXk,3197
|
|
14
|
+
avrae_ls/diagnostics.py,sha256=x5sZmpgje3G6UfOub2nQFv10lfjTb_7Qn_2SjqiGfLY,28072
|
|
15
|
+
avrae_ls/dice.py,sha256=DY7V7L-EwAXaCgddgVe9xU1s9lVtiw5Zc2reipNgdTk,874
|
|
16
|
+
avrae_ls/parser.py,sha256=iwdITmiBqRTszxI5avjzD-P0nrRQb1Hv5AiWl603_DU,2201
|
|
17
|
+
avrae_ls/runtime.py,sha256=2IbdZNZ7VCpjuU1lbbp8jTtroE6w7ii-r_erebJUl-A,26339
|
|
18
|
+
avrae_ls/server.py,sha256=pY4kIYUWY1q-yks9GEaHvsZk8ObwZRFMLG9cHtiEP70,17025
|
|
19
|
+
avrae_ls/signature_help.py,sha256=VSuLDExEEeNDc7yQZ_R6LXUcc95ErNCzoekiuMYEF6o,8654
|
|
20
|
+
avrae_ls/symbols.py,sha256=9oKcAHeHq1_hx2waKsfT5EWrcngXJdjYXcImAE4ingg,8672
|
|
21
|
+
draconic/LICENSE,sha256=Fzvu32_DafLKKn2mzxhEdlmrKZzAsigDZ87O7uoVqZI,1067
|
|
22
|
+
draconic/__init__.py,sha256=YPH420Pcn_nTkfB62hJy_YqC5kpJdzSa78jP8n4z_xY,109
|
|
23
|
+
draconic/exceptions.py,sha256=siahnHIsumbaUhKBDSrw_DmLZ-0oZks8L5oytPH8hD4,3753
|
|
24
|
+
draconic/helpers.py,sha256=jY-f9KHWP0ey2q09g6QsbtVxxet8LoQoZ8mAfju1E5c,9470
|
|
25
|
+
draconic/interpreter.py,sha256=ksL7qHwXmXsvSqf685xFEnzVLng6AZ2ewk1z7ymbKtc,42004
|
|
26
|
+
draconic/string.py,sha256=kGrRc6wNHRq1y5xw8Os-fBhfINDtIY2nBWQWkyLSfQI,2858
|
|
27
|
+
draconic/types.py,sha256=1Lsr6z8bW5agglGI4hLt_nPtYuZOIf_ueSpPDB4WDrs,13686
|
|
28
|
+
draconic/utils.py,sha256=D4vJ-txqS2-rlqsEpXAC46_j1sZX4UjY-9zIgElo96k,3122
|
|
29
|
+
draconic/versions.py,sha256=CUEsgUWjAmjez0432WwiBwZlIzWPIObwZUf8Yld18EE,84
|
|
30
|
+
avrae_ls-0.6.1.dist-info/METADATA,sha256=I-Sqcyi9TN39U-qGlBKqzXt02A0O3IRKSldqisl4_vU,7163
|
|
31
|
+
avrae_ls-0.6.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
32
|
+
avrae_ls-0.6.1.dist-info/entry_points.txt,sha256=OtYXipMQzqmxpMoApgo0MeJYFmMbkbFN51Ibhpb8hF4,52
|
|
33
|
+
avrae_ls-0.6.1.dist-info/licenses/LICENSE,sha256=O-0zMbcEi6wXz1DiSdVgzMlQjJcNqNe5KDv08uYzqR0,1055
|
|
34
|
+
avrae_ls-0.6.1.dist-info/RECORD,,
|
draconic/__init__.py
ADDED
draconic/exceptions.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import ast
|
|
3
|
+
|
|
4
|
+
from .versions import PY_310
|
|
5
|
+
|
|
6
|
+
__all__ = (
|
|
7
|
+
"DraconicException",
|
|
8
|
+
"DraconicSyntaxError",
|
|
9
|
+
"InvalidExpression",
|
|
10
|
+
"NotDefined",
|
|
11
|
+
"FeatureNotAvailable",
|
|
12
|
+
"DraconicValueError",
|
|
13
|
+
"LimitException",
|
|
14
|
+
"NumberTooHigh",
|
|
15
|
+
"IterableTooLong",
|
|
16
|
+
"TooManyStatements",
|
|
17
|
+
"TooMuchRecursion",
|
|
18
|
+
"WrappedException",
|
|
19
|
+
"AnnotatedException",
|
|
20
|
+
"NestedException",
|
|
21
|
+
"_PostponedRaise",
|
|
22
|
+
"_raise_in_context",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DraconicException(Exception):
|
|
27
|
+
"""Base exception for all exceptions in this library."""
|
|
28
|
+
|
|
29
|
+
__drac_context__: str = None
|
|
30
|
+
|
|
31
|
+
def __init__(self, msg):
|
|
32
|
+
super().__init__(msg)
|
|
33
|
+
self.msg = msg
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class DraconicSyntaxError(DraconicException):
|
|
37
|
+
"""Bad syntax."""
|
|
38
|
+
|
|
39
|
+
def __init__(self, original: SyntaxError, expr):
|
|
40
|
+
super().__init__(original.msg)
|
|
41
|
+
self.lineno = original.lineno
|
|
42
|
+
self.offset = original.offset
|
|
43
|
+
self.end_lineno = None
|
|
44
|
+
self.end_offset = None
|
|
45
|
+
self.expr = expr
|
|
46
|
+
|
|
47
|
+
if PY_310:
|
|
48
|
+
self.end_lineno = original.end_lineno
|
|
49
|
+
self.end_offset = original.end_offset
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def from_node(cls, node: ast.AST, msg: str, expr):
|
|
53
|
+
if PY_310:
|
|
54
|
+
inner = SyntaxError(
|
|
55
|
+
msg, ("<string>", node.lineno, node.col_offset + 1, expr, node.end_lineno, node.end_col_offset + 1)
|
|
56
|
+
)
|
|
57
|
+
else:
|
|
58
|
+
inner = SyntaxError(msg, ("<string>", node.lineno, node.col_offset + 1, expr))
|
|
59
|
+
return cls(inner, expr)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class InvalidExpression(DraconicException):
|
|
63
|
+
"""Base exception for all exceptions during run-time."""
|
|
64
|
+
|
|
65
|
+
def __init__(self, msg, node, expr):
|
|
66
|
+
super().__init__(msg)
|
|
67
|
+
self.node = node
|
|
68
|
+
self.expr = expr
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class NotDefined(InvalidExpression):
|
|
72
|
+
"""Some name is not defined."""
|
|
73
|
+
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class FeatureNotAvailable(InvalidExpression):
|
|
78
|
+
"""What you're trying to do is not allowed."""
|
|
79
|
+
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class DraconicValueError(InvalidExpression):
|
|
84
|
+
"""Bad value passed to some function."""
|
|
85
|
+
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class LimitException(InvalidExpression):
|
|
90
|
+
"""
|
|
91
|
+
Something exceeded execution limits.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class NumberTooHigh(LimitException):
|
|
98
|
+
"""Some number is way too big."""
|
|
99
|
+
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class IterableTooLong(LimitException):
|
|
104
|
+
"""Some iterable is way too big."""
|
|
105
|
+
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class TooManyStatements(LimitException):
|
|
110
|
+
"""Tried to execute too many statements."""
|
|
111
|
+
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class TooMuchRecursion(LimitException):
|
|
116
|
+
"""Too deep in recursion."""
|
|
117
|
+
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class WrappedException(InvalidExpression, abc.ABC):
|
|
122
|
+
"""abstract base exception for a lib exception that wraps some other exception"""
|
|
123
|
+
|
|
124
|
+
original: BaseException
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class AnnotatedException(WrappedException):
|
|
128
|
+
"""A wrapper for another exception to handle lineno info."""
|
|
129
|
+
|
|
130
|
+
def __init__(self, original, node, expr):
|
|
131
|
+
super().__init__(str(original), node, expr)
|
|
132
|
+
self.original = original
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class NestedException(WrappedException):
|
|
136
|
+
"""An exception occurred in a user-defined function call."""
|
|
137
|
+
|
|
138
|
+
def __init__(self, msg, node, expr, last_exc):
|
|
139
|
+
super().__init__(msg, node, expr)
|
|
140
|
+
self.last_exc = last_exc # type: DraconicException # used for tracebacking
|
|
141
|
+
# keep a reference to the end of the chain for easy comparison
|
|
142
|
+
if isinstance(last_exc, WrappedException):
|
|
143
|
+
self.original = last_exc.original
|
|
144
|
+
else:
|
|
145
|
+
self.original = last_exc
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# we need to raise some exception, but don't have the node context right now
|
|
149
|
+
class _PostponedRaise(Exception):
|
|
150
|
+
def __init__(self, cls, *args, **kwargs):
|
|
151
|
+
self.cls = cls
|
|
152
|
+
self.args = args
|
|
153
|
+
self.kwargs = kwargs
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _raise_in_context(cls, *args, **kwargs):
|
|
157
|
+
raise _PostponedRaise(cls, *args, **kwargs)
|