Skip to content

parisxmas/RustyBASIC

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RustyBASIC

An experimental QBASIC compiler written in Rust that targets the ESP32-C3 (RISC-V) microcontroller. Write embedded programs in familiar BASIC syntax instead of C/C++.

.bas source --> [Lexer] --> [Parser] --> [Sema] --> [LLVM Codegen] --> .o (RISC-V)
                                                                        |
                                                         ESP-IDF link: .o + C runtime --> .elf

Quick Start

Prerequisites

  • Rust toolchain (stable)
  • LLVM 18 (brew install llvm@18 on macOS)
  • ESP-IDF toolchain (for firmware/flash commands)
  • zstd library (brew install zstd on macOS)

Build

export LLVM_SYS_180_PREFIX=/opt/homebrew/opt/llvm@18
export LIBRARY_PATH=/opt/homebrew/opt/zstd/lib

cargo build

Usage

# Check syntax and types
rustybasic program.bas check

# Dump LLVM IR
rustybasic program.bas dump-ir [--target host|esp32c3]

# Compile to object file
rustybasic program.bas build [-o output.o] [--target esp32c3|host]

# Build ESP-IDF firmware
rustybasic program.bas firmware [--project-dir esp-project]

# Flash to device
rustybasic program.bas flash --port /dev/ttyUSB0

Examples

Hello World

' Hello World for RustyBASIC (QBASIC dialect)
DIM msg AS STRING
msg = "Hello from RustyBASIC!"

PRINT msg
PRINT "Running on ESP32-C3"

DIM answer AS INTEGER
answer = 42
PRINT "The answer is:"; answer

END

FizzBuzz

DIM i AS INTEGER

FOR i = 1 TO 100
    SELECT CASE 0
        CASE i MOD 15
            PRINT "FizzBuzz"
        CASE i MOD 3
            PRINT "Fizz"
        CASE i MOD 5
            PRINT "Buzz"
        CASE ELSE
            PRINT i
    END SELECT
NEXT i

END

Blink LED (ESP32-C3)

CONST LED_PIN = 2
CONST OUTPUT_MODE = 1

GPIO.MODE LED_PIN, OUTPUT_MODE

DO
    GPIO.SET LED_PIN, 1
    PRINT "LED ON"
    DELAY 500

    GPIO.SET LED_PIN, 0
    PRINT "LED OFF"
    DELAY 500
LOOP

Button Input (GPIO.READ)

' Reads a push button and toggles an LED

CONST BUTTON_PIN = 9
CONST LED_PIN = 2
CONST INPUT_MODE = 0
CONST OUTPUT_MODE = 1

DIM state AS INTEGER
DIM ledOn AS INTEGER

GPIO.MODE BUTTON_PIN, INPUT_MODE
GPIO.MODE LED_PIN, OUTPUT_MODE

ledOn = 0
GPIO.SET LED_PIN, 0

PRINT "Press the button to toggle LED..."

DO
    GPIO.READ BUTTON_PIN, state

    IF state = 0 THEN
        ' Button pressed (active low)
        IF ledOn = 0 THEN
            ledOn = 1
            GPIO.SET LED_PIN, 1
            PRINT "LED ON"
        ELSE
            ledOn = 0
            GPIO.SET LED_PIN, 0
            PRINT "LED OFF"
        END IF

        ' Simple debounce
        DELAY 300
    END IF

    DELAY 50
LOOP

SUB and FUNCTION Procedures

DECLARE SUB ShowResult (label AS STRING, value AS SINGLE)
DECLARE FUNCTION Add! (a AS SINGLE, b AS SINGLE)

DIM a AS SINGLE
DIM b AS SINGLE

INPUT "First number: "; a
INPUT "Second number: "; b

CALL ShowResult("A + B", Add!(a, b))
CALL ShowResult("A * B", a * b)

END

SUB ShowResult (label AS STRING, value AS SINGLE)
    PRINT label; " = "; value
END SUB

FUNCTION Add! (a AS SINGLE, b AS SINGLE)
    Add! = a + b
