Transpiles a constrained subset of Python into Commodore BASIC source code.
Write simple programs in Python (with modern tooling, syntax highlighting, and sane variable names), then run them on your system.
Inspired by the idea that you shouldn't have to think in line numbers.
Capable of generating BASIC 2.0 (Commodore 64 - the default), BASIC 7.0 (Commodore 128) and BASIC65 (MEGA65) dialetcs of BASIC.
- Python 3.8+
- No external dependencies (uses Python's built-in
astmodule)
Note: Despite the name, this transpiler does not use ANTLR4. It uses Python's own
astmodule to parse the input, which is simpler and requires no additional tools. The architecture is visitor-based in the same spirit.
Just copy transpiler.py somewhere on your PATH or into your project.
# Optionally make it directly executable
chmod +x transpiler.pypython3 transpiler.py input.py
python3 transpiler.py --basic7 input.py
python3 transpiler.py --basic65 input.py -o output.bas
python3 transpiler.py input.py --vars # show variable name map
python3 transpiler.py input.py --start 100 --step 10x = 42 # numeric variable
name = "hello" # string variable (type inferred from assignment)
x = 3.14 # floatprint("hello") # -> PRINT "HELLO"
print(x, y) # -> PRINT X ; Y
print() # -> PRINT (blank line)
name = input("Name? ") # -> PRINT "Name? " : INPUT A$x + y # addition
x - y # subtraction
x * y # multiplication
x / y # division
x % y # modulo -> MOD(X, Y)
x ** y # power -> X ^ Y
x // y # floor div -> INT(X / Y)x += 1 # -> X = X + 1
x -= 1 # -> X = X - 1
x *= 2 # -> X = X * 2
x %= 3 # -> X = MOD(X, 3)x == y # =
x != y # <>
x < y # <
x > y # >
x <= y # <=
x >= y # >=
a and b # AND
a or b # OR
not a # NOTif x > 0: # IF X > 0 THEN BEGIN
print("pos") # PRINT "POS"
elif x < 0: # BEND ELSE BEGIN
print("neg") # IF X < 0 THEN BEGIN
else: # BEND ELSE BEGIN
print("zero") # PRINT "ZERO"
# BEND
# BENDwhile x < 10: # DO
x += 1 # X = X + 1
# LOOP WHILE X < 10for i in range(5): # FOR I = 0 TO 4
print(i) # PRINT I
# NEXT I
for i in range(1, 11): # FOR I = 1 TO 10
for i in range(0, 10, 2): # FOR I = 0 TO 9 STEP 2break # EXIT (exits a DO/LOOP)def greet(): # REM -- greet (at reserved line)
print("hello") # PRINT "HELLO"
# RETURN
greet() # GOSUB <line>Functions cannot have parameters or return values. Use global variables to pass data between subroutines — exactly as you would in BASIC.
int(x) # INT(X)
str(x) # STR$(X)
len(s) # LEN(S$)
abs(x) # ABS(X)
chr(n) # CHR$(N)
ord(c) # ASC(C$)
round(x) # INT(X + 0.5)import time
time.sleep(1.5) # SLEEP 1.5
import sys
sys.exit() # END# This becomes a REM statementassert x > 0 # IF NOT (X > 0) THEN BEGIN : PRINT "Assertion failed" : END : BEND
assert x > 0, "bad x" # IF NOT (X > 0) THEN BEGIN : PRINT "BAD X" : END : BENDBASIC 65 variable names are very short (single letter, or letter+digit). The transpiler maintains a symbol table that maps Python names to BASIC names:
| Python name | BASIC 65 name |
|---|---|
counter |
A |
total |
B |
name |
A$ |
greeting |
B$ |
Use --vars to see the full mapping:
$ python3 transpiler.py myprog.py --vars
10 A = 0
...
Variable map:
counter -> A
total -> B
name -> A$
Maximum 286 numeric variables and 286 string variables.
The transpiler will give you a clear error message for any of these:
- Function parameters or return values
- Lists, tuples, dicts, sets
- Classes
- Lambda expressions
- List/dict/set comprehensions
try/except/finallycontinuein loopswithstatementsimport(excepttime,sys,math)global/nonlocal- Multiple assignment targets (
a, b = 1, 2) - Chained comparisons (
1 < x < 10) - f-strings (use
str(x)concatenation instead) - Nested functions
Input (fizzbuzz.py):
i = 1
while i <= 20:
m3 = int(i / 3) * 3
m5 = int(i / 5) * 5
if m3 == i and m5 == i:
print("FIZZBUZZ")
elif m3 == i:
print("FIZZ")
elif m5 == i:
print("BUZZ")
else:
print(i)
i += 1Output:
10 A = 1
20 DO
30 B = INT(A / 3) * 3
40 C = INT(A / 5) * 5
50 IF (B = A) AND (C = A) THEN BEGIN
60 PRINT "FIZZBUZZ"
70 BEND ELSE BEGIN
80 IF B = A THEN BEGIN
90 PRINT "FIZZ"
100 BEND ELSE BEGIN
110 IF C = A THEN BEGIN
120 PRINT "BUZZ"
130 BEND ELSE BEGIN
140 PRINT A
150 BEND
160 BEND
170 BEND
180 A = A + 1
190 LOOP WHILE A <= 20
- Save the output as a
.basfile - Copy it to your MEGA65's SD card (or a D81 image)
- On the MEGA65:
IMPORT "FIZZBUZZ.BAS" LISTto verify,RUNto execute
MIT — do whatever you want with it.
Built for the MEGA65 community. BASIC 65 dialect reference from the MEGA65 User's Guide and ROM 920413 binary analysis.