Modern QBasic/QuickBASIC Compiler
QBNex is a modern QBasic compiler built to keep the QuickBASIC/QBasic spirit alive. It stays true to classic BASIC compatibility, so existing ideas and code patterns still feel familiar, while adding self-hosted performance, OpenGL graphics, modern syntax, and native binaries for Windows, Linux, and macOS.
- QBNex
- Table of Contents
- About
- Features
- System Requirements
- Installation
- Quick Start
- Usage Guide
- Docker Complete Guide
- Standard Library Reference
- Testing & Verification
- Compilation Pipeline
- Troubleshooting Comprehensive Guide
- Code Examples
- Supported QBasic Commands
- Development
- Contributing
- License
- Acknowledgments
- Links
QBNex is a modern QBasic/QuickBASIC compiler that translates BASIC source code into optimized C++ and compiles to native binaries for Windows, Linux, and macOS. It was significantly refactored from QB64 to act as a sleek, CLI-driven compiler without the legacy IDE components.
Version: See release tags and qb --version
The compiler is self-hosting, written in QBNex BASIC itself (~26,000 lines), and supports 150+ QBasic/QB64 keywords. It features comprehensive graphics via OpenGL/FreeGLUT, sound synthesis via miniaudio, and full file I/O operations.
Repository: https://github.com/thirawat27/QBNex
Additional documentation is being consolidated into this README and CONTRIBUTING.md.
-
Comprehensive Language Support
- Full support for classic QBasic/QB4.5 syntax (100% backward compatible)
- Modern extended syntax:
IMPORT module,x += 1,# comments - AS TYPE syntax:
FUNCTION name AS STRINGinstead ofFUNCTION name$ - User-defined types (TYPE...END TYPE) with nested structures
- Subroutines and functions with parameters
- Multi-dimensional arrays with REDIM PRESERVE
- Standard library imports via
IMPORT moduleor'$IMPORT:'module.name' - 150+ QBasic/QB64 keywords and functions
- Extended data types: BIT, BYTE, _INTEGER64, _FLOAT, OFFSET (pointers)
- Unsigned integer types (UNSIGNED BYTE, UNSIGNED INTEGER, UNSIGNED LONG, UNSIGNED _INTEGER64)
-
Modern Execution
- Self-hosting compiler written in QBNex BASIC (~26,000 lines)
- Transpiles BASIC source to optimized C++ before compilation
- Compiles to native binaries for maximum performance
- CLI-driven compilation optimized for modern terminal workflows
- Cross-platform support: Windows (x64 release builds, x86/x64 source builds), Linux, macOS
-
Advanced Graphics & Sound
- OpenGL-based graphics subsystem with FreeGLUT
- Automatic detection of graphics/sound features
- SCREEN modes with VGA and hi-res graphics support (SCREEN 0-12+)
- Drawing primitives (LINE, CIRCLE, PAINT, DRAW with macro strings)
- Image manipulation (GET/PUT with PSET, AND, OR, XOR transfer modes)
- TrueType font rendering via FreeType library
- Image format loading (BMP, PCX, PNG, JPEG, etc.) via STB Image
- Sound synthesis via miniaudio library (SOUND, PLAY, BEEP)
- 4-voice polyphonic SOUND/PLAY synthesis via
_VOICE - ADSR envelopes via
_ADSR attack, decay, sustain, release - Custom and noise waveforms via
_WAVE(SINE,SQUARE,SAW,PINK,BROWN,LFSR,CUSTOM:...) - Cross-platform audio: ALSA (Linux), CoreAudio (macOS), Windows Multimedia
-
Network Capabilities
- TCP/IP networking with socket support
- Server sockets (
_OPENHOST) - Client connections (
_OPENCLIENT) - Connection management functions
- Conditional compilation via DEPENDENCY_SOCKETS
-
File System Operations
- Sequential file access (OPEN, PRINT #, WRITE #, INPUT #, LINE INPUT #)
- Random file access (OPEN FOR RANDOM, GET, PUT, FIELD, LSET, RSET)
- Binary file access (OPEN FOR BINARY, GET, PUT)
- Directory operations (MKDIR, CHDIR, RMDIR)
- File management (KILL, NAME...AS, FILES)
- Binary load/save (BLOAD, BSAVE)
-
Developer Tools
- Compiler version tracking (
-vflag) - Help and documentation (
-h,--help) - Examples display (
-gflag) - Warning system with
-wflag - Quiet mode (
-qflag) - Monochrome output option (
-mflag) - Settings management (
-sflag) - Pre-compiled content purge (
-pflag) - C code generation without compilation (
-zflag) - OPTION _EXPLICIT enforcement (
-eflag) - Compile and run immediately (
-xflag) - Custom output naming (
-oflag) - Debug mode with GDB information
- Configurable compiler settings via INI file
- Compiler version tracking (
-
System Integration
- Timer and date/time functions
- Command-line argument access
- Environment variable queries
- Low-level memory operations (PEEK, POKE, DEF SEG, VARPTR)
- Process control (SHELL, CHAIN, CALL, CALL ABSOLUTE)
- Error handling with ON ERROR GOTO, RESUME
Windows:
- Windows 7 or newer (32-bit or 64-bit)
- No additional setup required (MinGW is downloaded automatically by setup script)
- Recommended: Whitelist QBNex folder in antivirus software
macOS:
- macOS with Xcode Command Line Tools installed
- Install with:
xcode-select --install - OpenGL and GLUT libraries (typically pre-installed)
- CoreAudio for sound output
Linux:
- GNU C++ compiler (
g++) - OpenGL development libraries (
libglu1-mesa-dev) - ALSA development libraries (
libasound2-dev) - FreeGLUT development libraries
- X11 libraries
- ncurses library
Core Dependencies (installed automatically or required):
- OpenGL, GLU, GLEW, FreeGLUT (graphics)
- miniaudio library (audio)
- FreeType (TrueType fonts, optional)
- STB Image (image format loading)
Platform-Specific:
- Windows: MinGW g++ (auto-downloaded), Windows Multimedia library
- Linux: ALSA (
libasound2-dev), X11 - macOS: CoreAudio, Apple GLUT, Cocoa
Optional:
- ZLIB (compression support)
- Socket libraries (networking support)
Download the appropriate package for your operating system from the repository releases page, or build from source using the provided setup scripts. Tagged releases publish per-platform archives such as qbnex_<tag>_lnx.tar.gz, qbnex_<tag>_osx.tar.gz, and qbnex_<tag>_win-x64.zip.
windows-x86 / 32-bit packages are not published by the GitHub release workflow. If you need a 32-bit Windows build, compile it locally with the scripts in this repository by running setup_win.cmd and choosing the 32-bit MinGW toolchain, or by setting QBNEX_MINGW_ARCH=x86 before running the script.
Extract the package to a folder with full write permissions.
It is advisable to whitelist the QBNex folder in your antivirus or antimalware software.
Building from source:
setup_win.cmdTo force a 32-bit Windows build without the interactive prompt:
set QBNEX_MINGW_ARCH=x86
setup_win.cmdThe setup script will:
- Download MinGW compiler (64-bit or 32-bit based on your choice)
- Build library files (LibQB, FreeType, FreeGLUT)
- Compile the QBNex compiler
- Create
qb.exein the project root
Note: The script downloads ~150MB of MinGW binaries. Internet connection required.
Install the Xcode command line tools first:
xcode-select --installRun the setup script:
chmod +x setup_osx.command
./setup_osx.commandThe script will:
- Verify Xcode Command Line Tools are available
- Build the bundled runtime objects required for macOS
- Build the stage0 compiler and self-host the final compiler
- Create
qbin the project root
Required components: Xcode Command Line Tools, OpenGL, GLUT, CoreAudio, Cocoa
Run the setup script:
chmod +x setup_lnx.sh
./setup_lnx.shRequired packages:
# Debian/Ubuntu
sudo apt-get install g++ x11-utils mesa-common-dev libglu1-mesa-dev libasound2-dev zlib1g-dev libncurses-dev
# Fedora/RHEL
sudo dnf install gcc-c++ xmessage mesa-libGLU-devel alsa-lib-devel zlib-devel ncurses-devel
# Arch Linux
sudo pacman -S gcc xorg-xmessage glu alsa-lib zlib ncursesThe setup script compiles all libraries and creates the qb compiler binary.
QBNex provides Docker support for consistent cross-platform builds without installing dependencies locally.
Quick Start:
# Build the Docker image
docker build -t qbnex .
# Or using docker-compose
docker-compose buildCompile a program:
# Linux/macOS
docker run --rm -v $(pwd):/project qbnex qb yourfile.bas
# Windows (PowerShell)
docker run --rm -v ${PWD}:/project qbnex qb yourfile.bas
# Windows (Command Prompt)
docker run --rm -v %cd%:/project qbnex qb yourfile.basCompile and run immediately:
docker run --rm -v $(pwd):/project qbnex qb yourfile.bas -xFull Docker documentation: See the Docker Complete Guide section below.
Windows:
git clone https://github.com/thirawat27/QBNex.git
cd QBNex
setup_win.cmdLinux:
git clone https://github.com/thirawat27/QBNex.git
cd QBNex
chmod +x setup_lnx.sh
./setup_lnx.shmacOS:
git clone https://github.com/thirawat27/QBNex.git
cd QBNex
chmod +x setup_osx.command
./setup_osx.commandDocker:
git clone https://github.com/thirawat27/QBNex.git
cd QBNex
docker build -t qbnex .
docker run --rm -v $(pwd):/project qbnex qb source/qbnex.bas -wQBNex ships with a supported incremental CMake workflow for developers who want IDE-friendly builds while keeping the existing platform setup scripts intact.
cmake -S . -B build
cmake --build build --target qbnexThis produces qb-stage0 / qb-stage0.exe in the repository root.
To run the self-hosting phase through CMake:
cmake --build build --target qb-selfhostThis generates the final compiler binary (qb or qb.exe) in the repository root.
By default, qb-selfhost also removes qb-stage0 / qb-stage0.exe after the final compiler is generated, matching the behavior of the setup scripts.
Using presets (recommended):
# Linux
cmake --preset linux-gcc
cmake --build --preset linux-gcc-stage0
# macOS
cmake --preset macos-clang
cmake --build --preset macos-clang-stage0
# Windows (after setup_win.cmd)
cmake --preset windows-mingw
cmake --build --preset windows-mingw-stage0Notes:
- The CMake flow reuses existing runtime setup scripts under
internal/c/*/os/*/setup_build.*. - Existing
setup_win.cmd,setup_lnx.sh, andsetup_osx.commandscripts remain available for bootstrapping and troubleshooting. - On Windows, CMake currently expects a MinGW-based toolchain.
qbnexis the stable top-level target. It buildsqb-stage0by default, orqb-selfhostwhen configured with-DQBNEX_SELF_HOST=ON.- To keep stage0 for debugging, configure with
-DQBNEX_KEEP_STAGE0=ON.
Windows MinGW example:
setup_win.cmd
set PATH=%CD%\internal\c\c_compiler\bin;%PATH%
cmake -S . -B build -G "MinGW Makefiles"
cmake --build build --target qb-stage0Get up and running with QBNex in 3 simple steps:
1. Create a BASIC program (hello.bas):
PRINT "Hello, QBNex!"
PRINT "Welcome to modern BASIC programming!"
FOR i = 1 TO 5
PRINT "Count: "; i
NEXT i
PRINT "Done!"2. Compile it:
qb hello.bas3. Run the executable:
# Windows
hello.exe
# Linux/macOS
./helloThat's it! You've successfully compiled your first QBNex program.
Use qb or qbnex as the command name (depending on your setup):
# Compile to executable (creates hello.exe or ./hello)
qb hello.bas
# Compile with custom output name
qb hello.bas -o myprogram.exe
# Compile and run immediately
qb hello.bas -x
# Generate C code without compiling
qb hello.bas -z
# Show compiler version
qb --version
# Show help
qb --helpCommand Pattern:
qb <source.bas> [flags]| Flag | Aliases | Description | Example |
|---|---|---|---|
-h |
--help |
Show help information | qb -h |
-v |
--version |
Show compiler version | qb -v |
-i |
--info, --about |
Show project information | qb -i |
-g |
--examples |
Show common CLI examples | qb -g |
-c |
- | Compile the source file (default) | qb file.bas -c |
-o |
- | Specify output filename | qb file.bas -o myapp.exe |
-x |
- | Compile with console-mode CLI behavior | qb file.bas -x |
-w |
- | Show warnings during compilation | qb file.bas -w |
-Werror |
--warnings-as-errors |
Promote warnings to blocking diagnostics | qb file.bas --warnings-as-errors |
-q |
- | Quiet mode (minimal output) | qb file.bas -q |
-m |
- | Monochrome (no color) output | qb file.bas -m |
-d |
--verbose-errors |
Legacy alias for detailed diagnostics (enabled by default) | qb file.bas |
-k |
--compact-errors |
Use compact diagnostics (hide detailed notes) | qb file.bas -k |
-e |
- | Enable OPTION _EXPLICIT | qb file.bas -e |
-s |
- | View/edit compiler settings | qb -s:DebugInfo=true |
-p |
- | Purge all pre-compiled content | qb file.bas -p |
-z |
- | Generate C code only (no exe) | qb file.bas -z |
Recommended for debugging diagnostics:
# Detailed diagnostics are enabled by default
qb myprogram.bas
# Combine warnings when chasing follow-on failures
qb myprogram.bas -w
# Switch back to compact diagnostics when you only want headline + source snippet
qb myprogram.bas --compact-errorsDetailed output sections used by the modern QBNex formatter (cause, example, where, while, and related context notes) are now shown by default. -d and --verbose-errors are kept as backward-compatible aliases.
Combining Flags:
# Compile with warnings and custom output
qb myprogram.bas -w -o myapp.exe
# Quiet mode, generate C code only
qb myprogram.bas -q -z
# Compile, run immediately, with explicit mode
qb myprogram.bas -e -xQBNex supports configurable compiler settings via the -s flag or internal/config.ini:
Available Settings:
| Setting | Values | Default | Description |
|---|---|---|---|
SaveExeWithSource |
true/false |
false |
Include source code in compiled executable |
IgnoreWarnings |
true/false |
false |
Suppress warning messages during compilation |
DebugInfo |
true/false |
false |
Include GDB debugging information in output |
Viewing Settings:
# View all current settings
qb -s
# Output shows:
# SaveExeWithSource = false
# IgnoreWarnings = false
# DebugInfo = falseModifying Settings:
# Enable debug mode for GDB
qb -s:DebugInfo=true
# Disable warnings
qb -s:IgnoreWarnings=true
# Include source in executable
qb -s:SaveExeWithSource=true
# Combine multiple settings
qb -s:DebugInfo=true -s:IgnoreWarnings=falseUsing Config File:
Edit internal/config.ini directly:
[Compiler]
SaveExeWithSource=false
IgnoreWarnings=false
DebugInfo=falseWhen to Use Each Setting:
- DebugInfo=true: When debugging with GDB, adds symbol information
- IgnoreWarnings=true: For clean output in CI/CD pipelines
- SaveExeWithSource=true: For distributing source with binary
QBNex supports both traditional and modern import syntax for the bundled standard library:
Modern Syntax (Recommended):
' Modern import syntax - cleaner and easier to read
IMPORT qbnex
IMPORT json
IMPORT urlTraditional Syntax (Still Supported):
' Traditional import syntax
'$IMPORT:'qbnex'
'$IMPORT:'json'
'$IMPORT:'url'Basic Import Syntax:
' Import individual modules
'$IMPORT:'sys.env'
'$IMPORT:'io.path'
'$IMPORT:'strings.text'
'$IMPORT:'collections.list'
'$IMPORT:'math.numeric'Import Core Library:
' Import the full qbnex stdlib core (place at top of file)
'$IMPORT:'qbnex'
' Now you can use QBNex_ObjectHeader, QBNex_List, etc.
DIM myList AS QBNex_List
List_Init myList
List_Add myList, "Hello"Best Practices:
- Function-only imports: Place at end of file
SUB Main ()
PRINT Env_Platform$
PRINT Path_Join$("root", "demo.txt")
END SUB
'$IMPORT:'sys.env'
'$IMPORT:'io.path'- Full core imports: Place at top of file (for TYPE, CLASS, SUB, FUNCTION)
'$IMPORT:'qbnex'
CLASS Dog
Name AS STRING * 32
CONSTRUCTOR (petName AS STRING)
ME.Name = petName
END CONSTRUCTOR
END CLASSModern QBNex Syntax (QBasic + Extended):
QBNex now supports a modern, cleaner syntax while maintaining full backward compatibility with traditional QBasic:
| Feature | Modern Syntax | Traditional Syntax | Description |
|---|---|---|---|
| Import | IMPORT module |
'$IMPORT:'module' |
Import stdlib modules |
| Function Type | FUNCTION name AS STRING |
FUNCTION name$ |
AS TYPE syntax |
| Short Function | FUNC name() |
FUNCTION name() |
Shorter declaration |
| Single-line Func | DEF name(x) = x*2 |
FUNCTION...END FUNCTION |
Lambda-like syntax |
| Augmented Assign | x += 1 |
x = x + 1 |
+= -= *= /= operators |
| Alternative Comment | # comment |
' comment |
# for comments |
Modern API Examples:
# Import standard library
IMPORT qbnex
# JSON handling
json = json_parse("{""name"": ""John""}")
name = json_get_str(json)
output = json_string(json)
# URL encoding/decoding
encoded = encode("hello world")
decoded = decode(encoded)
# Augmented assignment (like other modern languages)
count += 1 # count = count + 1
total -= 5 # total = total - 5
value *= 2 # value = value * 2
average /= n # average = average / nAvailable Standard Library Modules:
| Module | Modern Import | Description |
|---|---|---|
| QBNex Core | IMPORT qbnex |
Full stdlib with JSON and URL helpers |
| JSON | Built-in | json_parse, json_string, json_obj, json_array |
| URL | Built-in | encode, decode, url_parse, path_join |
Example: Using Collections
'$IMPORT:'qbnex'
SUB Demo_Collections ()
DIM modules AS QBNex_List
DIM loadOrder AS QBNex_Queue
DIM features AS QBNex_HashSet
DIM history AS QBNex_Stack
DIM report AS QBNex_StringBuilder
List_Init modules
Queue_Init loadOrder
HashSet_Init features
Stack_Init history
SB_Init report
' Add items
List_Add modules, "collections.list"
List_Add modules, "strings.strbuilder"
List_Add modules, "sys.env"
Queue_Enqueue loadOrder, "core"
Queue_Enqueue loadOrder, "collections"
HashSet_Add features, "OOP"
HashSet_Add features, "Collections"
Stack_Push history, "init"
Stack_Push history, "ready"
' Build report
SB_AppendLine report, "Loaded modules:"
SB_AppendLine report, " " + List_Join$(modules, ", ")
SB_AppendLine report, "Queue head: " + Queue_Peek$(loadOrder)
SB_AppendLine report, "Set members: " + HashSet_ToString$(features, " | ")
SB_AppendLine report, "Latest stack item: " + Stack_Peek$(history)
PRINT SB_ToString$(report)
' Clean up
SB_Free report
Stack_Free history
HashSet_Free features
Queue_Free loadOrder
List_Free modules
END SUBExample: Using System Modules
SUB Demo_System ()
DIM nowValue AS QBNex_Date
Date_SetNow nowValue
PRINT "Platform: "; Env_Platform$
PRINT "64-bit: "; Env_Is64Bit&
PRINT "Home: "; Env_GetHome$
PRINT "Joined path: "; Path_Join$(Env_GetHome$, "qbnex/demo/output.txt")
PRINT "File name: "; Path_FileName$("src/stdlib/demo.bas")
PRINT "Arg count: "; Args_Count&
PRINT "Date ISO: "; Date_ToISOString$(nowValue)
END SUB
'$IMPORT:'strings.text'
'$IMPORT:'sys.env'
'$IMPORT:'sys.args'
'$IMPORT:'sys.datetime'
'$IMPORT:'io.path'Example: Using I/O Modules
SUB Demo_Data ()
DIM metadata AS QBNex_Dictionary
DIM outcome AS QBNex_Result
Dict_Init metadata
Dict_Set metadata, "name", "QBNex"
Dict_Set metadata, "layer", "stdlib"
PRINT "Dict count: "; Dict_Count&(metadata)
PRINT "Dict name: "; Dict_Get$(metadata, "name", "")
PRINT "JSON sample: "; Json_Object3$("name", Json_String$(Dict_Get$(metadata, "name", "")), "layer", Json_String$(Dict_Get$(metadata, "layer", "")), "status", Json_String$("ok"))
Result_Ok outcome, "stable"
PRINT "Result ok: "; Result_IsOk&(outcome)
PRINT "Result value: "; Result_Value$(outcome, "")
Dict_Free metadata
END SUB
'$IMPORT:'io.json'
'$IMPORT:'error.result'QBNex provides Docker containers for easy deployment without local dependencies installation. This is the recommended approach for CI/CD, reproducible builds, and development environments.
Building the Image:
# Build using Dockerfile directly
docker build -t qbnex .
# Or using docker-compose (recommended)
docker-compose buildRunning Basic Commands:
# Compile a BASIC program
docker run --rm -v $(pwd):/project qbnex qb yourfile.bas
# Show help
docker run --rm qbnex qb --help
# Show version
docker run --rm qbnex qb --version
# List examples
docker run --rm qbnex qb --examplesPlatform-Specific Volume Mounting:
# Linux/macOS
docker run --rm -v $(pwd):/project qbnex qb yourfile.bas
# Windows PowerShell
docker run --rm -v ${PWD}:/project qbnex qb yourfile.bas
# Windows Command Prompt
docker run --rm -v %cd%:/project qbnex qb yourfile.basdocker-compose.yml provides simplified management:
version: '3.8'
services:
qbnex:
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/project
working_dir: /projectUsing Docker Compose:
# Build the image
docker-compose build
# Compile a program
docker-compose run --rm qbnex qb yourfile.bas
# Compile and run immediately
docker-compose run --rm qbnex qb yourfile.bas -x
# Generate C code without compiling
docker-compose run --rm qbnex qb yourfile.bas -z
# Compile with custom output name
docker-compose run --rm qbnex qb yourfile.bas -o myprogram
# Compile with warnings
docker-compose run --rm qbnex qb yourfile.bas -w
# Quiet mode compilation
docker-compose run --rm qbnex qb yourfile.bas -qComplete Workflow Example:
# 1. Build image once
docker-compose build
# 2. Create hello.bas
echo 'PRINT "Hello from Docker!"' > hello.bas
# 3. Compile and run
docker-compose run --rm qbnex qb hello.bas -x
# 4. Check compiled binary
ls -la hello
./helloGraphics programs require X11 display forwarding to work. Here's how to set it up:
Linux (X11):
# Allow Docker to access X11
xhost +local:docker
# Run with display forwarding
docker run --rm \
-v $(pwd):/project \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
qbnex qb graphics.bas -x
# Or with docker-compose
docker-compose run --rm \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
qbnex qb graphics.bas -xmacOS (XQuartz):
# 1. Install XQuartz
brew install --cask xquartz
# 2. Start XQuartz and enable network connections
# Open XQuartz → Preferences → Security →
# Check "Allow connections from network clients"
# 3. Run with display forwarding
docker run --rm \
-v $(pwd):/project \
-e DISPLAY=host.docker.internal:0 \
qbnex qb graphics.bas -xWindows (VcXsrv):
# 1. Install VcXsrv X Server
# Download from: https://sourceforge.net/projects/vcxsrv/
# 2. Start VcXsrv with "Disable access control" checked
# 3. Run with display forwarding
docker run --rm \
-v ${PWD}:/project \
-e DISPLAY=host.docker.internal:0 \
qbnex qb graphics.bas -xGraphics Demo Example:
' graphics.bas
SCREEN 12
COLOR 15, 1
CLS
PRINT "QBNex Graphics in Docker!"
PRINT
' Draw shapes
LINE (50, 50)-(300, 200), 14, B
CIRCLE (400, 125), 75, 12
FOR i = 0 TO 639 STEP 20
LINE (i, 0)-(639 - i, 479), 9
NEXT i
PRINT "Graphics working!"
SLEEP
SCREEN 0Compile and run:
docker-compose run --rm -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix qbnex qb graphics.bas -xFor TCP/IP networking, use host network mode:
# Using docker run with host network
docker run --rm \
-v $(pwd):/project \
--network host \
qbnex qb server.bas -x
# Using docker-compose with host network
docker-compose run --rm --network host qbnex qb server.bas -xServer Example:
' server.bas
DIM serverHandle AS LONG
DIM clientHandle AS LONG
serverHandle = _OPENHOST("TCP/IP:8080")
IF serverHandle = 0 THEN
PRINT "Failed to create server"
END
END IF
PRINT "Server listening on port 8080..."
DO
clientHandle = _OPENCONNECTION(serverHandle)
IF clientHandle > 0 THEN EXIT DO
SLEEP 1
LOOP
PRINT "Client connected!"
CLOSE #clientHandle
CLOSE #serverHandleClient Example:
' client.bas
DIM clientHandle AS LONG
clientHandle = _OPENCLIENT("TCP/IP:8080:localhost")
IF clientHandle = 0 THEN
PRINT "Failed to connect"
END
END IF
PRINT "Connected to server"
PRINT #clientHandle, "Hello!"
CLOSE #clientHandleRun server:
docker-compose run --rm --network host qbnex qb server.bas -xRun client (in another terminal):
docker-compose run --rm --network host qbnex qb client.bas -xFor development with shell access:
# Start interactive shell
docker-compose run --rm qbnex bash
# Inside container:
# - Compile programs: qb myfile.bas
# - Run programs: ./myfile
# - List files: ls -la
# - Access source: /project (mounted from host)Interactive Session Example:
$ docker-compose run --rm qbnex bash
root@abc123:/project# ls
hello.bas test.bas
root@abc123:/project# qb hello.bas
Compiling...
Build complete: hello
root@abc123:/project# ./hello
Hello from QBNex!
root@abc123:/project# exitDevelopment Mode with Extended Image:
Use Dockerfile.dev for development with build cache:
# Build development image (~500MB with build cache)
docker build -f Dockerfile.dev -t qbnex-dev .
# Run with dev image
docker run --rm -v $(pwd):/project qbnex-dev qb yourfile.bas -zDockerfile (Production):
FROM ubuntu:22.04
# Install dependencies
RUN apt-get update && apt-get install -y \
build-essential \
libglu1-mesa-dev \
libasound2-dev \
freeglut3-dev \
libx11-dev \
libncurses5-dev \
&& rm -rf /var/lib/apt/lists/*
# Copy QBNex source
WORKDIR /opt/qbnex
COPY . .
# Build compiler
RUN chmod +x setup_lnx.sh && ./setup_lnx.sh
# Add to PATH
ENV PATH="/opt/qbnex:${PATH}"
# Default command
CMD ["bash"]Dockerfile.dev (Development):
FROM ubuntu:22.04
# Install dependencies with build cache preserved
RUN apt-get update && apt-get install -y \
build-essential \
libglu1-mesa-dev \
libasound2-dev \
freeglut3-dev \
libx11-dev \
libncurses5-dev \
gdb \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /opt/qbnex
COPY . .
# Build with debug info
RUN chmod +x setup_lnx.sh && ./setup_lnx.sh
ENV PATH="/opt/qbnex:${PATH}"
CMD ["bash"]docker-compose.yml:
services:
qbnex:
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/project
working_dir: /project
# Optional: Enable host networking
# network_mode: host
# Optional: Enable graphics
# environment:
# - DISPLAY=${DISPLAY}
# volumes:
# - /tmp/.X11-unix:/tmp/.X11-unixCI/CD Integration:
# Example Docker workflow snippet
name: Docker Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@<ref>
- name: Build Docker image
run: docker build -t qbnex .
- name: Compile test program
run: docker run --rm -v $(pwd):/project qbnex qb test.bas
- name: Run tests
run: docker run --rm -v $(pwd):/project qbnex qb test.bas -xPermission Issues:
# Fix permissions after compilation
sudo chmod +x ./yourprogram
# Or run with specific user
docker run --rm -v $(pwd):/project -u $(id -u):$(id -g) qbnex qb yourfile.basMissing Libraries:
# Check installed packages in container
docker run --rm qbnex dpkg -l | grep -E "libgl|libasound|x11"
# Rebuild without cache
docker-compose build --no-cache qbnexDisplay/Graphics Issues:
# Test X11 forwarding
docker run --rm -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix xterm
# Check DISPLAY variable
echo $DISPLAY
# Allow Docker access (Linux)
xhost +local:dockerNetwork Issues:
# Test network connectivity
docker run --rm --network host qbnex ping localhost
# Check if port is available
docker run --rm --network host qbnex netstat -tulpnVolume Mount Issues:
# Verify mounted files
docker run --rm -v $(pwd):/project qbnex ls -la /project
# Use absolute path
docker run --rm -v /absolute/path:/project qbnex qb yourfile.basDocker Files Summary:
| File | Purpose | Size | Use Case |
|---|---|---|---|
Dockerfile |
Production image | ~300MB | Final builds, CI/CD |
Dockerfile.dev |
Development image | ~500MB | Development, debugging |
.dockerignore |
Build exclusions | - | Optimizes build context |
docker-compose.yml |
Easy management | - | Recommended workflow |
Best Practices:
- Use docker-compose: Simplifies commands and configuration
- Volume mounting: Keep source on host, compile in container
- --rm flag: Clean up containers after use
- Rebuild periodically: Update dependencies with
docker-compose build --no-cache - Use dev image for debugging: Includes GDB and build artifacts
Security Notes:
- Docker containers run as root by default
- Use
-u $(id -u):$(id -g)for non-root execution - Review Dockerfile for any security concerns
- Don't expose ports unless necessary
- Use
.dockerignoreto exclude sensitive files
QBNex includes a comprehensive standard library with modern data structures, I/O utilities, and OOP support. All libraries use Python-style import syntax and are located in source/stdlib/.
Dynamic array with automatic resizing.
Functions:
List_Init(list)- Initialize listList_Add(list, item)- Add item to endList_Insert(list, index, item)- Insert at positionList_Remove(list, index)- Remove at positionList_Get$(list, index)- Get item as stringList_Count&(list)- Get item countList_Join$(list, separator$)- Join items with separatorList_Free(list)- Free memory
Example:
DIM myList AS QBNex_List
List_Init myList
List_Add myList, "Apple"
List_Add myList, "Banana"
List_Add myList, "Cherry"
PRINT "Items: "; List_Count&(myList)
PRINT "All: "; List_Join$(myList, ", ")
' Output: Items: 3
' Output: All: Apple, Banana, Cherry
List_Free myListLIFO (Last In, First Out) data structure.
Functions:
Stack_Init(stack)- Initialize stackStack_Push(stack, item)- Push item onto stackStack_Pop$(stack)- Remove and return top itemStack_Peek$(stack)- View top item without removingStack_Count&(stack)- Get item countStack_Free(stack)- Free memory
Example:
DIM history AS QBNex_Stack
Stack_Init history
Stack_Push history, "init"
Stack_Push history, "registry"
Stack_Push history, "ready"
PRINT "Latest: "; Stack_Peek$(history)
PRINT "Count: "; Stack_Count&(history)
' Output: Latest: ready
' Output: Count: 3
Stack_Free historyFIFO (First In, First Out) data structure.
Functions:
Queue_Init(queue)- Initialize queueQueue_Enqueue(queue, item)- Add item to backQueue_Dequeue$(queue)- Remove and return front itemQueue_Peek$(queue)- View front itemQueue_Count&(queue)- Get item countQueue_Free(queue)- Free memory
Example:
DIM loadOrder AS QBNex_Queue
Queue_Init loadOrder
Queue_Enqueue loadOrder, "core"
Queue_Enqueue loadOrder, "collections"
Queue_Enqueue loadOrder, "text"
PRINT "Next: "; Queue_Peek$(loadOrder)
' Output: Next: core
Queue_Free loadOrderHash-based collection with unique values.
Functions:
HashSet_Init(set)- Initialize setHashSet_Add(set, item)- Add item (returns 0 if duplicate)HashSet_Contains&(set, item)- Check if item existsHashSet_Remove(set, item)- Remove itemHashSet_Count&(set)- Get item countHashSet_ToString$(set, separator$)- Convert to stringHashSet_Free(set)- Free memory
Example:
DIM features AS QBNex_HashSet
HashSet_Init features
HashSet_Add features, "OOP"
HashSet_Add features, "Collections"
HashSet_Add features, "OOP" ' Duplicate, ignored
PRINT "Members: "; HashSet_ToString$(features, " | ")
PRINT "Count: "; HashSet_Count&(features)
' Output: Members: OOP | Collections
' Output: Count: 2
HashSet_Free featuresKey-value store with string keys.
Functions:
Dict_Init(dict)- Initialize dictionaryDict_Set(dict, key$, value$)- Set key-value pairDict_Get$(dict, key$, default$)- Get value by keyDict_Remove(dict, key$)- Remove key-value pairDict_Count&(dict)- Get item countDict_HasKey&(dict, key$)- Check if key existsDict_Free(dict)- Free memory
Example:
DIM metadata AS QBNex_Dictionary
Dict_Init metadata
Dict_Set metadata, "name", "QBNex"
Dict_Set metadata, "version", "current"
Dict_Set metadata, "kind", "compiler"
PRINT "Name: "; Dict_Get$(metadata, "name", "")
PRINT "Count: "; Dict_Count&(metadata)
' Output: Name: QBNex
' Output: Count: 3
Dict_Free metadataEfficient string concatenation for building large strings.
Functions:
SB_Init(sb)- Initialize string builderSB_Append(sb, text$)- Append textSB_AppendLine(sb, text$)- Append text with newlineSB_ToString$(sb)- Convert to stringSB_Free(sb)- Free memory
Example:
DIM report AS QBNex_StringBuilder
SB_Init report
SB_AppendLine report, "=== Report ==="
SB_AppendLine report, "Total items: 10"
SB_AppendLine report, "Status: OK"
SB_Append report, "End of report."
PRINT SB_ToString$(report)
' Output:
' === Report ===
' Total items: 10
' Status: OK
' End of report.
SB_Free reportString manipulation and formatting utilities.
Functions:
Text_PadLeft$(text$, length, padChar$)- Pad string on leftText_PadRight$(text$, length, padChar$)- Pad string on right- Additional text manipulation functions
Example:
PRINT Text_PadRight$("QBNex", 10, ".")
' Output: QBNex.....
PRINT Text_PadLeft$("123", 8, "0")
' Output: 00000123Cross-platform file path manipulation.
Functions:
Path_Join$(path1$, path2$)- Join path componentsPath_FileName$(path$)- Extract filenamePath_Directory$(path$)- Extract directoryPath_Extension$(path$)- Extract file extensionPath_WithoutExtension$(path$)- Remove extension
Example:
PRINT Path_Join$("src", "stdlib/demo.bas")
' Output: src/stdlib/demo.bas
PRINT Path_FileName$("src/stdlib/demo.bas")
' Output: demo.bas
PRINT Path_Extension$("src/stdlib/demo.bas")
' Output: .basCSV row creation and parsing.
Functions:
CSV_Row3$(col1$, col2$, col3$)- Create 3-column CSV row- Additional CSV parsing functions
Example:
PRINT CSV_Row3$("name", "score", "status")
' Output: name,score,status
PRINT CSV_Row3$("Alice", "100", "pass")
' Output: Alice,100,passJSON object creation.
Functions:
Json_Object3$(key1$, val1$, key2$, val2$, key3$, val3$)- Create JSON with 3 pairsJson_String$(text$)- Create JSON string valueJson_Number$(num$)- Create JSON number value- Additional JSON builders
Example:
PRINT Json_Object3$("name", Json_String$("QBNex"), "version", Json_String$("current"), "status", Json_String$("ok"))
' Output: {"name":"QBNex","version":"current","status":"ok"}Platform detection and environment variables.
Functions:
Env_Platform$- Get platform name (Windows/Linux/macOS)Env_Is64Bit&- Check if 64-bit platformEnv_GetHome$- Get home directory- Additional environment functions
Example:
PRINT "Platform: "; Env_Platform$
PRINT "64-bit: "; Env_Is64Bit&
PRINT "Home: "; Env_GetHome$
' Output (Linux):
' Platform: Linux
' 64-bit: 1
' Home: /home/userCommand-line argument access.
Functions:
Args_Count&- Get argument countArgs_Get$(index)- Get argument at index
Example:
PRINT "Argument count: "; Args_Count&
FOR i = 0 TO Args_Count& - 1
PRINT "Arg "; i; ": "; Args_Get$(i)
NEXT iDate and time utilities.
Functions:
Date_SetNow(date)- Set to current timeDate_ToISOString$(date)- Convert to ISO 8601 stringDate_GetFullYear&(date)- Get yearDate_GetMonth&(date)- Get monthDate_GetDay&(date)- Get dayDate_NowMs#- Get current timestamp in milliseconds
Example:
DIM now AS QBNex_Date
Date_SetNow now
PRINT "ISO: "; Date_ToISOString$(now)
PRINT "Year: "; Date_GetFullYear&(now)
PRINT "Timestamp: "; Date_NowMs#
' Output:
' ISO: 2026-04-13T19:30:00Z
' Year: 2026
' Timestamp: 1744564200000Mathematical helper functions.
Functions:
Math_Clamp#(value#, min#, max#)- Clamp value to range- Additional numeric utilities
Example:
PRINT Math_Clamp#(15#, 0#, 10#)
' Output: 10
PRINT Math_Clamp#(5#, 0#, 10#)
' Output: 5Structured error handling with typed failures, propagation context, and readable error chains.
Functions:
Result_Ok(result, value$)- Set successful resultResult_Fail(result, message$)- Set a generic error resultResult_FailCode(result, code&, message$)- Set an error with an explicit codeResult_FailWithContext(result, code&, message$, context$, source$)- Set an error with code, context, and source informationResult_AddContext(result, context$)- Prepend outer context while propagating an errorResult_SetSource(result, source$)- Record the subsystem, file, or module that emitted the errorResult_SetCause(result, cause$)- Attach the underlying cause textResult_Propagate(result, sourceResult, context$, source$)- Copy an error forward and add outer contextResult_IsOk&(result)- Check if result is OKResult_IsError&(result)- Check if result is an errorResult_Code&(result)- Get the error codeResult_Value$(result, default$)- Get result valueResult_Message$(result)- Get the error messageResult_Context$(result)- Get the accumulated context chainResult_Source$(result)- Get the source/subsystem textResult_Cause$(result)- Get the attached cause textResult_ErrorChain$(result)- Render a readable combined error chainResult_Describe$(result)- Describe the result in a readable single stringResult_Expect$(result, expectation$)- Abort with a panic-style message if the result is an error
Example:
DIM outcome AS QBNex_Result
DIM startup AS QBNex_Result
Result_Ok outcome, "stable"
IF Result_IsOk&(outcome) THEN
PRINT "Success: "; Result_Value$(outcome, "")
END IF
' Output: Success: stable
Result_FailWithContext outcome, 404, "Configuration file not found", "while reading settings.json", "config.loader"
Result_SetCause outcome, "startup profile is missing from the project root"
Result_Propagate startup, outcome, "while starting application", "startup"
IF Result_IsError&(startup) THEN
PRINT "Error: "; Result_ErrorChain$(startup)
END IF
' Output: Error: while starting application -> while reading settings.json: [E404] Configuration file not found [source=startup] | cause: startup profile is missing from the project rootQBNex supports object-oriented programming with classes, inheritance, and interfaces.
Class Declaration:
'$IMPORT:'qbnex'
CLASS Animal
Name AS STRING * 32
Age AS INTEGER
CONSTRUCTOR (petName AS STRING, petAge AS INTEGER)
ME.Name = petName
ME.Age = petAge
END CONSTRUCTOR
FUNCTION Describe$ ()
Describe$ = RTRIM$(ME.Name) + " (age " + STR$(ME.Age) + ")"
END FUNCTION
END CLASSInheritance:
CLASS Dog EXTENDS Animal
Breed AS STRING * 32
CONSTRUCTOR (petName AS STRING, petAge AS INTEGER, petBreed AS STRING)
ME.Breed = petBreed
END CONSTRUCTOR
FUNCTION Bark$ ()
Bark$ = "Woof!"
END FUNCTION
END CLASSUsing Classes:
DIM pet AS Dog
New_Dog pet, "Buddy", 3, "Collie"
PRINT "Name: "; pet.Name
PRINT "Describe: "; pet.Describe$
PRINT "Sound: "; pet.Bark$Interfaces:
IMPLEMENTS IPet
' Class implements IPet interface
' Can be checked with QBNEX_Implements&()Runtime OOP API:
QBNEX_RegisterClass$(className$, parentClassID)- Register classQBNEX_FindClass$(className$)- Find class by nameQBNEX_RegisterMethod(classID, methodName$, slot)- Register methodQBNEX_RegisterInterface(classID, interfaceName$)- Register interfaceQBNEX_ObjectInit(header, classID)- Initialize objectQBNEX_ObjectClassName$(header)- Get object class nameQBNEX_ObjectIs&(object, className$)- Check inheritanceQBNEX_Implements&(classID, interfaceName$)- Check interfaceQBNEX_FindMethodSlot&(classID, methodName$)- Find method slot
Example:
DIM pet AS Dog
New_Dog pet, "Buddy", 3, "Collie"
PRINT "Class: "; QBNEX_ObjectClassName$(pet.Header)
PRINT "Is Animal: "; QBNEX_ObjectIs&(pet.Header, "Animal")
PRINT "Is Dog: "; QBNEX_ObjectIs&(pet.Header, "Dog")
PRINT "Has IPet: "; QBNEX_Implements&(pet.Header.ClassID, "IPet")QBNex includes comprehensive test suites to verify compiler and library functionality.
QBNex ships with runnable smoke scripts under tests/ for both Windows (.cmd) and Linux/macOS (.sh).
Current script coverage includes:
- Diagnostics formatting and include-chain reporting
- CLI behavior (
--help,--version, invalid switches,-q,-z,-x, settings output) - Encoding handling (UTF-16 rejection, invalid UTF-8 rejection, BOM acceptance, empty source, spaced paths)
- Label resolution and stale-output protections
- Audio synth command parsing (
_VOICE,_ADSR,_WAVE, SOUND, PLAY) - Standard library import/include smoke checks
- Warning behavior and
--warnings-as-errorspromotion - Lightweight compile-time benchmark harness
Supporting fixture sources live in tests/fixtures/ (15 .bas files).
Smoke Tests:
# Linux/macOS
chmod +x tests/*.sh
./tests/diagnostics_smoke.sh
./tests/warnings_smoke.sh
./tests/labels_smoke.sh
./tests/encoding_smoke.sh
./tests/cli_smoke.sh
./tests/stdlib_smoke.sh
./tests/audio_smoke.sh
./tests/benchmark_smoke.sh:: Windows
tests\diagnostics_smoke.cmd
tests\warnings_smoke.cmd
tests\labels_smoke.cmd
tests\encoding_smoke.cmd
tests\cli_smoke.cmd
tests\stdlib_smoke.cmd
tests\audio_smoke.cmd
tests\benchmark_smoke.cmdDiagnostics Smoke Test Only (Windows):
tests\diagnostics_smoke.cmdDiagnostics Smoke Test Only (Linux/macOS):
chmod +x tests/diagnostics_smoke.sh
./tests/diagnostics_smoke.shCompiler Smoke Suite (Windows):
set QBNEX_CI=1
set QBNEX_BOOTSTRAP=1
setup_win.cmd
tests\diagnostics_smoke.cmd
tests\warnings_smoke.cmd
tests\labels_smoke.cmd
tests\encoding_smoke.cmd
tests\cli_smoke.cmd
tests\stdlib_smoke.cmdBenchmark Smoke Test (manual):
# Linux/macOS
chmod +x tests/benchmark_smoke.sh
./tests/benchmark_smoke.sh:: Windows
tests\benchmark_smoke.cmdCurrent CLI smoke coverage includes:
--help,--version, unknown switch, invalid output path- quiet mode (
-q) - settings output (
-s) - warnings-as-errors
- console-mode CLI flag (
-x) - C-generation mode (
-z)
This script validates both behaviors in one run:
- Default compilation output includes detailed markers such as
[!] causeand[+] examplewithout requiring-d --compact-errorshides those detailed sections and keeps output compact- Source fixture for the test is
tests/fixtures/diagnostics_compile_error.bas
Smoke and benchmark suites remain available for local validation and troubleshooting, but the default GitHub-hosted build workflows now focus on building and packaging artifacts rather than running these suites on every CI execution.
Success markers printed by the scripts:
DIAGNOSTICS_SMOKE_OKWARNINGS_SMOKE_OKLABELS_SMOKE_OKENCODING_SMOKE_OKCLI_SMOKE_OKSTDLIB_SMOKE_OKBENCHMARK_SMOKE_OK
Current compilation status:
✅ Successfully Compiling (100%)
- Collections: List, Stack, Queue, Set, Dictionary (5/5)
- Strings: StringBuilder, Text (2/2)
- I/O: CSV, JSON, Path (3/3)
- System: Args, DateTime, Env (3/3)
- Math: Numeric (1/1)
- Error: Result (1/1)
- OOP: Class, Interface (2/2)
- Core: qbnex_stdlib.bas (1/1)
The tests/stdlib_smoke.* scripts validate representative stdlib entry points using:
tests/fixtures/stdlib_import_success.bastests/fixtures/qbnex_stdlib_include_success.bastests/fixtures/url_import_success.bas
Smoke scripts report one `*_SMOKE_OK` marker each on success.
If a script fails, it prints `*_SMOKE_FAIL` with paths to captured output.
Use this checklist to verify your QBNex installation:
- Compiler executable exists:
qb --versionprints compiler version - Help works:
qb --helpdisplays usage - Diagnostics smoke:
tests\diagnostics_smoke.cmdor./tests/diagnostics_smoke.sh - Warnings smoke:
tests\warnings_smoke.cmdor./tests/warnings_smoke.sh - Labels smoke:
tests\labels_smoke.cmdor./tests/labels_smoke.sh - Encoding smoke:
tests\encoding_smoke.cmdor./tests/encoding_smoke.sh - CLI smoke:
tests\cli_smoke.cmdor./tests/cli_smoke.sh - Stdlib smoke:
tests\stdlib_smoke.cmdor./tests/stdlib_smoke.sh - Benchmark smoke (optional):
tests\benchmark_smoke.cmdor./tests/benchmark_smoke.sh - Docker (optional): Docker image builds successfully
For detailed testing information, see:
tests/*.cmd- Windows smoke/benchmark scriptstests/*.sh- Linux/macOS smoke/benchmark scriptstests/fixtures/*.bas- fixture programs used by the smoke suites
Simple Test Program:
' test_basic.bas
DIM failures AS LONG
' Test arithmetic
IF 2 + 2 <> 4 THEN failures = failures + 1
IF 10 - 5 <> 5 THEN failures = failures + 1
IF 3 * 4 <> 12 THEN failures = failures + 1
IF 20 / 4 <> 5 THEN failures = failures + 1
' Test strings
IF LEN("Hello") <> 5 THEN failures = failures + 1
IF LCASE$("HELLO") <> "hello" THEN failures = failures + 1
' Report results
IF failures = 0 THEN
PRINT "ALL_TESTS_PASSED"
SYSTEM 0
ELSE
PRINT "TEST_FAILURES: "; failures
SYSTEM 1
END IFLibrary Test:
' test_collections.bas
'$IMPORT:'qbnex'
DIM failures AS LONG
' Test List
DIM myList AS QBNex_List
List_Init myList
List_Add myList, "item1"
List_Add myList, "item2"
IF List_Count&(myList) <> 2 THEN failures = failures + 1
IF List_Get$(myList, 0) <> "item1" THEN failures = failures + 1
List_Free myList
' Test Stack
DIM stack AS QBNex_Stack
Stack_Init stack
Stack_Push stack, "first"
Stack_Push stack, "second"
IF Stack_Count&(stack) <> 2 THEN failures = failures + 1
IF Stack_Peek$(stack) <> "second" THEN failures = failures + 1
Stack_Free stack
' Report
IF failures = 0 THEN
PRINT "COLLECTION_TESTS_PASSED"
ELSE
PRINT "COLLECTION_TEST_FAILURES: "; failures
END IFRun test:
qb test_collections.bas -xQBNex uses GitHub Actions for automated builds and release packaging:
pull_request.yml: Builds compiler artifacts for Linux, macOS, and Windows x64 on pull requests targetingmainpush.yml: Runs the same three-target build matrix for pushes tomainrelease.yml: Runs on pushed tags matchingv*and on manual dispatch, packages release archives, and publishes a GitHub Release with generated notes- Push and pull-request workflows honor
ci-skipin the commit message to skip the build matrix when explicitly requested - Default GitHub-hosted workflows do not run the smoke or benchmark suites; use the local commands above when you need deeper validation
windows-x86/ 32-bit builds are still supported through the repository setup scripts, but they must be compiled manually outside GitHub Actions
To cut a release from Git:
git tag <tag>
git push origin <tag>When the release workflow succeeds, GitHub Releases will contain:
qbnex_<tag>_lnx.tar.gzqbnex_<tag>_osx.tar.gzqbnex_<tag>_win-x64.zip
If you need qbnex_<tag>_win-x86.zip or another 32-bit Windows artifact, build it locally with setup_win.cmd.
Successful Compilation:
QQQQ BBBB N N EEEEE X X
Q Q B B NN N E X X
Q QQ BBBB N N N EEEE X
Q Q B B N NN E X X
QQQQ BBBB N N EEEEE X X
QBNex Compiler
Preparing build files... [########################################] 100%
Compilation Error:
[x] QBNex :: Error [E1203] Expression mixes incompatible types
[@] source/qbnex.bas(1708,28)
[#] source
1708 | IF UserDefine(0, i) = l$ THEN
| ^ string value compared against incompatible type
[>] next Convert the value to the correct type using an explicit conversion function.
[::] flow Parsing :: parse source file -> file: source/qbnex.bas
Detailed Diagnostics (default):
[x] QBNex :: Error [E1106] IF statement is missing THEN or GOTO
[@] app.bas(42,13)
[#] source
42 | IF score > 10 PRINT "win"
| ^ add THEN or GOTO here
[>] next Add THEN after the IF condition.
[::] flow Parsing :: parse source file -> file: app.bas
[!] cause The compiler parsed an IF condition but never found THEN or GOTO to finish the statement.
[+] example IF score > 10 THEN PRINT "win"
[x] QBNex :: Build Halted 1 blocking diagnostic(s)
QBNex diagnostics use compact markers so the important parts of an error can be scanned quickly in a plain terminal:
| Marker | Meaning |
|---|---|
[x] |
Error diagnostic |
[!!] |
Fatal diagnostic |
[~] |
Warning diagnostic |
[i] |
Informational diagnostic |
[@] |
File location (file(line,column)) |
[#] |
Source snippet |
[>] |
Next action / fix hint |
[::] |
Phase and flow summary |
[*] |
Active compiler configuration note |
[=] |
Repeated diagnostic or hidden duplicate summary |
[^] |
Verbose location context |
[.] |
Verbose detail note |
[>>] |
Verbose in-progress context |
[!] |
Verbose cause note |
[+] |
Verbose example or corrected form |
Detailed diagnostics are enabled by default so the full trail, root-cause notes, and remediation examples appear automatically. Use -k or --compact-errors when you prefer compact output.
Runtime Success:
RUNTIME_SMOKE_OK
Runtime Failure:
RUNTIME_SMOKE_FAIL 3
(Exit code: 1)
Understanding how QBNex translates BASIC code to native binaries.
QBNex is a self-hosting compiler written in QBNex BASIC itself (~26,000 lines). The compilation process involves multiple stages:
┌─────────────────┐
│ BASIC Source │ (yourfile.bas)
│ (.bas file) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Pre-pass │ Handle $INCLUDE, $DEFINE, $IFDEF
│ (Parsing) │ Validate syntax, process imports
└────────┬────────┘
│
▼
┌─────────────────┐
│ Code Generation│ Translate BASIC to C++ code
│ (Transpiler) │ Generate optimized C++ in internal/temp/
└────────┬────────┘
│
▼
┌─────────────────┐
│ C++ Compiler │ Invoke g++ (Windows/Linux) or clang++ (macOS)
│ (Native) │ Link against OpenGL, audio, etc.
└────────┬────────┘
│
▼
┌─────────────────┐
│ Native Binary │ yourfile.exe (Windows)
│ (Executable) │ ./yourfile (Linux/macOS)
└─────────────────┘
During the pre-pass stage, the compiler:
- Reads source file: Loads your
.basfile into memory - Processes directives:
$INCLUDE: Includes external files$DEFINE/$IFDEF/$ENDIF: Conditional compilation$IMPORT:: Loads standard library modules
- Validates syntax: Checks for syntax errors
- Builds symbol table: Records variables, functions, types
- Handles metacommands: Processes
OPTION _EXPLICIT, etc.
Example:
' This is processed during pre-pass
'$IMPORT:'qbnex'
'$INCLUDE:'mylib.bas'
$DEFINE DEBUG_MODE
$IFDEF DEBUG_MODE
PRINT "Debug mode enabled"
$ENDIFThe code generation stage:
- Translates BASIC to C++: Each BASIC statement becomes C++ code
- Optimizes output: Removes redundant code, inlines functions
- Generates runtime calls: Links to LibQB runtime library
- Handles types: Converts BASIC types to C++ equivalents
- Creates temp files: Outputs to
internal/temp/
BASIC to C++ Translation:
' BASIC Input
PRINT "Hello, World!"
FOR i = 1 TO 10
PRINT i
NEXT iBecomes (simplified):
// Generated C++ Output
print_string("Hello, World!\n");
for (int i = 1; i <= 10; i++) {
print_integer(i);
print_string("\n");
}The final stage:
- Invokes C++ compiler:
- Windows:
g++from MinGW - Linux:
g++from GCC - macOS:
clang++from Xcode
- Windows:
- Links libraries:
- OpenGL, FreeGLUT, GLEW (graphics)
- miniaudio (sound)
- FreeType (fonts)
- Platform-specific libraries
- Produces native binary: Platform-specific executable
- Cleans up: Removes temporary files (unless
-zflag used)
Compilation Command (Windows):
g++ -mconsole -s -Wfatal-errors -w -Wall \
qbx.cpp \
libqb\os\win\libqb_setup.o \
parts\video\font\ttf\os\win\src.o \
parts\core\os\win\src.a \
-lopengl32 -lglu32 -static-libgcc -static-libstdc++ \
-D GLEW_STATIC -D FREEGLUT_STATIC \
-lws2_32 -lwinmm -lgdi32 \
-o "output.exe"When you compile with -z flag (generate C code only), you can inspect the output:
qb myprogram.bas -zGenerated files appear in internal/temp/:
qbx.cpp- Main C++ source file*.h- Header files- Resource files (icons, etc.)
View generated C++ code:
# Windows
type internal\temp\qbx.cpp | more
# Linux/macOS
less internal/temp/qbx.cppFlag -z (Generate C code only):
qb myprogram.bas -z
# Creates internal/temp/qbx.cpp
# Does NOT compile to executable
# Useful for: debugging, inspection, custom buildsFlag -x (Compile and run):
qb myprogram.bas -x
# Compiles to myprogram.exe
# Immediately executes myprogram.exe
# Shows program output in consoleFlag -o (Custom output name):
qb myprogram.bas -o myapp.exe
# Creates myapp.exe instead of myprogram.exe
# Useful for: deployment, versioningFlag -w (Show warnings):
qb myprogram.bas -w
# Shows potential issues that aren't errors
# Recommended for developmentFlag -e (OPTION _EXPLICIT):
qb myprogram.bas -e
# Requires all variables to be declared with DIM
# Catches typos in variable names
# Best practice for production codeCompilation Speed:
- Simple programs: 1-3 seconds
- Medium programs: 3-10 seconds
- Large programs (1000+ lines): 10-30 seconds
- Compiler itself (~26K lines): 1-2 minutes
Binary Size:
- Hello World: ~1-2 MB (includes runtime)
- Graphics program: ~3-5 MB (includes OpenGL libs)
- Full stdlib program: ~2-4 MB
Optimization Tips:
- Use
-qflag for cleaner output during development - Use
-wflag to catch potential issues early - Use
-eflag to enforce variable declaration - Use
-zflag for debugging compilation issues
Common issues and their solutions.
Problem: "Cannot convert number to string"
Cannot convert number to string
Caused by: IF USERDEFINE ( 0 , I ) = L$ THEN
LINE 1708
Solution:
- Check type mismatches in comparisons
- Ensure proper type conversion with
STR$(),VAL() - Verify variable types match in operations
- Known issue in main compiler self-hosting (line 1708)
Problem: "File not found"
Error: File not found: myprogram.bas
Solution:
# Check file exists
ls -la myprogram.bas # Linux/macOS
dir myprogram.bas # Windows
# Use absolute path
qb /path/to/myprogram.bas
# Check current directory
pwd # Linux/macOS
cd # WindowsProblem: "Permission denied"
bash: ./myprogram: Permission denied
Solution:
# Make executable
chmod +x myprogram
# Or run with explicit interpreter
./myprogramProblem: Program compiles but doesn't run
Solution:
# Check if executable exists
ls -la myprogram
# Check dependencies (Linux)
ldd myprogram
# Run with strace (Linux debugging)
strace ./myprogram
# Check for missing libraries
./myprogram
# Error message will show missing libraryProblem: Graphics not displaying
Solution:
' Ensure proper SCREEN mode
SCREEN 12 ' 640x480 graphics mode
' Check if graphics initialized
CLS ' Clear screen
' Add delay to see output
SLEEP 2Docker graphics:
# Enable X11 forwarding
xhost +local:docker
docker run --rm -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix qbnex qb graphics.bas -xProblem: Sound not working
Solution:
- Linux: Check ALSA drivers installed (
libasound2-dev) - macOS: CoreAudio should work automatically
- Windows: Check Windows Multimedia library
- Use
BEEPfor simple test
BEEP ' Should produce system beep
SOUND 440, 18 ' 440Hz for 1 secondPolyphonic synth example:
_VOICE 1
_ADSR .01, .05, .7, .08
_WAVE "SINE"
SOUND 440, 6
_VOICE 2
_WAVE "CUSTOM:0,1,0,-1"
SOUND 660, 6
_VOICE 0 ' return to automatic 4-voice round-robin
_WAVE "PINK", 3
PLAY "MB T180 O3 L8 CEG"Problem: "Volume mount not working"
Solution:
# Use absolute paths
docker run --rm -v /absolute/path:/project qbnex qb file.bas
# Check Docker Compose config
docker-compose config
# Verify files mounted
docker run --rm -v $(pwd):/project qbnex ls -la /projectProblem: "Graphics not working in Docker"
Solution:
# Linux: Allow Docker X11 access
xhost +local:docker
# macOS: Install XQuartz
brew install --cask xquartz
# Enable "Allow connections from network clients" in XQuartz preferences
# Windows: Install VcXsrv
# Run with "Disable access control" checkedProblem: "Build fails in Docker"
Solution:
# Rebuild without cache
docker-compose build --no-cache
# Check Dockerfile syntax
docker build --no-cache -t qbnex .
# Verify all files present
ls -la
# Should include: Dockerfile, docker-compose.yml, source/Problem: "Import not working"
Solution:
' Correct syntax (note the quotes and colon)
'$IMPORT:'qbnex'
'$IMPORT:'collections.list'
'$IMPORT:'sys.env'
' Wrong syntax (common mistakes)
'$IMPORT: qbnex' ' Missing quotes around module
'$IMPORT:qbnex' ' Missing inner quotes
IMPORT:'qbnex' ' Missing $ prefixProblem: "Module not found"
Solution:
- Modules are in
source/stdlib/relative to compiler root - Ensure compiler installation is complete
- Check file exists:
source/stdlib/collections/list.bas
# Verify stdlib files exist
ls -la source/stdlib/collections/
ls -la source/stdlib/strings/
ls -la source/stdlib/sys/Problem: "QBNex_List not defined"
Solution:
' Must import qbnex core first
'$IMPORT:'qbnex'
' Then you can use QBNex types
DIM myList AS QBNex_List
List_Init myListWindows:
Problem: "g++ not found"
- Run
setup_win.cmdto download MinGW - Check antivirus isn't blocking download
- Whitelist QBNex folder in antivirus
Problem: "Antivirus flags qb.exe"
- Add QBNex folder to exclusion list
- This is a false positive (compiled binary heuristic)
- QBNex is open source (MIT License)
Linux:
Problem: "Missing libraries"
# Install dependencies (Debian/Ubuntu)
sudo apt-get install build-essential libglu1-mesa-dev libasound2-dev freeglut3-dev libx11-dev
# Install dependencies (Fedora)
sudo dnf install gcc-c++ libglu-devel libasound-devel freeglut-devel libX11-devel
# Install dependencies (Arch)
sudo pacman -S gcc glu alsa-lib freeglut libx11macOS:
Problem: "xcode-select not found"
# Install Xcode Command Line Tools
xcode-select --install
# Verify installation
gcc --version
clang --versionCheck compiler version:
qb --version
# Should show: QBNex Compiler <build info>Show help:
qb --help
# Shows all available flags and usageShow examples:
qb --examples
# Shows common usage patternsCheck settings:
qb -s
# Shows current compiler settingsVerbose compilation:
# Compile with warnings
qb myprogram.bas -w
# Compile in quiet mode (less output)
qb myprogram.bas -qGenerate C code for inspection:
qb myprogram.bas -z
# Check internal/temp/qbx.cpp for generated code-
Main compiler self-hosting (line 1708)
- Type conversion issue in
UserDefine()function - Pre-compiled
qb.exeworks fine - Affects recompiling compiler from source
- Workaround: Use existing
qb.exe
- Type conversion issue in
-
Executable timeout in automated tests
- Console I/O in certain environments
- Manual execution works correctly
- Use
-xflag for immediate testing
When reporting bugs, include:
- Platform: Windows/Linux/macOS version
- Compiler version:
qb --version - Source code: Minimal reproducing example
- Expected behavior: What you expected
- Actual behavior: What happened
- Error messages: Complete error output
- Steps to reproduce: How to trigger the bug
Create minimal bug report:
' bug_demo.bas
' Expected: Should print "Hello"
' Actual: Prints nothing or error
PRINT "Hello" ' This line causes issueqb bug_demo.bas -w
# Copy complete output' hello.bas
PRINT "Hello, World!"
PRINT "Welcome to QBNex!"qb hello.bas' calc.bas
DIM a AS INTEGER
DIM b AS INTEGER
DIM result AS SINGLE
a = 10
b = 20
PRINT "Addition "; a; " + "; b; " = "; a + b
PRINT "Subtraction "; a; " - "; b; " = "; a - b
PRINT "Multiplication "; a; " * "; b; " = "; a * b
PRINT "Division "; a; " / "; b; " = "; a / b
PRINT "Modulo "; a; " MOD 3 = "; a MOD 3
PRINT "Power "; a; " ^ 2 = "; a ^ 2
PRINT "Square root SQR("; a; ") = "; SQR(a)QBNex supports a comprehensive range of data types:
' types_demo.bas
DIM bitVar AS BIT
DIM byteVar AS BYTE
DIM intVar AS INTEGER
DIM longVar AS LONG
DIM int64Var AS _INTEGER64
DIM singleVar AS SINGLE
DIM doubleVar AS DOUBLE
DIM floatVar AS _FLOAT
DIM stringVar AS STRING * 20
DIM offsetVar AS OFFSET
' Unsigned types
DIM uByte AS UNSIGNED BYTE
DIM uInt AS UNSIGNED INTEGER
DIM uLong AS UNSIGNED LONG
DIM uInt64 AS UNSIGNED _INTEGER64
' Type conversion
DIM num AS INTEGER
num = CINT("123")
PRINT "Converted: "; num' loop.bas
PRINT "Even and Odd Numbers (1-20)"
PRINT
FOR i = 1 TO 20
IF i MOD 2 = 0 THEN
PRINT i; " is even"
ELSE
PRINT i; " is odd"
END IF
NEXT i
PRINT
PRINT "Countdown"
count = 10
DO WHILE count > 0
PRINT count
count = count - 1
LOOP
PRINT "Blast off!"' functions.bas
DECLARE SUB Greet (name$)
DECLARE FUNCTION Square# (x AS SINGLE)
DECLARE FUNCTION Factorial& (n AS INTEGER)
CALL Greet("Alice")
CALL Greet("Bob")
PRINT "Square of 5 "; Square(5)
PRINT "Square of 12.5 "; Square(12.5)
PRINT "Factorial of 5 "; Factorial(5)
PRINT "Factorial of 10 "; Factorial(10)
END
SUB Greet (name$)
PRINT "Hello, "; name$; "!"
PRINT "Welcome to QBNex!"
PRINT
END SUB
FUNCTION Square# (x AS SINGLE)
Square = x * x
END FUNCTION
FUNCTION Factorial& (n AS INTEGER)
IF n <= 1 THEN
Factorial = 1
ELSE
Factorial = n * Factorial(n - 1)
END IF
END FUNCTION' arrays.bas
OPTION BASE 1
DIM numbers(10) AS INTEGER
DIM total AS INTEGER
DIM average AS SINGLE
' Fill array
FOR i = 1 TO 10
numbers(i) = i * 10
NEXT i
' Calculate sum
total = 0
FOR i = 1 TO 10
total = total + numbers(i)
NEXT i
average = total / 10
PRINT "Numbers ";
FOR i = 1 TO 10
PRINT numbers(i);
IF i < 10 THEN PRINT ", ";
NEXT i
PRINT
PRINT "Total "; total
PRINT "Average "; average
' Dynamic arrays
REDIM dynamic(5) AS INTEGER
dynamic(1) = 100
REDIM PRESERVE dynamic(10) AS INTEGER
PRINT "Dynamic array element "; dynamic(1)' fileio.bas
DIM line$ AS STRING
DIM count AS INTEGER
' Write to file
OPEN "data.txt" FOR OUTPUT AS #1
PRINT #1, "Line 1 Hello"
PRINT #1, "Line 2 World"
PRINT #1, "Line 3 QBNex"
CLOSE #1
PRINT "File written successfully!"
PRINT
' Read from file
OPEN "data.txt" FOR INPUT AS #1
count = 0
WHILE NOT EOF(1)
LINE INPUT #1, line$
count = count + 1
PRINT "Read line "; count; " "; line$
WEND
CLOSE #1
' Append to file
OPEN "data.txt" FOR APPEND AS #1
PRINT #1, "Line 4 Appended"
CLOSE #1
PRINT
PRINT "File operations completed!"
' Clean up
KILL "data.txt"' types.bas
TYPE Player
Name AS STRING * 20
Score AS LONG
Health AS SINGLE
Level AS INTEGER
END TYPE
DIM player1 AS Player
DIM player2 AS Player
player1.Name = "Alice"
player1.Score = 1500
player1.Health = 100.0
player1.Level = 5
player2.Name = "Bob"
player2.Score = 2300
player2.Health = 85.5
player2.Level = 7
PRINT "Player 1"
PRINT " Name "; player1.Name
PRINT " Score "; player1.Score
PRINT " Health "; player1.Health
PRINT " Level "; player1.Level
PRINT
PRINT "Player 2"
PRINT " Name "; player2.Name
PRINT " Score "; player2.Score
PRINT " Health "; player2.Health
PRINT " Level "; player2.Level' graphics.bas
SCREEN 12 ' 640x480, 16 colors
COLOR 15, 1 ' White on blue
CLS
PRINT "QBNex Graphics Demo"
PRINT "Press any key to continue..."
SLEEP
' Draw shapes
LINE (50, 50)-(300, 200), 14, B ' Yellow box
CIRCLE (400, 125), 75, 12 ' Red circle
LINE (100, 300)-(500, 400), 10, BF ' Filled green rectangle
' Draw pattern
FOR i = 0 TO 639 STEP 20
LINE (i, 0)-(639 - i, 479), 9
NEXT i
LOCATE 25, 1
PRINT "Graphics demo complete. Press any key..."
SLEEP
SCREEN 0 ' Return to text modeQBNex supports 150+ QBasic/QB64 keywords and functions. Below is a comprehensive reference organized by category.
| Command | Description | Example |
|---|---|---|
IF...THEN...ELSE |
Conditional execution | IF x > 0 THEN PRINT "Positive" |
ELSEIF |
Additional condition | ELSEIF x < 0 THEN PRINT "Negative" |
END IF |
End conditional block | END IF |
SELECT CASE |
Multi-way branch | SELECT CASE x |
CASE |
Case branch | CASE 1, 2, 3 |
CASE IS |
Conditional case | CASE IS > 10 |
CASE TO |
Range case | CASE 1 TO 10 |
END SELECT |
End select block | END SELECT |
FOR...TO...STEP |
Counted loop | FOR i = 1 TO 10 STEP 2 |
NEXT |
End for loop | NEXT i |
WHILE...WEND |
While loop (legacy) | WHILE x < 10 |
DO...LOOP |
Do loop | DO WHILE x < 10 |
DO WHILE |
Do while condition | DO WHILE x < 10 |
DO UNTIL |
Do until condition | DO UNTIL x >= 10 |
LOOP WHILE |
Loop while condition | LOOP WHILE x < 10 |
LOOP UNTIL |
Loop until condition | LOOP UNTIL x >= 10 |
EXIT FOR |
Exit for loop | EXIT FOR |
EXIT DO |
Exit do loop | EXIT DO |
GOTO |
Jump to label | GOTO 100 |
GOSUB |
Call subroutine | GOSUB 1000 |
RETURN |
Return from gosub | RETURN |
ON...GOTO |
Computed goto | ON x GOTO 100, 200, 300 |
ON...GOSUB |
Computed gosub | ON x GOSUB 1000, 2000 |
END |
End program | END |
STOP |
Stop execution | STOP |
SYSTEM |
Exit to OS | SYSTEM |
| Command | Description | Example |
|---|---|---|
DIM |
Declare variable/array | DIM x AS INTEGER |
REDIM |
Redimension array | REDIM arr(20) |
REDIM PRESERVE |
Redimension keeping data | REDIM PRESERVE arr(30) |
CONST |
Declare constant | CONST MAX = 100 |
LET |
Assign value (optional) | LET x = 10 |
COMMON SHARED |
Share across modules | COMMON SHARED x AS INTEGER |
SHARED |
Share in sub/function | SHARED x |
STATIC |
Static variable | STATIC count AS INTEGER |
TYPE...END TYPE |
User-defined type | TYPE Player |
DEFINT |
Default integer | DEFINT A-Z |
DEFSTR |
Default string | DEFSTR S |
DEFSNG |
Default single | DEFSNG A |
DEFDBL |
Default double | DEFDBL D |
DEFLNG |
Default long | DEFLNG L |
OPTION BASE |
Array base index | OPTION BASE 1 |
ERASE |
Erase array | ERASE arr |
SWAP |
Swap two variables | SWAP a, b |
CLEAR |
Clear variables | CLEAR |
| Command | Description | Example |
|---|---|---|
PRINT |
Print to screen | PRINT "Hello" |
PRINT USING |
Formatted print | PRINT USING "##.##"; x |
INPUT |
Get user input | INPUT "Name ", name$ |
LINE INPUT |
Get line of input | LINE INPUT "Text ", text$ |
WRITE |
Write comma-separated | WRITE #1, a, b, c |
CLS |
Clear screen | CLS |
LOCATE |
Position cursor | LOCATE 10, 20 |
TAB |
Tab to column | PRINT TAB(10); "Text" |
SPC |
Print spaces | PRINT SPC(5); "Text" |
BEEP |
Sound beep | BEEP |
SLEEP |
Pause execution | SLEEP 2 |
INKEY$ |
Get key press | k$ = INKEY$ |
| Function | Description | Example |
|---|---|---|
LEFT$ |
Left substring | LEFT$("Hello", 2) → "He" |
RIGHT$ |
Right substring | RIGHT$("Hello", 2) → "lo" |
MID$ |
Middle substring | MID$("Hello", 2, 3) → "ell" |
LEN |
String length | LEN("Hello") → 5 |
INSTR |
Find substring | INSTR("Hello", "ll") → 3 |
LCASE$ |
Lowercase | LCASE$("HELLO") → "hello" |
UCASE$ |
Uppercase | UCASE$("hello") → "HELLO" |
LTRIM$ |
Trim left spaces | LTRIM$(" Hi") → "Hi" |
RTRIM$ |
Trim right spaces | RTRIM$("Hi ") → "Hi" |
TRIM$ |
Trim both sides | TRIM$(" Hi ") → "Hi" |
STR$ |
Number to string | STR$(123) → " 123" |
VAL |
String to number | VAL("123") → 123 |
CHR$ |
ASCII to char | CHR$(65) → "A" |
ASC |
Char to ASCII | ASC("A") → 65 |
SPACE$ |
Create spaces | SPACE$(5) → " " |
STRING$ |
Repeat character | STRING$(3, "*") → "***" |
HEX$ |
Number to hex | HEX$(255) → "FF" |
OCT$ |
Number to octal | OCT$(8) → "10" |
| Function | Description | Example |
|---|---|---|
ABS |
Absolute value | ABS(-5) → 5 |
SGN |
Sign (-1, 0, 1) | SGN(-5) → -1 |
SIN |
Sine | SIN(1.57) → 1.0 |
COS |
Cosine | COS(0) → 1.0 |
TAN |
Tangent | TAN(0.785) → 1.0 |
ATN |
Arctangent | ATN(1) → 0.785 |
EXP |
Exponential | EXP(1) → 2.718 |
LOG |
Natural logarithm | LOG(2.718) → 1.0 |
SQR |
Square root | SQR(16) → 4 |
INT |
Integer part (floor) | INT(3.7) → 3 |
FIX |
Truncate decimal | FIX(-3.7) → -3 |
RND |
Random number | RND → 0.0-1.0 |
RANDOMIZE |
Seed random | RANDOMIZE TIMER |
MOD |
Modulo | 10 MOD 3 → 1 |
^ |
Power | 2 ^ 3 → 8 |
\ |
Integer division | 10 \ 3 → 3 |
| Function | Description | Example |
|---|---|---|
CINT |
Convert to integer | CINT(3.7) → 4 |
CLNG |
Convert to long | CLNG(3.7) → 4 |
CSNG |
Convert to single | CSNG(3) → 3.0 |
CDBL |
Convert to double | CDBL(3) → 3.0 |
CSTR |
Convert to string | CSTR(123) → "123" |
MKI$ |
Integer to string | MKI$(100) |
MKL$ |
Long to string | MKL$(100000) |
MKS$ |
Single to string | MKS$(value) |
MKD$ |
Double to string | MKD$(value) |
CVI |
String to integer | CVI(s$) |
CVL |
String to long | CVL(s$) |
CVS |
String to single | CVS(s$) |
CVD |
String to double | CVD(s$) |
| Command | Description | Example |
|---|---|---|
DIM arr(n) |
Declare array | DIM arr(10) AS INTEGER |
REDIM |
Resize array | REDIM arr(20) |
REDIM PRESERVE |
Resize keeping data | REDIM PRESERVE arr(30) |
LBOUND |
Lower bound | LBOUND(arr) → 0 or 1 |
UBOUND |
Upper bound | UBOUND(arr) → 10 |
ERASE |
Erase array | ERASE arr |
OPTION BASE |
Set array base | OPTION BASE 1 |
| Command | Description | Example |
|---|---|---|
OPEN |
Open file | OPEN "file.txt" FOR OUTPUT AS #1 |
CLOSE |
Close file | CLOSE #1 |
PRINT # |
Write to file | PRINT #1, "Data" |
WRITE # |
Write formatted | WRITE #1, a, b, c |
INPUT # |
Read from file | INPUT #1, x |
LINE INPUT # |
Read line | LINE INPUT #1, line$ |
INPUT$ |
Read n characters | INPUT$(10, #1) |
EOF |
End of file | EOF(1) |
LOF |
Length of file | LOF(1) |
LOC |
Current position | LOC(1) |
SEEK |
Set file position | SEEK #1, 100 |
FREEFILE |
Get free file number | f = FREEFILE |
GET |
Read record | GET #1, , record |
PUT |
Write record | PUT #1, , record |
FIELD |
Define record fields | FIELD #1, 10 AS name$ |
LSET |
Left-justify in field | LSET name$ = "John" |
RSET |
Right-justify in field | RSET name$ = "John" |
KILL |
Delete file | KILL "file.txt" |
NAME...AS |
Rename file | NAME "old.txt" AS "new.txt" |
FILES |
List files | FILES "*.bas" |
CHDIR |
Change directory | CHDIR "C\DATA" |
MKDIR |
Make directory | MKDIR "NEWDIR" |
RMDIR |
Remove directory | RMDIR "OLDDIR" |
| Command | Description | Example |
|---|---|---|
SCREEN |
Set screen mode | SCREEN 12 |
COLOR |
Set colors | COLOR 15, 1 |
CLS |
Clear screen | CLS |
LOCATE |
Position cursor | LOCATE 10, 20 |
PSET |
Set pixel | PSET (100, 100), 15 |
PRESET |
Reset pixel | PRESET (100, 100) |
LINE |
Draw line | LINE (0, 0)-(100, 100), 15 |
CIRCLE |
Draw circle | CIRCLE (160, 100), 50, 14 |
PAINT |
Fill area | PAINT (160, 100), 9, 14 |
DRAW |
Draw with macro | DRAW "U50 R50 D50 L50" |
VIEW |
Set viewport | VIEW (0, 0)-(320, 200) |
WINDOW |
Set coordinates | WINDOW (-10, -10)-(10, 10) |
PMAP |
Map coordinates | PMAP(x, 0) |
POINT |
Get pixel color | POINT(100, 100) |
PALETTE |
Set palette | PALETTE 1, 63 |
WIDTH |
Set screen width | WIDTH 80, 25 |
GET (graphics) |
Capture image | GET (0, 0)-(10, 10), arr |
PUT (graphics) |
Display image | PUT (50, 50), arr, PSET |
SOUND |
Play sound | SOUND 440, 18 |
PLAY |
Play music | PLAY "MFT180 O3 C E G" |
BEEP |
System beep | BEEP |
| Command/Function | Description | Example |
|---|---|---|
TIMER |
Seconds since midnight | t = TIMER |
DATE$ |
Current date | d$ = DATE$ |
TIME$ |
Current time | t$ = TIME$ |
COMMAND$ |
Command-line args | cmd$ = COMMAND$ |
ENVIRON$ |
Environment variable | ENVIRON$("PATH") |
FRE |
Free memory | FRE("") |
CSRLIN |
Current row | r = CSRLIN |
POS |
Current column | c = POS(0) |
PEEK |
Read memory byte | PEEK(&H417) |
POKE |
Write memory byte | POKE &H417, 0 |
DEF SEG |
Set memory segment | DEF SEG = &HA000 |
VARPTR |
Variable pointer | VARPTR(x) |
VARSEG |
Variable segment | VARSEG(x) |
SADD |
String address | SADD(s$) |
VARPTR$ |
Pointer as string | VARPTR$(x) |
BLOAD |
Load binary file | BLOAD "file.bin", 0 |
BSAVE |
Save binary file | BSAVE "file.bin", 0, 1000 |
SHELL |
Execute command | SHELL "DIR" |
CHAIN |
Chain to program | CHAIN "prog2.bas" |
CALL |
Call subroutine | CALL MySub(x, y) |
CALL ABSOLUTE |
Call machine code | CALL ABSOLUTE(addr) |
| Command | Description | Example |
|---|---|---|
ON ERROR GOTO |
Set error handler | ON ERROR GOTO ErrorHandler |
ON ERROR RESUME NEXT |
Ignore errors | ON ERROR RESUME NEXT |
RESUME |
Resume after error | RESUME |
RESUME NEXT |
Resume next line | RESUME NEXT |
RESUME <label> |
Resume at label | RESUME Continue |
ERR |
Error number | IF ERR = 53 THEN... |
ERL |
Error line number | PRINT "Error at line"; ERL |
ERDEV |
Device error code | ERDEV |
ERDEV$ |
Device error string | ERDEV$ |
| Command | Description | Example |
|---|---|---|
SUB...END SUB |
Define subroutine | SUB MySub(x) |
FUNCTION...END FUNCTION |
Define function | FUNCTION Add(a, b) |
DECLARE SUB |
Declare subroutine | DECLARE SUB MySub(x AS INTEGER) |
DECLARE FUNCTION |
Declare function | DECLARE FUNCTION Add#(a, b) |
DEF FN |
Define inline function | DEF FNSquare(x) = x * x |
STATIC |
Static sub/function | SUB MySub STATIC |
SHARED |
Share variables | SHARED x, y |
COMMON SHARED |
Share across modules | COMMON SHARED x AS INTEGER |
DATA |
Define data | DATA 10, 20, 30 |
READ |
Read data | READ x, y, z |
RESTORE |
Reset data pointer | RESTORE MyData |
KEY |
Define function key | KEY 1, "LIST" + CHR$(13) |
KEY ON/OFF |
Enable/disable keys | KEY ON |
KEY LIST |
List key definitions | KEY LIST |
ON TIMER |
Timer event | ON TIMER(1) GOSUB TimerEvent |
TIMER ON/OFF |
Enable/disable timer | TIMER ON |
ON COM |
Serial port event | ON COM(1) GOSUB ComEvent |
ON PEN |
Light pen event | ON PEN GOSUB PenEvent |
ON STRIG |
Joystick event | ON STRIG(1) GOSUB JoyEvent |
ON PLAY |
Music event | ON PLAY(1) GOSUB MusicEvent |
TRON |
Trace on | TRON |
TROFF |
Trace off | TROFF |
LPRINT |
Print to printer | LPRINT "Text" |
LPOS |
Printer position | LPOS(1) |
OUT |
Output to port | OUT &H3F8, 65 |
INP |
Input from port | INP(&H3F8) |
WAIT |
Wait for port | WAIT &H3DA, 8 |
STICK |
Joystick position | STICK(0) |
VIEW PRINT |
Set text viewport | VIEW PRINT 1 TO 20 |
| Operator | Description | Example |
|---|---|---|
AND |
Logical AND | IF x > 0 AND y > 0 THEN |
OR |
Logical OR | IF x = 1 OR y = 1 THEN |
NOT |
Logical NOT | IF NOT flag THEN |
XOR |
Exclusive OR | result = a XOR b |
EQV |
Equivalence | result = a EQV b |
IMP |
Implication | result = a IMP b |
| Operator | Description | Example |
|---|---|---|
= |
Equal | IF x = 10 THEN |
<> |
Not equal | IF x <> 10 THEN |
< |
Less than | IF x < 10 THEN |
> |
Greater than | IF x > 10 THEN |
<= |
Less or equal | IF x <= 10 THEN |
>= |
Greater or equal | IF x >= 10 THEN |
Primary compiler modules now live under source/utilities/ with source/qbnex.bas
acting mainly as the main orchestrator. Shared startup/build/temp-workspace state is
grouped in source/utilities/state.bas.
For rebuild steps, minimum verification, and module-splitting guidance, see
CONTRIBUTING.md.
QBNex is a self-hosting compiler written in QBNex BASIC itself (~26,000 lines). The compilation process:
- Bootstrap Phase: A minimal C++ compiler (
internal/c/qbx.cpp) compiles the bootstrap data ininternal/source/into a stage0 compiler - Self-Hosting Phase: The build generates a stage0-compatible source from
source/qbnex.basand its modules, then the stage0 compiler recompiles that source into the finalqb/qb.exe - Code Generation: The QBNex compiler translates BASIC source code into optimized C++ code
- Native Compilation: Platform C++ compiler (g++/clang++) compiles the generated C++ to native binary
- Runtime Linking: Generated code links against OpenGL, miniaudio, FreeType, and platform libraries
QBNex/
├── .github/
│ ├── scripts/ # CI helper scripts (bundle/release notes)
│ └── workflows/ # pull_request.yml, push.yml, release.yml
├── assets/
│ └── icons/ # Platform icon assets (linux/macos/windows)
├── installer/
│ └── qbnex_setup.iss # Inno Setup installer script
├── internal/ # Bootstrap/runtime build workspace
│ ├── c/ # Native runtime sources and build scripts
│ │ ├── c_compiler/ # Downloaded MinGW toolchain (Windows setup)
│ │ ├── libqb/ # Runtime core (common + os/win|lnx|osx)
│ │ ├── parts/ # Runtime modules (audio/core/input/network/video/zlib)
│ │ ├── qbx.cpp # Stage0 C++ compiler entry point
│ │ ├── build_cache/ # Native build cache
│ │ └── pch/ # Precompiled header artifacts
│ ├── source/ # Bootstrap templates/data files consumed by stage0
│ ├── support/ # Terminal color + watch support data
│ ├── temp/ # Generated intermediate files during builds
│ └── config.ini # Compiler settings
├── licenses/ # Third-party license files
├── source/ # QBNex compiler source (BASIC)
│ ├── qbnex.bas # Main orchestrator entry point
│ ├── icon.rc # Windows resource script
│ ├── qbnex.ico # Compiler icon
│ ├── global/ # Version/constants/settings modules
│ ├── includes/ # Shared include files
│ ├── stdlib/ # Standard library modules + examples
│ │ ├── collections/
│ │ ├── error/
│ │ ├── io/
│ │ ├── math/
│ │ ├── oop/
│ │ ├── strings/
│ │ └── sys/
│ ├── subs_functions/ # Built-in functions/subroutines
│ └── utilities/ # Compiler utility modules
├── tests/
│ ├── *.cmd / *.sh # Smoke and benchmark test runners
│ └── fixtures/ # Test input programs
├── CMakeLists.txt # CMake build graph
├── CMakePresets.json # Cross-platform CMake presets
├── docker-compose.yml
├── Dockerfile
├── Dockerfile.dev
├── setup_lnx.sh
├── setup_osx.command
├── setup_win.cmd
├── qb.cmd # Windows wrapper command
├── README.md
├── CONTRIBUTING.md
└── CHANGELOG.md
# Generated after build/test (not source)
# - build/
# - qb, qb.exe
# - qb-stage0, qb-stage0.exe
Windows:
git clone https://github.com/thirawat27/QBNex.git
cd QBNex
setup_win.cmdLinux:
git clone https://github.com/thirawat27/QBNex.git
cd QBNex
chmod +x setup_lnx.sh
./setup_lnx.shmacOS:
git clone https://github.com/thirawat27/QBNex.git
cd QBNex
chmod +x setup_osx.command
./setup_osx.commandDocker:
git clone https://github.com/thirawat27/QBNex.git
cd QBNex
docker build -t qbnex .
docker run --rm -v $(pwd):/project qbnex qb source/qbnex.bas -w- Built-in Functions/Subs: Add to
source/subs_functions/subs_functions.bas - Runtime Library: Modify C++ code in
internal/c/andinternal/c/parts/ - Graphics Extensions: Edit
source/subs_functions/extensions/opengl/ - Compiler Core: Main compiler logic in
source/qbnex.bas
QBNex uses GitHub Actions for automated builds:
- Push to
main: Linux, macOS, Windows x64 artifact builds - Pull requests to
main: Linux, macOS, Windows x64 artifact builds - Release tags (
v*): Package and publish GitHub Releases for Linux, macOS, and Windows x64
CI workflows call the platform setup_* scripts directly in CI mode. Push and pull-request workflows can be skipped with a commit message containing ci-skip, while the release workflow packages archives and publishes them through GitHub Releases. windows-x86 / 32-bit builds must be compiled manually with setup_win.cmd.
Contributions are welcome! Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.
- Report bugs and issues via GitHub Issues
- Suggest new features and enhancements
- Improve documentation and examples
- Submit pull requests with fixes or features
- Write test cases for better coverage
- Optimize performance of compiler or runtime
- Help others by answering questions in discussions
Please note that this project follows a code of conduct. By participating, you are expected to uphold this code and maintain a respectful, inclusive community.
For security vulnerabilities, please read SECURITY.md and follow the responsible disclosure process. Do not create public GitHub issues for security vulnerabilities.
This project is licensed under the MIT License - see the LICENSE file for details.
The MIT License is a permissive open source license that allows free use, modification, and distribution of the software with minimal restrictions.
- QBasic/QuickBASIC - The original BASIC implementation by Microsoft that inspired this project
- QB64 - Modern QBasic compiler that served as the foundation for QBNex
- FreeGLUT & OpenGL - Cross-platform window management and graphics rendering
- miniaudio - Single-file audio playback library enabling cross-platform sound
- FreeType - TrueType font rendering engine
- STB Image - Single-header image loading library
- GLEW - OpenGL Extension Wrangler for advanced graphics features
- Contributors - Everyone who has contributed code, documentation, or feedback to this project
- Repository: https://github.com/thirawat27/QBNex
- Issues: https://github.com/thirawat27/QBNex/issues
- Changelog: CHANGELOG.md
- Security Policy: SECURITY.md
- Contributing Guide: CONTRIBUTING.md
Created by thirawat27