END FUNCTION

User-Defined Types (Structs)

TYPE Point
    x AS SINGLE
    y AS SINGLE
END TYPE

TYPE Rect
    left AS SINGLE
    top AS SINGLE
    width AS SINGLE
    height AS SINGLE
END TYPE

DECLARE FUNCTION RectArea! (r AS Rect)
DECLARE SUB PrintPoint (p AS Point)

DIM origin AS Point
origin.x = 0.0
origin.y = 0.0

DIM cursor AS Point
cursor.x = 10.5
cursor.y = 20.3

CALL PrintPoint(origin)
CALL PrintPoint(cursor)

DIM r AS Rect
r.left = 0
r.top = 0
r.width = 100
r.height = 50

PRINT "Rectangle area:"; RectArea!(r)

END

SUB PrintPoint (p AS Point)
    PRINT "Point("; p.x; ","; p.y; ")"
END SUB

FUNCTION RectArea! (r AS Rect)
    RectArea! = r.width * r.height
END FUNCTION

Arrays

' 1D array (indices 0..5)
DIM arr(5) AS INTEGER
arr(0) = 10
arr(1) = 20
arr(2) = 30
PRINT arr(0); arr(1); arr(2)

' 2D array (indices 0..2 x 0..3)
DIM matrix(2, 3) AS SINGLE
matrix(1, 2) = 99.5
PRINT "Matrix(1,2) ="; matrix(1, 2)

I2C Communication

' Reads temperature from a BMP280 sensor over I2C

CONST I2C_BUS = 0
CONST SDA_PIN = 4
CONST SCL_PIN = 5
CONST I2C_FREQ = 100000

CONST BMP280_ADDR = 118
CONST REG_CHIP_ID = 208
CONST REG_TEMP_MSB = 250

DIM chipId AS INTEGER
DIM rawTemp AS INTEGER

PRINT "Initializing I2C bus..."
I2C.SETUP I2C_BUS, SDA_PIN, SCL_PIN, I2C_FREQ

' Write register address, then read chip ID
I2C.WRITE BMP280_ADDR, REG_CHIP_ID
I2C.READ BMP280_ADDR, 1, chipId
PRINT "Chip ID:"; chipId

IF chipId = 88 THEN
    PRINT "BMP280 detected!"

    ' Read raw temperature MSB
    I2C.WRITE BMP280_ADDR, REG_TEMP_MSB
    I2C.READ BMP280_ADDR, 1, rawTemp
    PRINT "Raw temp MSB:"; rawTemp
ELSE
    PRINT "Unknown device"
END IF

END

SPI Communication

' Read the WHO_AM_I register from an SPI sensor (e.g. BME280)

CONST SPI_BUS = 2
CONST CLK_PIN = 6
CONST MOSI_PIN = 7
CONST MISO_PIN = 2
CONST SPI_FREQ = 1000000

DIM response AS INTEGER

PRINT "Initializing SPI bus..."
SPI.SETUP SPI_BUS, CLK_PIN, MOSI_PIN, MISO_PIN, SPI_FREQ

PRINT "Reading WHO_AM_I register..."
SPI.TRANSFER 208, response
PRINT "Device ID:"; response

IF response = 96 THEN
    PRINT "BME280 detected!"
ELSEIF response = 88 THEN
    PRINT "BMP280 detected!"
ELSE
    PRINT "Unknown device"
END IF

END

WiFi Disconnect

' Demonstrates the full WiFi lifecycle

DIM ssid AS STRING
DIM pass AS STRING
DIM status AS INTEGER

ssid = "MyNetwork"
pass = "MyPassword"

PRINT "Connecting to WiFi..."
WIFI.CONNECT ssid, pass
DELAY 3000

WIFI.STATUS status
IF status = 1 THEN
    PRINT "Connected!"

    PRINT "Doing some work..."
    DELAY 2000

    PRINT "Disconnecting..."
    WIFI.DISCONNECT
    DELAY 1000

    WIFI.STATUS status
    IF status = 0 THEN
        PRINT "Disconnected successfully."
    ELSE
        PRINT "Still connected."
    END IF
