Cookbook

Worked examples that use oaknut.basic directly. Each is a small, complete program.

Tokenise a listing to a file

from pathlib import Path

from oaknut.basic import tokenise

source = Path("menu.bas").read_text(encoding="utf-8")
Path("MENU").write_bytes(tokenise(source))

The source string is the program’s logical text; if it contains non-ASCII characters, encode them to the Acorn character set before tokenising (or let the CLI’s --encoding do it). See Round-tripping and code points for the code-point contract.

De-tokenise a stored program

from pathlib import Path

from oaknut.basic import detokenise

listing = detokenise(Path("MENU").read_bytes())
print(listing)

Number unnumbered source, then tokenise

from oaknut.basic import number_lines, tokenise

draft = "PRINT \"HELLO\"\nGOTO 10"

# Two steps...
numbered = number_lines(draft, start=10, step=10)
program = tokenise(numbered)

# ...or one, with auto-numbering inside tokenise.
program = tokenise(draft, start=10, step=10)

Validate a program defensively

Because every codec failure is a BASICError, a batch tool can convert what it can and collect the rest:

from oaknut.basic import detokenise, BASICError

def to_listing(program: bytes) -> str | None:
    try:
        return detokenise(program)
    except BASICError as exc:
        log.warning("skipping malformed program: %s", exc)
        return None

Round-trip through a disc image

When the program lives on a disc, the path-object wrappers compose the codec with the disc’s encoding and the right load address — prefer them to calling tokenise() / detokenise() by hand:

from oaknut.dfs import DFS

with DFS.from_file("game.ssd") as disc:
    menu = disc.root / "$" / "MENU"
    listing = menu.read_basic()
    menu.write_basic(listing.replace("GOTO 10", "GOTO 20"))