Skip to content

Commit da858cc

Browse files
gh-11: Add type annotations, enforce static typing in ASM-Lang interpreter
1 parent 73c904e commit da858cc

File tree

7 files changed

+178
-254
lines changed

7 files changed

+178
-254
lines changed

asm-lang.exe

1.57 KB
Binary file not shown.

asm-lang.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
from typing import List, Optional
88
import threading
99

10-
from extensions import ASMExtensionError, ReplContext, load_runtime_services
10+
from extensions import ASMExtensionError, ReplContext, load_runtime_services, RuntimeServices
1111
from interpreter import ASMRuntimeError, Environment, ExitSignal, Interpreter, TracebackFormatter
1212
from lexer import ASMParseError, Lexer
1313
from parser import Parser, Statement
1414

15-
import tkinter # Ensure tkinter is available for GUI extensions
15+
import tkinter # needed for the gui extension (ext\gui.py) to work for the frozen executable
1616

1717
def _print_error(msg: str) -> None:
1818
"""Print an error message to stderr prefixed with U+0007 (BEL)."""
@@ -61,7 +61,7 @@ def _parse_statements_from_source(text: str, filename: str, *, type_names: Optio
6161
return program.statements
6262

6363

64-
def run_repl(*, verbose: bool, services) -> int:
64+
def run_repl(*, verbose: bool, services: Optional[RuntimeServices]) -> int:
6565
print("\x1b[38;2;153;221;255mASM-Lang\033[0m REPL. Enter statements, blank line to run buffer.") # "ASM-Lang" in light blue
6666
# Use "<repl>" as the REPL's effective source filename so that MAIN() and imports behave
6767
had_output = False
@@ -101,6 +101,7 @@ def _output_sink(text: str) -> None:
101101
picked = services.hook_registry.pick_repl() if services is not None else None
102102
if picked is not None:
103103
_name, runner, _ext = picked
104+
assert services is not None
104105
ctx = ReplContext(
105106
verbose=verbose,
106107
services=services,
@@ -210,6 +211,7 @@ def _output_sink(text: str) -> None:
210211
continue
211212

212213
buffer.append(line)
214+
return 0
213215

214216

215217
def run_cli(argv: Optional[List[str]] = None) -> int:
@@ -272,7 +274,7 @@ def run_cli(argv: Optional[List[str]] = None) -> int:
272274
except BaseException as exc:
273275
return _print_internal_error(exc=exc, interpreter=None, verbose=bool(getattr(args, "verbose", False)))
274276

275-
program: Optional[str] = None
277+
program = None
276278
if remaining:
277279
if len(remaining) > 1:
278280
_print_error("Too many non-extension inputs; expected a single program argument")

extensions.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def pick_repl(self) -> Optional[Tuple[str, Callable[["ReplContext"], int], str]]
126126
if len(self._repls) == 1:
127127
return self._repls[0]
128128
names = ", ".join(sorted({n for n, _, _ in self._repls}))
129-
raise ASMExtensionError(f"Multiple REPL providers registered ({names}); select one explicitly is not implemented")
129+
raise ASMExtensionError(f"Multiple REPL providers registered ({names}); explicit selection not implemented")
130130

131131

132132
@dataclass(frozen=True)
@@ -181,7 +181,7 @@ def register_operator(
181181
qualified = f"{prefix}.{qualified}"
182182
self._services.operators.append((qualified, int(min_args), None if max_args is None else int(max_args), impl, doc))
183183

184-
def operator(self, name: str, min_args: int, max_args: Optional[int] = None, *, doc: str = ""):
184+
def operator(self, name: str, min_args: int, max_args: Optional[int] = None, *, doc: str = "") -> Callable[[Callable[..., Any]], Callable[..., Any]]:
185185
def deco(fn: Callable[..., Any]) -> Callable[..., Any]:
186186
self.register_operator(name, min_args, max_args, fn, doc=doc)
187187
return fn
@@ -211,14 +211,14 @@ def register_type(
211211
)
212212
)
213213

214-
def type(self, name: str, *, printable: bool = True):
214+
def type(self, name: str, *, printable: bool = True) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
215215
def deco(fn: Callable[..., Any]) -> Callable[..., Any]:
216216
raise ASMExtensionError("Use register_type(...) explicitly; decorator form not supported")
217217

218218
return deco
219219

220220
# ---- hooks ----
221-
def on_event(self, event: str, handler: Optional[Callable[..., None]] = None, *, priority: int = 0):
221+
def on_event(self, event: str, handler: Optional[Callable[..., None]] = None, *, priority: int = 0) -> Callable[..., None] | Callable[[Callable[..., None]], Callable[..., None]]:
222222
if handler is None:
223223
def deco(fn: Callable[..., None]) -> Callable[..., None]:
224224
self._services.hook_registry.on_event(event, fn, priority=priority, ext_name=self._ext_name)
@@ -227,7 +227,7 @@ def deco(fn: Callable[..., None]) -> Callable[..., None]:
227227
self._services.hook_registry.on_event(event, handler, priority=priority, ext_name=self._ext_name)
228228
return handler
229229

230-
def every_n_steps(self, every_n: int, handler: Optional[Callable[[Any, StepContext], None]] = None, *, name: str = ""):
230+
def every_n_steps(self, every_n: int, handler: Optional[Callable[[Any, StepContext], None]] = None, *, name: str = "") -> Callable[[Any, StepContext], None] | Callable[[Callable[[Any, StepContext], None]], Callable[[Any, StepContext], None]]:
231231
if handler is None:
232232
def deco(fn: Callable[[Any, StepContext], None]) -> Callable[[Any, StepContext], None]:
233233
self._services.hook_registry.add_step_rule(name=name or fn.__name__, every_n=every_n, handler=fn, ext_name=self._ext_name)

0 commit comments

Comments
 (0)