ELSE
    PRINT "Failed to connect."
END IF

END

PWM LED Fade

CONST LED_PIN = 2
CONST PWM_CH = 0

PWM.SETUP PWM_CH, LED_PIN, 5000, 8

DIM duty AS INTEGER

DO
    FOR duty = 0 TO 255
        PWM.DUTY PWM_CH, duty
        DELAY 5
    NEXT duty

    FOR duty = 255 TO 0 STEP -1
        PWM.DUTY PWM_CH, duty
        DELAY 5
    NEXT duty
LOOP

HTTP GET Request

DIM response$ AS STRING

WIFI.CONNECT "MyNetwork", "MyPassword"
DELAY 3000

HTTP.GET "http://httpbin.org/get", response$
PRINT response$

END

INCLUDE Directive

Split code across multiple files with INCLUDE:

include_lib.bas

CONST PI = 3.14159
CONST GREETING = "Hello from the library!"

DECLARE SUB PrintBanner (title AS STRING)
DECLARE FUNCTION CircleArea! (radius AS SINGLE)

SUB PrintBanner (title AS STRING)
    PRINT "==========================="
    PRINT " "; title
    PRINT "==========================="
END SUB

FUNCTION CircleArea! (radius AS SINGLE)
    CircleArea! = PI * radius * radius
END FUNCTION

include_main.bas

INCLUDE "include_lib.bas"

DIM r AS SINGLE
r = 5.0

CALL PrintBanner(GREETING)
PRINT "Radius:"; r
PRINT "Area:"; CircleArea!(r)

END

Paths are resolved relative to the including file's directory. Circular includes are detected and reported as errors.

ENUM Types

ENUM Color
    Red = 1
    Green = 2
    Blue = 3
END ENUM

DIM c AS INTEGER
c = Color.Green
PRINT "Color:"; c        ' prints 2

FOR EACH (Array Iteration)

DIM scores(4) AS INTEGER
scores(0) = 90
scores(1) = 85
scores(2) = 92
scores(3) = 78
scores(4) = 95

FOR EACH s AS INTEGER IN scores
    PRINT "Score:"; s
NEXT

END

String Interpolation

DIM name AS STRING
DIM age AS INTEGER
name = "Alice"
age = 30

PRINT $"Hello, {name}! You are {age} years old."

TRY/CATCH Error Handling

PRINT "Before try"

TRY
    PRINT "Inside try block"
    PRINT "This should work fine"
CATCH err
    PRINT "Caught error: "; err
END TRY

PRINT "After try/catch"
END

LAMBDA Expressions

DIM square AS FUNCTION
square = LAMBDA(x AS INTEGER) => x * x

DIM result AS INTEGER
result = square(5)
PRINT "5 squared = "; result
END

ASSERT

DIM x AS INTEGER
x = 42

ASSERT x > 0, "x must be positive"
ASSERT x = 42
PRINT "All assertions passed!"
END

TASK (Concurrent Execution)

' FreeRTOS tasks on ESP32, pthreads on host
PRINT "Main task starting"

TASK "blinker", 2048, 1
    PRINT "Blinker task running"
    DELAY 1000
    PRINT "Blinker task done"
END TASK

PRINT "Main task continues"
END

EVENT System

DIM count AS INTEGER
count = 0

ON TIMER 1000 GOSUB tick
PRINT "Timer event registered"
DELAY 5000
PRINT "Count: "; count
END

tick:
    count = count + 1
    PRINT "Tick!"
RETURN

State Machine DSL

MACHINE TrafficLight
    STATE RED
        ON TIMER GOTO GREEN
    END STATE
    STATE GREEN
        ON TIMER GOTO YELLOW
    END STATE
    STATE YELLOW
        ON TIMER GOTO RED
    END STATE
END MACHINE

PRINT "Traffic light created"
TrafficLight.EVENT "TIMER"
PRINT "After first event"
END

MODULE Namespaces

