Skip to main content

pyPILOT

An implementation of the PILOT (Programmed Inquiry, Learning, or Teaching) programming language.

My Deepest Condolences

I just learned (10/5/26) that the author of Atari PILOT Harry Stewart -- whose documents and notes were invaluable to the accuracy of this recreation -- passed away in 2025 at the age of 89. Harry was a devoted husband, father, and grandfather according to all reports, and a hobby musician to boot. Rest in peace, Harry -- You helped me understand the basic concepts of programming (input, output, matching, conditionals, modified execution flow) very very early (Six!) I only wish he might have seen this resurrection and gotten a chuckle out of it. I certainly hope other "ATARI Kids" of the 80s like me have a chuckle as well.

What is PILOT?

PILOT is a very simple interpreted language created by John Amsden Starkweather in the late 1960s and formalized as a machine-independent specification in 1973 ("PILOT-73"). A predecessor to Logo, it was designed for computer-aided instruction: single-letter commands, text typed straight into the program, and a learner answering questions into an accept buffer.

This project targets ATARI PILOT — the 1980/81 dialect shipped in an Atari cartridge for the 400/800, authored by Harry B. Stewart. It is not Core PILOT and not Common PILOT, despite superficial similarity. See SPEC.md §1.1 and the research notes in history/.

Status

1.1.0. The optional interactive extra adds Atari turtle graphics, four-voice sound, and keyboard-backed joystick/paddle input. Install it with pip install repilot[interactive]; the base install remains dependency-free.

Zero third-party runtime dependencies, so it runs on a classroom Raspberry Pi with nothing but CPython. 953 tests, 90% branch coverage, mypy strict, and a 3-OS × 2-Python CI matrix that installs the built wheel and runs a program from it.

Install

$ pip install repilot
$ pypilot examples/summary.pilot

The distribution is repilot; the importable package is pypilot:

from pypilot import parse, PilotState

That is deliberate rather than an oversight. pypilot is taken on PyPI by an unrelated long-standing package, so the distribution needed a different name, and renaming the import path as well would have broken every import in the docs and tests for no benefit. PyPI normalises names, so rePILOT in the web UI is installed as repilot.

The language specification and implementation history live in SPEC.md; the release notes in CHANGELOG.md. The language reference is in docs/language.rst, and the API in docs/api.rst.

Why use it?

Don't. This is purely a bucket-list project. I've never exactly implemented a language, and this likely won't be a repo of my finest code.

Then, why write it?

Primarily? Safer-at-Home, Covid-19, boredom, and avoiding the dishes.

On a deeper level? Because it's crossed my mind repeatedly for years and doing it will stop me from saying, "You know, I've always wanted to write my own PILOT runtime, wouldn't that be fun this rainy weekend?" before I binge more Netflix.

PILOT was magical to me at 6 or 7 years old. It's simple to understand and has minimal command names which make it easier on learners who type at rates approaching 10 words per hour. Added to which, ATARI's implementation came with a step by step course disguised as a cute book of cartoons.

Development

This project uses uv.

uv sync --all-extras --dev     # create .venv and install everything
uv run pytest                  # run the test suite
uv run ruff check .            # lint
uv run ruff format .           # format
uv run mypy                    # type check
uv build                       # build sdist + wheel
uv run --group docs sphinx-build -W -b html docs docs/_build/html

Every one of those is a release gate, and CI runs all of them on Linux, Windows and macOS — plus a clean-environment install of the built wheel, because building a distribution and being able to install it are different claims.

The packaging tests build real archives and inspect their contents. tools/scan_secrets.py greps everything Git would stage for API tokens and the like; run it before any bulk git add -A.

Releasing

See RELEASING.md. Publishing is gated on a tag and uses trusted publishing, so there is no PyPI token in the repository.

Run a program. examples/summary.pilot demonstrates the whole language:

$ uv run pypilot examples/summary.pilot
HELLO, WORLD
THE SUM OF TWO AND THREE IS 5.
HALF OF IT, TRUNCATED, IS 2.
AND THE REMAINDER IS 1.

EXPRESSIONS HAVE NO OPERATOR PRECEDENCE, SO 1+2*3 IS:
  9   (not 7)

AN UNDEFINED STRING PRINTS ITS OWN NAME:
  NOT_SET_ANYWHERE

AND NUMBERS WRAP AT 16 BITS WITHOUT COMPLAINING:
  -32768

JM: BRANCHES ON WHICH MATCH FIELD WON, BECAUSE M: SETS AN
ORDINAL RATHER THAN A BOOLEAN. TRY IT WITH YES, NO OR MAYBE:
yes
  YOU SAID YES - THAT WAS FIELD 1.

U: CALLS A MODULE, AND E: COMES BACK FROM ONE. IT IS ALSO HOW
A PROGRAM ENDS WHEN THERE IS NO MODULE TO RETURN FROM.
NOTE THE J: AFTER THE RETURNING E: - WITHOUT IT EXECUTION WOULD
FALL BACK INTO THE MODULE AND CALL IT AGAIN.
THE MODULE HAS BEEN ENTERED 1 TIMES.

AND A J: LOOPS, AS LONG AS ITS CONDITION HOLDS:
  COUNT 1
  COUNT 2
  COUNT 3
  COUNT 4
  COUNT 5

The interactive REPL

Run pypilot with no program file and you get the immediate mode of the original system: type a statement and it runs, or build a program with AUTO and RUN it. Here is a real session — type the lines shown, answer ada and yes at the two prompts:

