PuffinBASIC
BASIC interpreter written in Java
Install / Use
/learn @mayuropensource/PuffinBASICREADME
PuffinBASIC
A cross-platform modern BASIC compiler/interpreter written in Java.
BASIC (Beginners' All-purpose Symbolic Instruction Code) is a general-purpose high-level language from the 1960s. PuffinBASIC is an implementation of the BASIC language specification. PuffinBASIC conforms most closely to GWBASIC.
Show Case
2D Scrolling Game (Teaser)
YouTube Video: <a href="https://youtu.be/m3snkPVhhVo"><img src="samples/nextGame.png"></a> Source: Under development, not committed yet
TESSEL - A 2D Tile Game
YouTube Video: <a href="https://youtu.be/L8xkM-g3Zms"><img src="samples/tessel/images/tesselsnap1.png" width="64"></a> Source: <a href="samples/tessel/tessel.bas">tessel.bas</a>
FlyPuffinFly - A Scrolling Game
YouTube Video: <a href="https://youtu.be/bIIamQp9N3o"><img src="samples/puffingame/images/flypuffinflysnap.png" width="64"></a> Source: <a href="samples/puffingame/flypuffinfly.bas">flypuffinfly.bas</a>
Conway's Game of Life
YouTube Video: <a href="https://youtu.be/TpsxQrxKqdg"><img src="samples/gameoflife/images/gameoflifesnap.png" width="64"></a> <a href="https://youtu.be/A5tN6cBqoAc">#2</a> Source: <a href="samples/gameoflife/gameoflife.bas">gameoflife.bas</a>
Mandelbrot Set
YouTube Video: <a href="https://youtu.be/7CukYv9tfXA"><img src="samples/mandelbrotsnap.png" width="64"></a> <a href="https://youtu.be/xKp9U2BXTHM">Zoomed</a> Source: <a href="samples/mandelbrot.bas">mandelbrot.bas</a>
Why Implement BASIC?
- Love and Familiarity: GWBASIC was my first programming language, I loved it and made many games in it.
- Interpreter language: BASIC is simple and an interpreted language and hence a good choice for implementing this interpreter.
- Learn antlr4: I was learning antlr4 and BASIC was the perfect language to try because of its simple instruction set.
- Resurrect BASIC: Implementations such as GWBASIC don't work on most modern platforms. My goal is to make PuffinBASIC work anywhere Java can work.
- Improve BASIC: I wanted to add modern graphics, better data types, etc., to make it easier to write games.
Version
0.1 experimental (NOT YET READY FOR PRODUCTION USE!)
- The interpreter is not yet thoroughly tested and is likely to have bugs.
- This is an active project so expect large changes at frequent intervals.
Code Samples
Print multiplication tables
10 FOR I% = 1 TO 10
20 PRINT "Multiplication table of ", I%
30 FOR J% = 1 TO 10
40 PRINT I%, "x", J%, "=", I%*J%
50 NEXT J%
60 NEXT I%
Print prime numbers between 1 and 100
Line numbers are optional in PuffinBASIC.
FOR I% = 1 TO 100
J% = 3
N% = I% \ 2
ISPRIME% = (I% > 1) AND ((I% MOD 2 <> 0) OR (I% = 2))
WHILE J% <= N% AND ISPRIME% = -1
ISPRIME% = I% MOD J% <> 0
J% = J% + 2
WEND
IF ISPRIME% THEN PRINT STR$(I%), " is prime"
NEXT I%
Print Fibonacci Series
10 A@ = 0 : B@ = 1
20 FOR I% = 1 TO 20
30 C@ = A@ + B@
40 PRINT C@,
50 A@ = B@ : B@ = C@
60 NEXT I%
70 PRINT ""
Print trigonometric function graph
10 PRINT SPACE$(40), "0"
20 FOR D = 0 TO 360 STEP 10
30 x# = 3.14159 * D / 180.0
40 y# = SIN(x#)
50 PRINT SPACE$(40 + CINT(y# * 40)), "*"
60 NEXT D
Graphics
10 SCREEN "PuffinBASIC 2D Graphics", 800, 600
20 LINE (100, 100) - (200, 200), "B"
30 FOR I% = 10 TO 50 STEP 10
40 CIRCLE (150, 150), I%, I%
50 NEXT I%
60 COLOR 255, 0, 0
70 LINE (200, 200) - (250, 300), "BF"
80 COLOR 0, 255, 255
90 FONT "Georgia", "bi", 32
100 DRAWSTR "Graphics with PuffinBASIC", 10, 400
110 DIM A%(101, 101)
120 GET (100, 100) - (201, 201), A%
130 PUT (250, 250), A%
140 DIM B%(32, 32)
150 LOADIMG "samples/enemy1.png", B%
160 FOR I% = 1 TO 5
170 PUT (400, 100 * I%), B%
180 NEXT
190 COLOR 255, 255, 0
200 DRAW "M600,400; UN50; RN50; DB50; F100"
210 COLOR 255, 255, 255
220 CIRCLE (700, 100), 10, 20
230 COLOR 255, 0, 255
240 PAINT (700, 100), 255, 255, 255
250 CIRCLE (700, 400), 50, 50, 0, 90
260 CIRCLE (700, 500), 50, 50, 90, 180, "F"
1000 SLEEP 5000
<img src="samples/puffin_graphics.png" width="512"/>
Dependencies
JDK 11+, Maven, antlr4
Build and test
$ mvn compile
$ mvn test
Run using Maven
$ mvn exec:java -D"exec.args"="samples/graph.bas"
Graphics mode:
$ mvn exec:java -D"exec.args"="-g samples/graphics.bas"
Working with Intellij
Import the pom.xml file in Intellij. After importing, if you make a change to the antlr4 grammar, regenerate the antlr4 code using following command and then reload the changes.
$ mvn generate-sources
How it works?
- PuffinBASIC's grammar is defined using antlr4.
- The user source code is parsed using antlr4 lexer+parser.
- An intermediate representation (IR) is generated. A symbol table keeps track of variables, scalars, arrays, etc. objects.
- The interpreter runtime processes the IR instructions and executes them in a single thread. For graphics, an optional Runtime is used.
Since PuffinBASIC generates IR from source code and the runtime executes the IR, we can say PuffinBASIC is both a compiler and an interpreter.
Planned Improvements
- Android port, requires a rewrite of Graphics runtime.
- ...
Performance
PuffinBASIC is an interpreter, and it should not be expected to have very good performance characteristics. Certain operations such as PRINT USING, INPUT, etc are not optimized for performance. PuffinBASIC primitives have not been benchmarked. That being said, games containing 2D graphics work reasonably well.
Memory
PuffinBASIC runs within a JVM and can use as much memory as available for the JVM process.
Reference
Mode of operation
Indirect Mode
- Write a program in your favorite editor.
- Save it in a text file with a '.bas' extension (not a requirement).
- Use maven or PuffinBasicInterpreterMain to run the program.
PuffinBASIC supports indirect mode only.
Line-number and No-line-number modes
PuffinBASIC programs can have one of two modes:
- Line-number mode: All lines must start with a line number. GOTO/GOSUB statements can use both line number and label.
- No-line-number mode: No line should start with a line number. GOTO/GOSUB statements can use label only.
Compatibility
PuffinBASIC is mostly compatible with Microsoft's GWBASIC. Graphics is supported using Java 2D graphics.
PuffinBASIC will not support assembly instructions.
Input
PuffinBASIC aims for cross-platform compatibility and doesn't support platform specific features. Input statements and functions are line based and require 'ENTER' key to be pressed. Same applies for the sequential file writes, print statements always output a line and input statements read the whole line.
Case sensitivity
Operators, Statements and Standard Functions are case-insensitive. Constants, variables and user defined functions are case-sensitive.
Graphics
Graphics uses platform-independent Swing window. Graphics functions are slightly different and more general than GWBASIC. Graphics statements/functions require a '-g' flag to be set at runtime. See Graphics section in reference. PRINT/WRITE statements are displayed on standard out only. For displaying text on Swing window, new statements are added.
DIM
DIM statement declares size of each dimension (rather than maximum value of the dimension).
Data Types
PuffinBASIC supports scalar, array, struct, list, set and dict types. Int32, Int4, Float32, Float64, and String are the scalar types. Each scalar type has a corresponding array type.
Commands
PuffinBASIC does not support BASIC Commands, such as LIST, RUN, etc.
Errors
PuffinBASIC can raise following kind of errors:
- PuffinBasicSyntaxError: if source code has lexical or parsing error, e.g. missing parenthesis.
- PuffinBasicSemanticError: if source code has a semantic error, e.g. data type mismatch.
- PuffinBasicRuntimeError: if a runtime error happens, e.g. division by zero, IO error, etc.
- PuffinBasicInternalError: if there is a problem with PuffinBASIC implementation.
Data Types
Scalar Types
-
Int32 (32-bit signed integer): Int32 constants can have an optional '%' suffix. Int32 constants can be decimal, octal or hexadecimal. Octal numbers must have '&' or '&O' prefix, e.g. &12 or &O12. Hexadecimal numbers must have '&H' prefix, e.g. &HFF.
-
Int64 (64-bit signed integer): Int64 constants must have '@' suffix. Int64 constants can be decimal, octal or hexadecimal.
-
Float32 (32-bit signed IEEE-754 floating-point): Float32 constants can have an optional '!' suffix. Float32 constants can use a decimal format or scientific notations, e.g. 1.2 or 1.2E-2.
-
Float64 (64-bit signed IEEE-754 floating-point): Float64 constants can have an optional '#' suffix. Float32 constants can use a decimal format or scientific notations, e.g. 1.2 or 1.2E-2.
-
String: String stores any non-newline (or carriage return) ASCII character. A string must be enclosed within double-quotes, e.g. "A TEST, STRING". There is no limit on the length of a string.
Array Types
- N-dimensional Int32 Array
- N-dimensional Int64 Array
- N-dimensional Float32 Array
- N-dimensional Float64 Array
- N-dimensional String Array
Composite Types
- Struct: User defined type composed of scalar, array, struct, list, set and dict types.
- List: Variable length list of scalar and struct values.
- Set: Unordered open hash-set of scalar values.
- Dict: Unordered open hash-map of scalar to scalar and struct values.
Type conversion
Numeric types are converted into one another via implicit type conversion. String and numeric types are not implicitly converted. Use STR$ or VAL functions for conversion between String and numeric values.
Variables
A variable name must start with a letter followed by one or more letters or numeric digits. A variable name must not start with 'FN' because it is reserved for user defined functions.
A variable name has an optional suffix which
Related Skills
node-connect
339.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
339.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.8kCommit, push, and open a PR