MODULE Math
    FUNCTION Square(x AS INTEGER) AS INTEGER
        Square = x * x
    END FUNCTION

    FUNCTION Cube(x AS INTEGER) AS INTEGER
        Cube = x * x * x
    END FUNCTION
END MODULE

DIM a AS INTEGER
a = Math.Square(4)
PRINT "4 squared = "; a
a = Math.Cube(3)
PRINT "3 cubed = "; a
END

DO...LOOP Variations

DIM count AS INTEGER
DIM sum AS INTEGER

' Pre-condition: DO WHILE...LOOP
count = 1
sum = 0
DO WHILE count <= 10
    sum = sum + count
    count = count + 1
LOOP
PRINT "Sum 1..10 ="; sum

' Post-condition: DO...LOOP UNTIL
count = 10
DO
    PRINT count;
    count = count - 1
LOOP UNTIL count < 1
PRINT

' EXIT DO
count = 0
DO
    count = count + 1
    IF count = 5 THEN EXIT DO
LOOP
PRINT "Exited at count ="; count

END

NTP Clock

WIFI.CONNECT "MySSID", "MyPassword"
DELAY 3000
NTP.SYNC "pool.ntp.org"
NTP.TIME$ timeStr$
PRINT "Current time: "; timeStr$
NTP.EPOCH epoch%
PRINT "Unix epoch: "; epoch%

File System (LittleFS)

FILE.OPEN "test.txt", "w"
FILE.WRITE "Hello from RustyBASIC!"
FILE.CLOSE
FILE.EXISTS "test.txt", found%
PRINT "File exists: "; found%
FILE.OPEN "test.txt", "r"
FILE.READ$ content$
FILE.CLOSE
PRINT "Read: "; content$
FILE.DELETE "test.txt"

WebSocket Client

WIFI.CONNECT "MySSID", "MyPassword"
DELAY 3000
WS.CONNECT "ws://echo.websocket.org"
WS.SEND "Hello WebSocket!"
DELAY 1000
WS.RECEIVE$ msg$
PRINT "Received: "; msg$
WS.CLOSE

TCP Server

WIFI.CONNECT "MySSID", "MyPassword"
DELAY 3000
TCP.LISTEN 8080
PRINT "Listening on port 8080..."
TCP.ACCEPT client%
PRINT "Client connected: "; client%
TCP.RECEIVE$ request$
PRINT "Got: "; request$
TCP.SEND "HTTP/1.0 200 OK\r\n\r\nHello from RustyBASIC!\r\n"
TCP.CLOSE

Watchdog Timer

WDT.ENABLE 5000
PRINT "Watchdog enabled (5s timeout)"
FOR i = 1 TO 10
    PRINT "Working... "; i
    WDT.FEED
    DELAY 1000
NEXT i
WDT.DISABLE
PRINT "Watchdog disabled"

HTTPS Client

WIFI.CONNECT "MySSID", "MyPassword"
DELAY 3000
HTTPS.GET$ "https://httpbin.org/get", response$
PRINT "GET response: "; response$

I2S Audio Output

I2S.INIT 44100, 16, 2
PRINT "I2S initialized at 44100 Hz, 16-bit, stereo"
I2S.WRITE "audio data placeholder"
DELAY 1000
I2S.STOP
PRINT "I2S stopped"

Web Server

WIFI.CONNECT "MySSID", "MyPassword"
DELAY 3000
WEB.START 80
PRINT "Web server started on port 80"
WEB.WAIT$ path$
PRINT "Request for: "; path$
WEB.BODY$ body$
PRINT "Body: "; body$
WEB.REPLY 200, "Hello from RustyBASIC!"
WEB.STOP

SD Card

SD.INIT 5
PRINT "SD card initialized"
SD.OPEN "test.txt", "w"
SD.WRITE "Hello from RustyBASIC!"
SD.CLOSE
PRINT "File written"
SD.OPEN "test.txt", "r"
SD.READ$ content$
SD.CLOSE
PRINT "Read: "; content$
SD.FREE space%
PRINT "Free space: "; space%; " bytes"