$ uv run pypilot
pilot> AUTO 100,10
AUTO-NUMBER INPUT MODE. AN EMPTY LINE EXITS.
auto> 100 T:WHAT IS YOUR NAME?
auto> 110 A:$NAME
auto> 120 T:HELLO, $NAME.
auto> 130 T:ARE YOU A STUDENT? (YES OR NO)
auto> 140 A:$ANSWER
auto> 150 M: YES , NO
auto> 160 JM:*YES,*NO
auto> 170 T:UNRECOGNISED ANSWER.
auto> 180 J:*END
auto> 190 *YES
auto> 200 T:WELCOME TO THE COURSE.
auto> 210 J:*END
auto> 220 *NO
auto> 230 T:COME BACK ANY TIME.
auto> 240 *END
auto> 250 E:

pilot> LIST
   1  100 T:WHAT IS YOUR NAME?
   2  110 A:$NAME
   3  120 T:HELLO, $NAME.
   4  130 T:ARE YOU A STUDENT? (YES OR NO)
   5  140 A:$ANSWER
   6  150 M: YES , NO
   7  160 JM:*YES,*NO
   8  170 T:UNRECOGNISED ANSWER.
   9  180 J:*END
  10  190 *YES
  11  200 T:WELCOME TO THE COURSE.
  12  210 J:*END
  13  220 *NO
  14  230 T:COME BACK ANY TIME.
  15  240 *END
  16  250 E:
pilot> RUN
WHAT IS YOUR NAME?
ada
HELLO, ada.
ARE YOU A STUDENT? (YES OR NO)
yes
WELCOME TO THE COURSE.
pilot> QUIT
GOODBYE

Note the colon is optional at the prompt, so T HELLO and T:HELLO are the same statement (spec 6.2). The transcript above is generated by tools/readme_session.py, which runs the real REPL — documentation that is produced rather than typed does not drift.

The console examples run without optional dependencies. graphics.pilot, controls.pilot, and sound.pilot use the optional interactive backend; install it with pip install repilot[interactive] to run them in a native window with keyboard controllers and sound. Their behavior is covered by fake-host tests, so the default test suite remains headless. A window opened by a completed CLI run stays visible until you close it.

Add --trace to watch each statement; trace goes to stderr, so it never mixes with the program's own output:

$ uv run pypilot --trace examples/summary.pilot

Parse a program without running it:

uv run pypilot --check examples/greeter.pilot
uv run pypilot --check --list examples/adventure.pilot
$ uv run pypilot --check --list examples/graphics.pilot
examples/graphics.pilot: parsed 5 statement(s), 0 label(s)
  1  R:Draw a square with the ATARI PILOT turtle graphics command.
  2  GR:CLEAR
  3  GR:GOTO 0,0;TURNTO 0;4(DRAW 15;TURN 90)
  4  T:SQUARE COMPLETE AT (%X,%Y)
  5  E:

A syntax error is reported the way a 1980s interpreter would have reported it — line number, message, and the offending line echoed back:

$ printf 'T:HELLO\nFO:1,DATA\n' > bad.pilot
$ uv run pypilot --check bad.pilot
bad.pilot: line 2: 'FO' is not an ATARI PILOT command: ATARI PILOT has no file
handles; use READ: (spec 9.6)
  FO:1,DATA
$ echo $?
1

Or from Python:

from pypilot import PilotState, parse

program = parse("*GREET T:HELLO, $NAME\nA:$NAME\n*AGAIN J:*GREET\nE:")
print(len(program), "statements,", len(program.labels), "labels")
print(program.resolve("*GREET"))  # -> 0

# An undefined string variable prints *its own name* — the ATARI rule,
# and the opposite of every other PILOT dialect.
state = PilotState()
print(state.expand("HELLO $NAME"))  # HELLO NAME
state.set_string("NAME", "ada")
print(state.expand("HELLO $NAME"))  # HELLO ada

Three rules worth knowing before you write tests, because Python's defaults are the opposite of every one of them:

from pypilot import Numeric, PilotState

print(Numeric(7) / Numeric(3))  # 2  — truncates toward zero
print(Numeric(-7) / Numeric(3))  # -2 — not Python's -3
print(Numeric(32767) + Numeric(1))  # -32768 — wraps, silently

state = PilotState()
print(state.evaluate("1+2*3"))  # 9 — no operator precedence!
print(state.evaluate("1+(2*3)"))  # 7 — parens are the only fix
print(state.evaluate("-7\\3"))  # 1 — the remainder is always positive

License

MIT. See LICENSE.

Metadata

Release files for rePILOT 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rePILOT 1.1.0
File Size Uploaded
repilot-1.1.0.tar.gz 249.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rePILOT 1.1.0
File Interpreter ABI Platform
repilot-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 333.7 kB

Release files / repilot-1.1.0.tar.gz

Download URL repilot-1.1.0.tar.gz
Size 249.8 kB
Tags Source
SHA-256 checksum
How to use checksums
613adaaf37f613fcd928a4a1defa1e3d5d332a297322db86999d490fc0955162
BLAKE2b-256 checksum
How to use checksums
73ba057b47defd6a24783e1187549a70ba2da8bb3229d51c3a61035497abb179
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release files / repilot-1.1.0-py3-none-any.whl

Download URL repilot-1.1.0-py3-none-any.whl
Size 84.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
25de04b008cd7d371f9c8bf1e09063d362836bda7cb1a4d78496b95aa43e1398
BLAKE2b-256 checksum
How to use checksums
e6ecb7dc51e3438d169c9fef4e3f627548e13926e67946db413ed490febda15f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page