Getting started

The library is function-shaped: there is no program object to construct. Three functions, importable straight from the oaknut.basic namespace, do the work.

from oaknut.basic import tokenise, detokenise, number_lines

Tokenising and de-tokenising

tokenise takes a numbered source listing as a str and returns the tokenised program as bytes; detokenise does the reverse:

program = tokenise("10 PRINT \"HELLO\"\n20 GOTO 10\n")
# program is bytes: 0x0D 0x00 0x0A ... 0x0D 0xFF

listing = detokenise(program)
# listing is "10 PRINT \"HELLO\"\n20 GOTO 10\n"

The two are exact inverses at the byte level — see Round-tripping and code points for what that guarantees, and for the character-encoding contract the strings follow.

Numbering

number_lines prepends ascending line numbers to unnumbered source, the AUTO equivalent:

number_lines("PRINT\nGOTO 10", start=10, step=10)
# "10 PRINT\n20 GOTO 10"

tokenise accepts the same start and step keywords directly, to number and tokenise unnumbered source in one call:

tokenise("PRINT\nEND", start=10, step=10)

On a disc image

When the program lives in a disc image, prefer the path-object wrappers over calling the codec by hand. oaknut.dfs.DFSPath.write_basic() and read_basic() (and the ADFS equivalents) compose the codec with the correct load-address default and the disc’s character encoding:

from oaknut.dfs import DFS

with DFS.from_file("game.ssd") as disc:
    (disc.root / "$" / "MENU").write_basic("10 PRINT\n20 END")
    print((disc.root / "$" / "MENU").read_basic())

Never move a BASIC program through read_text / write_text: a tokenised program is bytecode, and a text codec would mangle it.