Async / Cooperative Multitasking

PRINT "Starting async demo"
FOR i = 1 TO 5
    PRINT "Working... "; i
    YIELD
    AWAIT 500
NEXT i
PRINT "Done"

Cron Scheduling

CRON.ADD 1, "*/5"
PRINT "Cron job added (every 5 minutes)"
CRON.CHECK 1, fired%
PRINT "Should fire: "; fired%
CRON.REMOVE 1
PRINT "Cron job removed"

Regex Pattern Matching

DIM text$ AS STRING
text$ = "Hello World 123"
REGEX.MATCH "[0-9]+", text$, found%
PRINT "Has numbers: "; found%
REGEX.FIND$ "[0-9]+", text$, match$
PRINT "Found: "; match$
REGEX.REPLACE$ "[0-9]+", text$, "456", result$
PRINT "Replaced: "; result$

Bitwise Shift Operators

DIM a AS INTEGER
a = 1
PRINT "1 SHL 4 = "; a SHL 4
PRINT "16 SHR 2 = "; 16 SHR 2
DIM flags AS INTEGER
flags = 1 SHL 0 OR 1 SHL 2 OR 1 SHL 4
PRINT "Flags: "; flags
PRINT "Bit 2 set: "; (flags SHR 2) AND 1

Language Reference

Types

Type Suffix Description
INTEGER % 32-bit signed integer
LONG & 32-bit signed long
SINGLE ! 32-bit float
DOUBLE # 64-bit float (soft-float on ESP32)
STRING $ Reference-counted heap string
User type Defined with TYPE...END TYPE

Operators

Category Operators
Arithmetic + - * / \ (int div) ^ (power) MOD
Comparison = <> < > <= >=
Logical AND OR NOT XOR
Bitwise shift SHL (shift left) SHR (shift right)

Arrays

Statement Description
DIM arr(N) AS type Declare 1D array with indices 0..N
DIM mat(R, C) AS type Declare multi-dimensional array
arr(i) = expr Assign to array element
arr(i) Read array element (in expressions)

Arrays are fixed-size, heap-allocated, zero-initialized, and bounds-checked at runtime.

Control Flow

Statement Description
IF...THEN...ELSEIF...ELSE...END IF Conditional branching
SELECT CASE...CASE...CASE ELSE...END SELECT Multi-way branch
FOR...TO...STEP...NEXT Counted loop
DO WHILE/UNTIL...LOOP Pre-condition loop
DO...LOOP WHILE/UNTIL Post-condition loop
WHILE...WEND Legacy while loop
GOTO label Unconditional jump
GOSUB label / RETURN Subroutine call/return
ON expr GOTO l1, l2, ... Computed GOTO (branch by value)
ON expr GOSUB l1, l2, ... Computed GOSUB (branch by value)
ON ERROR GOTO label Set error handler
FOR EACH var IN array...NEXT Iterate over array elements
TRY...CATCH var...END TRY Structured error handling
EXIT FOR/DO/SUB/FUNCTION Early exit

Preprocessor

Directive Description
INCLUDE "file.bas" Inline another source file (resolved relative to current file)

I/O

Statement Description
PRINT expr; expr Output (; = no space, , = tab)
PRINT USING fmt$; expr Formatted output
INPUT "prompt"; var Read user input
LINE INPUT "prompt"; var$ Read entire line

String Functions

Function Returns Description
LEN(s$) Integer String length
ASC(s$) Integer ASCII code of first character
CHR$(n) String Single-character string from ASCII code
LEFT$(s$, n) String First n characters
RIGHT$(s$, n) String Last n characters
MID$(s$, start, len) String Substring (1-based start)
INSTR(s$, find$) Integer Position of find in s (1-based, 0 if not found)
STR$(n) String Number to string
VAL(s$) Single String to number
UCASE$(s$) String Uppercase
LCASE$(s$) String Lowercase
TRIM$(s$) String Strip leading/trailing whitespace
STRING$(n, code) String String of n characters with ASCII code
SPACE$(n) String String of n spaces

