Something went wrong. Try again.
An experimental alternative to Rust sans-io that enables the direct style
Something went wrong. Try again.
3.1 kB · 104 lines
Python
at experiments
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105"""The desk's world and its clock, and a deterministic simulation driver.
Shared by the capabilities host and the sans-io host, so that the two producebyte-identical output from the same script: patrons and books are tables,loans a dict, the clock virtual — a patron visit takes five days, `Sleep(d)`wakes at `now + d` — and machines run as events in day order, ties insubmission order."""
from __future__ import annotations
import heapqfrom collections.abc import Callablefrom dataclasses import dataclass, field
VISIT_DAYS = 5
PATRONS = { "ada": ("Ada Lovelace", False), "grace": ("Grace Hopper", False), "linus": ("Linus Torvalds", True),}
BOOKS = { "111": ("SICP", True), "222": ("TAPL", True), "333": ("Dragon Book", False),}
SCRIPT = [ "checkout ada 111", "checkout grace 222", "status ada", "renew ada 111", "renew ada 111", "checkin ada 111", "checkout linus 111", "checkout ada 333", "status grace", "checkin grace 222", "checkin grace 222", "quit",]
@dataclassclass World: day: int = 0 counter: int = 0 loans: dict[str, int] = field(default_factory=dict) extended: set[str] = field(default_factory=set) lines: list[str] = field(default_factory=lambda: list(SCRIPT)) out: list[str] = field(default_factory=list)
def read_line(self) -> str: return self.lines.pop(0) if self.lines else "quit"
def patron(self, id: str) -> tuple[str, bool, int]: name, blocked = PATRONS.get(id, (f"<{id}>", True)) on_loan = sum(1 for k in self.loans if k.startswith(f"loan/{id}/")) return name, blocked, on_loan
def book(self, isbn: str) -> tuple[str, bool]: title, available = BOOKS.get(isbn, ("<unknown>", False)) return title, available and not any(k.endswith(f"/{isbn}") for k in self.loans)
def list(self, prefix: str) -> list[tuple[str, int]]: return sorted((k, v) for k, v in self.loans.items() if k.startswith(prefix))
def extend(self, key: str, days: int) -> bool: if key in self.extended or key not in self.loans: return False self.extended.add(key) self.loans[key] += days return True
def count(self) -> int: self.counter += 1 return self.counter
def write(self, who: str, text: str) -> None: self.out.append(f"[day {self.day:>2} {who:<8}] {text}")
class Sim: """Events keyed by (day, seq): a step to run for a machine."""
def __init__(self, world: World) -> None: self.world = world self._q: list[tuple[int, int, Callable[[], None]]] = [] self._seq = 0
def at(self, day: int, step: Callable[[], None]) -> None: self._seq += 1 heapq.heappush(self._q, (day, self._seq, step))
def now(self, step: Callable[[], None]) -> None: self.at(self.world.day, step)
def run(self) -> None: while self._q: day, _, step = heapq.heappop(self._q) self.world.day = max(self.world.day, day) step()