Math Functions

Function Returns Description
SQR(x) Single Square root
ABS(x) Single Absolute value
SIN(x) Single Sine (radians)
COS(x) Single Cosine (radians)
TAN(x) Single Tangent (radians)
ATN(x) Single Arctangent (radians)
LOG(x) Single Natural logarithm
EXP(x) Single e^x
INT(x) Integer Floor (round toward -infinity)
FIX(x) Integer Truncate (round toward zero)
SGN(x) Integer Sign: -1, 0, or 1
RND Single Random float in [0, 1)
RANDOMIZE seed Seed the random number generator

Classic BASIC

Statement Description
SWAP var1, var2 Exchange values of two variables
DEF FNname(params) = expr Define inline function
DATA v1, v2, ... Declare inline data (mixed int/float/string)
READ var1, var2, ... Read next item(s) from data pool
RESTORE Reset data read pointer to beginning

Advanced Features

Statement Description
ASSERT condition [, message$] Runtime assertion — aborts with message if condition is false
ENUM Name...END ENUM Define named integer constants (e.g. Color.Red)
$"text {expr} text" String interpolation — embeds expressions in strings
LAMBDA(params) => expr Anonymous function expression
DIM var AS FUNCTION Declare a function pointer variable
TASK name$, stack, priority...END TASK Spawn concurrent task (FreeRTOS/pthreads)
ON GPIO.CHANGE pin GOSUB label Register GPIO interrupt handler
ON TIMER ms GOSUB label Register periodic timer event
ON MQTT.MESSAGE GOSUB label Register MQTT message handler
MACHINE Name...STATE...END MACHINE Define finite state machine
MachineName.EVENT expr$ Send event to state machine
MODULE Name...END MODULE Group SUBs/FUNCTIONs into namespace (dot-notation access)

Hardware (ESP32-C3)

Statement Description
GPIO.MODE pin, mode Configure GPIO pin
GPIO.SET pin, value Write digital output
GPIO.READ pin, var Read digital input
I2C.SETUP bus, sda, scl, freq Initialize I2C
I2C.WRITE addr, data Write to I2C device
I2C.READ addr, len, var Read from I2C device
SPI.SETUP bus, clk, mosi, miso, freq Initialize SPI
SPI.TRANSFER data, var SPI send/receive
WIFI.CONNECT ssid, password Connect to WiFi
WIFI.STATUS var Check WiFi status
WIFI.DISCONNECT Disconnect WiFi
DELAY ms Pause execution (milliseconds)
ADC.READ pin, var Read analog input
PWM.SETUP ch, pin, freq, res Configure PWM channel
PWM.DUTY ch, duty Set PWM duty cycle
UART.SETUP port, baud, tx, rx Initialize UART
UART.WRITE port, data Write byte to UART
UART.READ port, var Read byte from UART
TIMER.START Start stopwatch timer
TIMER.ELAPSED var Get elapsed time (ms)
HTTP.GET url$, result$ HTTP GET request
HTTP.POST url$, body$, result$ HTTP POST request
NVS.WRITE key$, value Write integer to flash storage
NVS.READ key$, var Read integer from flash storage
MQTT.CONNECT broker$, port Connect to MQTT broker
MQTT.DISCONNECT Disconnect from MQTT broker
MQTT.PUBLISH topic$, message$ Publish message to topic
MQTT.SUBSCRIBE topic$ Subscribe to topic
MQTT.RECEIVE var$ Receive message (blocking)
BLE.INIT name$ Initialize BLE with device name
BLE.ADVERTISE mode Start (1) or stop (0) BLE advertising
BLE.SCAN var$ Scan for BLE devices
BLE.SEND data$ Send data via BLE GATT notify
BLE.RECEIVE var$ Receive BLE data (blocking)
JSON.GET json$, key$, var$ Extract value by key (dot-notation for nested)
JSON.SET json$, key$, val$, var$ Set key in JSON, returns updated JSON
JSON.COUNT json$, var Count elements in JSON array/object
LED.SETUP pin, count Initialize WS2812 NeoPixel strip
LED.SET index, r, g, b Set pixel color (0-255 per channel)
LED.SHOW Push pixel buffer to LED strip
LED.CLEAR Turn off all pixels
DEEPSLEEP ms Enter deep sleep for ms milliseconds
ESPNOW.INIT Initialize ESP-NOW peer-to-peer networking
ESPNOW.SEND peer$, data$ Send data to peer MAC address
ESPNOW.RECEIVE var$ Receive ESP-NOW message (blocking)
TOUCH.READ pin, var Read capacitive touch sensor
SERVO.ATTACH ch, pin Attach servo on PWM channel to pin
SERVO.WRITE ch, angle Set servo angle (0-180 degrees)
TONE pin, freq, duration Generate tone on pin (freq Hz, duration ms)
IRQ.ATTACH pin, mode Attach GPIO interrupt handler
IRQ.DETACH pin Detach GPIO interrupt handler
TEMP.READ var Read internal temperature sensor
OTA.UPDATE url$ Over-the-air firmware update from URL
OLED.INIT w, h Initialize SSD1306 OLED display
OLED.PRINT x, y, text$ Draw text at position
OLED.PIXEL x, y, color Set pixel on/off
OLED.LINE x1, y1, x2, y2, c Draw line
OLED.CLEAR Clear display buffer
OLED.SHOW Push buffer to OLED
LCD.INIT cols, rows Initialize HD44780 LCD
LCD.PRINT text$ Print text at current cursor position
LCD.CLEAR Clear LCD display
LCD.POS col, row Set cursor position
UDP.INIT port Initialize UDP socket on port
UDP.SEND host$, port, data$ Send UDP datagram
UDP.RECEIVE var$ Receive UDP datagram (blocking)
NTP.SYNC server$ Synchronize clock with NTP server
NTP.TIME$ var$ Get current date/time as formatted string
NTP.EPOCH var% Get current Unix epoch timestamp
FILE.OPEN path$, mode$ Open file on LittleFS ("r", "w", "a")
FILE.WRITE data$ Write string to open file
FILE.READ$ var$ Read string from open file
FILE.CLOSE Close current file
FILE.DELETE path$ Delete file from filesystem
FILE.EXISTS path$, var% Check if file exists (-1 = true, 0 = false)
WS.CONNECT url$ Connect to WebSocket server
WS.SEND data$ Send text message via WebSocket
WS.RECEIVE$ var$ Receive WebSocket message
WS.CLOSE Close WebSocket connection
TCP.LISTEN port Start TCP server on port
TCP.ACCEPT var% Accept incoming TCP connection
TCP.SEND data$ Send data on TCP connection
TCP.RECEIVE$ var$ Receive data from TCP connection
TCP.CLOSE Close TCP server and client sockets
WDT.ENABLE timeout_ms Enable watchdog timer with timeout
WDT.FEED Reset (feed) the watchdog timer
WDT.DISABLE Disable watchdog timer
HTTPS.GET$ url$, var$ HTTPS GET request (TLS)
HTTPS.POST$ url$, body$, var$ HTTPS POST request (TLS)
I2S.INIT rate, bits, channels Initialize I2S audio output
I2S.WRITE data$ Write audio data to I2S bus
I2S.STOP Stop and release I2S driver
WEB.START port Start HTTP web server on port
WEB.WAIT$ var$ Wait for HTTP request, get path
WEB.BODY$ var$ Get request body string
WEB.REPLY status, body$ Send HTTP response
WEB.STOP Stop web server
SD.INIT cs_pin Initialize SD card via SPI (CS pin)
SD.OPEN path$, mode$ Open file on SD card ("r", "w", "a")
SD.WRITE data$ Write string to open SD file
SD.READ$ var$ Read string from open SD file
SD.CLOSE Close current SD file
SD.FREE var% Get free space in bytes
YIELD Cooperative yield (FreeRTOS taskYIELD)
AWAIT ms Cooperative delay (non-blocking wait)
CRON.ADD id, expr$ Add cron job with schedule expression
CRON.CHECK id, var% Check if cron job should fire (1/0)
CRON.REMOVE id Remove cron job
REGEX.MATCH pattern$, text$, var% Test regex match (1/0)
REGEX.FIND$ pattern$, text$, var$ Find first regex match
REGEX.REPLACE$ pattern$, text$, repl$, var$ Replace regex matches

Project Structure

RustyBASIC/
├── Cargo.toml                     # Workspace root
├── crates/
│   ├── rustybasic-common/         # Span, source types
│   ├── rustybasic-lexer/          # logos-based tokenizer
│   ├── rustybasic-parser/         # Recursive descent parser + AST
│   ├── rustybasic-sema/           # Type checking, scope resolution
│   ├── rustybasic-codegen/        # LLVM IR generation (inkwell)
│   └── rustybasic-driver/         # CLI entry point
├── runtime/                       # C runtime library (ESP-IDF component)
│   ├── include/rb_runtime.h
│   └── src/                       # rb_print.c, rb_string.c, rb_array.c, rb_gpio.c, ...
├── esp-project/                   # ESP-IDF project template for linking
├── examples/                      # Example .bas programs
│   ├── hello.bas
│   ├── fizzbuzz.bas
│   ├── blink.bas
│   ├── calculator.bas
│   ├── arrays.bas
│   ├── structs.bas
│   ├── button.bas
│   ├── doloop.bas
│   ├── i2c.bas
│   ├── spi.bas
│   ├── wifi_disconnect.bas
│   ├── wifi_scan.bas
│   ├── include_main.bas
│   ├── include_lib.bas
│   ├── string_funcs.bas
│   ├── math_funcs.bas
│   ├── adc.bas
│   ├── pwm.bas
│   ├── uart.bas
│   ├── timer.bas
│   ├── http.bas
│   ├── nvs.bas
│   ├── mqtt.bas
│   ├── ble.bas
│   ├── json.bas
│   ├── neopixel.bas
│   ├── deepsleep.bas
│   ├── espnow.bas
│   ├── data_read.bas
│   ├── swap.bas
│   ├── deffn.bas
│   ├── print_using.bas
│   ├── randomize.bas
│   ├── string_funcs2.bas
│   ├── oled.bas
│   ├── lcd.bas
│   ├── servo.bas
│   ├── tone.bas
│   ├── touch.bas
│   ├── udp.bas
│   ├── temp.bas
│   ├── assert.bas
│   ├── enum.bas
│   ├── foreach.bas
│   ├── interpolation.bas
│   ├── try_catch.bas
│   ├── lambda.bas
│   ├── task.bas
│   ├── events.bas
│   ├── state_machine.bas
│   ├── module.bas
│   ├── ntp.bas
│   ├── filesystem.bas
│   ├── websocket.bas
│   ├── tcp_server.bas
│   ├── watchdog.bas
│   ├── https.bas
│   ├── i2s_audio.bas
│   ├── web_server.bas
│   ├── sd_card.bas
│   ├── async_demo.bas
│   ├── cron_demo.bas
│   ├── regex_demo.bas
│   └── bitwise.bas
└── tests/

Design Decisions

Area Choice Rationale
Lexer logos crate Zero-copy, fast, case-insensitive keywords
Parser Hand-written recursive descent BASIC's line-oriented grammar needs custom handling
Expressions Pratt parsing (precedence climbing) Clean operator precedence
Variables alloca + LLVM mem2reg Standard pattern, avoids manual phi nodes
Strings Refcounted heap (rb_string_t*) Memory-efficient for ESP32-C3's 320KB RAM
Floats f32 (not f64) No hardware FPU; f32 is 2x cheaper in soft-float
Runtime C library linked via ESP-IDF Direct access to ESP-IDF APIs
Target riscv32-unknown-none-elf ESP32-C3 = RV32IMC

License

MIT

About

ESP32 RISC-V BASIC Compiler

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors