Introduction to BuLang

BuLang is a production-ready, stack-based bytecode virtual machine designed specifically for game development. It provides high-performance script execution with support for object-oriented programming, first-class functions, typed buffers for binary data, and a unique process system for cooperative multitasking.

βœ… BuLang v1.1 - Production Ready

All 11 comprehensive test suites passing with 100% coverage. The language is stable, battle-tested, and ready for production use with proven support for 50K+ concurrent processes, typed buffers, exception handling, and more.

Key Features:

The language combines simplicity with power, allowing you to write game logic, AI behaviors, interactive systems, and process binary data efficiently - all while maintaining clean, readable code.

Getting Started

BuLang is designed to be easy to learn for developers coming from any programming background.

Hello World

Start with the simplest program:

// Hello World
print("Hello, BuLang!")

The print() function outputs text to the console. That's all you need to get started!

Variables and Basic Types

BuLang uses dynamic typing - you don't need to declare variable types:

// Variables
var name = "BuLang";
var version = 1.0;
version = "ola";
var count = 42;
var is_active = true;

print(name);
print(count);

Language Basics

Data Types

BuLang supports the following primitive types:

Type Example Description
Number 42, 3.14 Integer or floating-point numbers
String "hello" Text values
Boolean true, false Truth values
Null nil Absence of value
Array [1, 2, 3] Collections of values
Map {x: 10, y: 20} Key-value pairs
Buffer @(1024, TYPE_UINT8) Typed binary data arrays

Operators


// Arithmetic
a = 10 + 5    // 15
b = 10 - 5    // 5
c = 10 * 5    // 50
d = 10 / 5    // 2
e = 10 % 3    // 1

// Comparison
a == b        // false
a != b        // true
a > b         // true
a >= b        // true

// Logical
true && false    // false
true || false     // true
! true          // false

Bitwise Operations

a & 0b1010    // 0b1000  AND
a | 0b1010    // 0b1110 OR
a ^ 0b1010    // 0b0100  XOR

a << 2    // 40 Left Shift
a >> 2    // 2 Right Shift

(~0) == -1 Bitwise NOT

// Assignment
a = 10
a += 5    // a = a + 5
a -= 5    // a = a - 5
a *= 5    // a = a * 5
a /= 5    // a = a / 5
a %= 5    // a = a % 5

// Increment and Decrement
a++    // a = a + 1
a--    // a = a - 1

Arrays



var arr = [1, 2, 3];
var empty = [];
var mixed = [1, "hello", true, nil,v];

print(mixed);

// Acesso
print(arr[0]);     // 1
print(arr[-1]);    // 3 (Python-style!)

// Modificar
arr[1] = 10;
print(arr[1]);     // 10

// MΓ©todos
arr.push(4);
print(arr.len());  // 4 or lenght()

var x = arr.pop();
print(x);          // 4

arr.clear();
print(arr.len());  // 0

// Loop
var nums = [10, 20, 30];
for (var i = 0; i < nums.len(); i++) {
    print(nums[i]);
}

// 2D arrays
var matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
];

print(matrix[1][2]);  // 6

Maps


 //Create
var player = {
    name: "Hero",
    hp: 100,
    level: 5
};

var empty = {};

// Acess
print(player["name"]);  // "Hero"
player["hp"] = 80;
print(player["hp"]);    // 80

// Methods
print(player.len());    // 3

if (player.has("level")) {
    print("Has level");
}


var keys = player.keys();
print(keys);  // ["name", "hp", "level"]

var values = player.values();
print(values);  // ["Hero", 80, 5]

player.remove("level");
print(player.len());  // 2

player.clear();
print(player.len());  // 0

// Nested
var game = {
    player: {
        x: 100,
        y: 200
    },
    enemies: [
        {x: 50, y: 50},
        {x: 150, y: 150}
    ]
};

print(game["player"]["x"]);     // 100
print(game["enemies"][0]["y"]); // 50

// Loop sobre keys
var config = {width: 800, height: 600, fps: 60};
var keys = config.keys();

print(keys);

for (var i = 0; i < keys.len(); i++) 
{
    var key = keys[i];
    var value = config[key];
    write(" key {}   : value {}\n", key,value);
}
 

String Methods

// String operations
var text = "Hello, BuLang!";

// Length & Access
print(text.len());        // 14
print(text[0]);           // "H"
print(text[-1]);          // "!"

// Methods
text.upper();             // "HELLO, BULANG!"
text.lower();             // "hello, bulang!"
text.contains("BuLang");  // true
text.indexOf("Lang");     // 8
text.split(", ");         // ["Hello", "BuLang!"]
text.trim();              // Remove whitespace

// Concatenation
var greeting = "Hello" + " " + "World";

Structs


struct Point {
   x,y;
};

var point = Point(1,2);

print(point.x);
print(point.y);

Control Flow


// If statements
x = 10
if x > 5 {
    print("x is greater than 5")
} elif x == 5 {
    print("x is 5")
} else {
    print("x is less than 5")
}

// Loops
for i = 0; i < 5; i = i + 1 {
    print(i)
}

// While loop
count = 0
while count < 3 
{
    print(count)
    count = count + 1
}


// do while loop
count = 0
do 
{
    print(count)
    count = count + 1
} while (count < 3)


// Switch statement no fallthrough
switch x {
    case 1:
        print("x is 1")
    case 2:
        print("x is 2")
    default:
        print("x is not 1 or 2")
}

//continue and break
for i = 0; i < 5; i = i + 1 )
{
    if (i == 2 )
    {
        continue;
    }

    if (i == 3 )
    {
        break;
    }

    print(i);
}

 

Foreach Loops

BuLang provides native foreach loops for iterating over arrays and other iterable collections. This is more concise and cleaner than traditional for loops when you need to process every element.

Basic Syntax

// Iterate over array
var numbers = [10, 20, 30, 40, 50];

foreach (num in numbers) 
{
    print(num);
}
// Output: 10, 20, 30, 40, 50

With Break and Continue

You can use break and continue inside foreach loops:

// Find first element > 25
var values = [10, 20, 30, 40, 50];
var found = nil;

foreach (v in values) 
{
    if (v > 25) 
    {
        found = v;
        break;  // Exit loop early
    }
}

print(found);  // 30

Foreach with Strings

// Concatenate strings
var words = ["Hello", " ", "BuLang", "!"];
var sentence = "";

foreach (word in words) 
{
    sentence = sentence + word;
}

print(sentence);  // "Hello BuLang!"
πŸ’‘ Variable Scope

The loop variable (num, v, etc.) is scoped to the foreach block and doesn't affect variables with the same name outside the loop.

Try-Catch Exception Handling

BuLang supports structured exception handling with try-catch blocks. This allows you to gracefully handle errors and prevent crashes.

Basic Syntax

// Catch division by zero
def safe_divide(a, b) 
{
    if (b == 0) 
    {
        throw "Cannot divide by zero!";
    }
    return a / b;
}

try 
{
    var result = safe_divide(10, 2);
    print(result);  // 5
    
    result = safe_divide(10, 0);  // This throws
    print("This won't execute");
} 
catch (error) 
{
    print("Error: " + error);
}

print("Program continues!");
// Output:
// 5
// Error: Cannot divide by zero!
// Program continues!

Throwing Exceptions

Use the throw statement to raise an exception:

// Custom validation
def validate_age(age) 
{
    if (age < 0) 
    {
        throw "Age cannot be negative";
    }
    if (age > 150) 
    {
        throw "Age is unrealistic";
    }
    return true;
}

try 
{
    validate_age(-5);
} 
catch (err) 
{
    print("Validation error: " + err);
}
// Output: Validation error: Age cannot be negative

Nested Try-Catch

// Multiple error handling layers
try 
{
    print("Outer try");
    
    try 
    {
        print("Inner try");
        throw "Inner error";
    } 
    catch (inner_err) 
    {
        print("Caught inner: " + inner_err);
        throw "Outer error";  // Re-throw or new error
    }
} 
catch (outer_err) 
{
    print("Caught outer: " + outer_err);
}
βœ… Best Practice

Use try-catch for recoverable errors (invalid input, missing files). Use assertions for programming errors that should never happen in production.

Goto & Gosub

BuLang supports low-level control flow with goto and gosub statements. These are useful for implementing state machines, custom loops, and subroutines within processes.

⚠️ Use with Caution

While powerful, goto and gosub can make code harder to understand. Use them primarily in processes where they provide clear benefits.

Goto - Unconditional Jump

The goto statement jumps to a labeled position in the code. Labels are defined with label_name:

// Simple goto example
process loop_test() 
{
    var i = 0;
loop_start:
    write("loop {} \n", i);
    i += 1;
    if (i < 5) goto loop_start;
    
    print("Loop finished!");
    exit;
}

loop_test();

Gosub - Subroutine Call

The gosub statement jumps to a label and expects a return to come back to the next instruction.

// Gosub example
process teste() {
    print("A");
    gosub sub;
    print("C");
    exit;
    
sub:
    print("B");
    return;
}

teste();
// Output: A, B, C

Combining Goto and Gosub

You can combine both for complex control flow:

// Loop with subroutine
process loop_with_sub() 
{
    var i = 0;
    
mainloop:
    gosub body;
    i += 1;
    if (i < 5) goto mainloop;
    exit;
    
body:
    write("iteration {} \n", i);
    return;
}

loop_with_sub();

Nested Gosub Calls

You can nest gosub calls - each return goes back to the caller:

// Nested gosub
process nested_example() {
    print("start");
    gosub a;
    print("end");
    exit;
    
a:
    print("A");
    gosub b;
    print("A return");
    return;
    
b:
    print("B");
    return;
}

nested_example();
// Output: start, A, B, A return, end

Include External Files

You can include external BuLang files using the include directive:

// Include example
include "strings.cc";

// Now you can use functions from strings.cc
str = "hello";
upper_str = to_upper(str);
print(upper_str);

Best Practices

Common Use Cases

Use Case Recommended Statement Example
Game loops goto Main game loop, frame updates
State machines goto AI behavior states, UI states
Reusable code blocks gosub Drawing routines, cleanup code
Error handling goto Jump to error handler label

Built-in Math Opcodes

BuLang includes mathematical functions as native VM opcodes, providing zero-overhead execution comparable to compiled languages. These functions execute directly in the VM dispatch loop without any function call overhead.

⚑ Performance Critical

Math operations are compiled to dedicated opcodes (OP_SIN, OP_COS, etc.), not function calls. This means physics calculations, AI pathfinding, and graphics transforms execute at near-native speed.

Unary Math Functions (1 Argument)

These functions take a single numeric argument:

Function Opcode Description
sin(x) OP_SIN Sine (radians)
cos(x) OP_COS Cosine (radians)
tan(x) OP_TAN Tangent (radians)
asin(x) OP_ASIN Arc sine β†’ radians
acos(x) OP_ACOS Arc cosine β†’ radians
atan(x) OP_ATAN Arc tangent β†’ radians
sqrt(x) OP_SQRT Square root
abs(x) OP_ABS Absolute value
log(x) OP_LOG Natural logarithm (base e)
floor(x) OP_FLOOR Round down to integer
ceil(x) OP_CEIL Round up to integer
deg(x) OP_DEG Radians β†’ Degrees
rad(x) OP_RAD Degrees β†’ Radians
exp(x) OP_EXP e^x (exponential)

Binary Math Functions (2 Arguments)

These functions take two numeric arguments:

Function Opcode Description
atan2(y, x) OP_ATAN2 Arc tangent of y/x (full circle)
pow(base, exp) OP_POW base^exp (exponentiation)

Examples

Physics - Projectile Motion

// Calculate projectile trajectory
def calculate_trajectory(velocity, angle_deg, time) 
{
    var angle = rad(angle_deg);  // OP_RAD
    var vx = velocity * cos(angle);  // OP_COS
    var vy = velocity * sin(angle);  // OP_SIN
    
    var gravity = -9.81;
    var x = vx * time;
    var y = vy * time + 0.5 * gravity * pow(time, 2);  // OP_POW
    
    return {x: x, y: y};
}

var pos = calculate_trajectory(50, 45, 1.0);
write("Position: ({}, {})\\n", pos.x, pos.y);

AI - Angle to Target

// Calculate angle from enemy to player
def angle_to_target(enemy_x, enemy_y, player_x, player_y) 
{
    var dx = player_x - enemy_x;
    var dy = player_y - enemy_y;
    
    // atan2 returns angle in radians (-PI to PI)
    var angle_rad = atan2(dy, dx);  // OP_ATAN2
    var angle_deg = deg(angle_rad);  // OP_DEG
    
    return angle_deg;
}

var angle = angle_to_target(100, 100, 200, 150);
write("Enemy should face: {} degrees\\n", angle);

Math - Distance Formula

// Calculate distance between two points
def distance(x1, y1, x2, y2) 
{
    var dx = x2 - x1;
    var dy = y2 - y1;
    
    // sqrt(dxΒ² + dyΒ²)
    return sqrt(pow(dx, 2) + pow(dy, 2));  // OP_SQRT, OP_POW
}

var dist = distance(0, 0, 3, 4);
write("Distance: {}\\n", dist);  // 5.0

Graphics - Circular Motion

// Object moving in a circle
process circular_motion() 
{
    var angle = 0;
    var radius = 100;
    var center_x = 400;
    var center_y = 300;
    
    loop 
    {
        var x = center_x + radius * cos(rad(angle));  // OP_RAD, OP_COS
        var y = center_y + radius * sin(rad(angle));  // OP_RAD, OP_SIN
        
        write("Position: ({}, {})\\n", floor(x), floor(y));  // OP_FLOOR
        
        angle = angle + 2;  // 2 degrees per frame
        if (angle >= 360) angle = 0;
        
        frame;
    }
}

circular_motion();
πŸš€ Performance Advantage
  • Math functions compile to single opcodes - no function call overhead
  • Physics calculations run at near-native speed
  • Critical for real-time game loops processing thousands of entities
  • Comparable performance to compiled C/C++ math operations

Functions

Functions in BuLang are first-class citizens. You can define them and pass them as arguments:

// Function definition
def add(a, b) {
    return a + b
}

result = add(5, 3)
print(result)  // 8

def apply(func, x, y) {
    return func(x, y)
}

print(apply(add, 10, 20))  // 30

def fib(n) 
{
    if (n <= 1) return n;
    return fib(n-1) + fib(n-2);
}


var start = clock();
fib(31);
var end = clock();
var ms = end - start;
assert(ms < 50, "fib perf");
pass("performance");

Fibers


def patrol() {
    var count = 0;
    while (count < 10) {
        write("Patrol tick {}\n", count);
        yield(100);  // Suspende 100ms
        count++;
    }
    print("Patrol done!");
}

def attack() {
    var count = 0;
    while (count < 5) 
    {
        write("Attack tick {}\n", count);
        yield(200);  // Suspende 200ms
        count++;
    }
    print("Attack done!");
}

process enemy() {
    x = 100;
    
    fiber patrol();  // Fiber 1
    fiber attack();  // Fiber 2
    
    // Fiber 0 (main) continua aqui
    var i = 0;
    while (i < 2000) {
       // write("Main fiber: x={}\n", x);
        x++;
        frame;
        i++;
    }
}

struct State {
    var x, y;
};

def teste(state) 
{
    while (true) 
    {
    print("A");
    state.x = state.x + 1;  
    yield(100);
    print("B");
    state.y = state.y + 1;
    yield(100);
    }
}

process enemy2() 
{
    var state = State(1, 1);
    
    fiber teste(state);   
   
    
    while (true) 
    {
        write("{} {}\n", state.x, state.y);
        frame;
    }
}

enemy2();

enemy();
 

Typed Buffers

BuLang provides typed buffers for efficient binary data manipulation. Buffers are essential for image processing, audio, file I/O, and interfacing with low-level APIs.

🎨 New in v1.1

Typed buffers bring zero-copy operations, direct file I/O, and type-safe binary data handling to BuLang.

Creating Buffers

Use the @(size, type) syntax to create typed buffers:

// Create buffers of different types
var bytes = @(1024, TYPE_UINT8);      // 1KB byte buffer
var shorts = @(100, TYPE_INT16);      // 100 signed 16-bit integers  
var ints = @(1000, TYPE_INT32);       // 1000 signed 32-bit integers
var floats = @(256, TYPE_FLOAT);      // 256 floats

// Load from file
var image_data = @("texture.raw", TYPE_UINT8);
print(image_data.length());  // Size in bytes

Type System

BuLang supports four buffer types:

Type Size Range Use Case
TYPE_UINT8 1 byte 0 to 255 Pixels, bytes, RGB
TYPE_INT16 2 bytes -32,768 to 32,767 Audio samples
TYPE_INT32 4 bytes -2.1B to 2.1B Large integers
TYPE_FLOAT 4 bytes Β±3.4e38 Coordinates, math

Buffer Methods

Indexing and Assignment

// Access elements like arrays
var buffer = @(10, TYPE_UINT8);

buffer[0] = 255;
buffer[1] = 128;
buffer[2] = 64;

print(buffer[0]);  // 255
print(buffer[1]);  // 128

fill(value) - Fill Buffer

Fill entire buffer with a single value:

// Fill all elements
var floats = @(100, TYPE_FLOAT);
floats.fill(3.14159);

print(floats[0]);   // 3.14159
print(floats[99]);  // 3.14159

clear() - Zero Buffer

Set all elements to zero:

// Clear buffer
var data = @(50, TYPE_INT32);
data.fill(42);
data.clear();

print(data[0]);  // 0

length() - Get Size

Returns the number of elements in the buffer:

// Get buffer size
var buffer = @(256, TYPE_UINT8);
print(buffer.length());  // 256

slice(start, end) - Create View

Create a view into a subset of the buffer (no copy):

// Create buffer view
var original = @(10, TYPE_INT32);
original.fill(100);

// View elements [2, 3, 4]
var view = original.slice(2, 5);
print(view.length());  // 3

// Modifying view affects original
view.fill(999);
print(original[2]);    // 999

copy(dst_offset, src, src_offset, count) - Copy Data

Copy data between buffers:

// Copy between buffers
var src = @(5, TYPE_UINT8);
var dst = @(10, TYPE_UINT8);

// Fill source
for (var i = 0; i < 5; i = i + 1) {
    src[i] = i * 10;
}

// Copy src[0:5] to dst[3:8]
dst.copy(3, src, 0, 5);

// dst = [0, 0, 0, 0, 10, 20, 30, 40, 0, 0]

save(filename) - Save to File

Write buffer contents to a binary file:

// Save buffer to disk
var pixels = @(1920 * 1080 * 4, TYPE_UINT8);
pixels.fill(128);  // Gray image
pixels.save("output.raw");

Buffer Examples

Image Processing

// Create RGB gradient (100x100)
var width = 100;
var height = 100;
var image = @(width * height * 3, TYPE_UINT8);

// Fill with gradient
for (var y = 0; y < height; y = y + 1) {
    for (var x = 0; x < width; x = x + 1) {
        var i = (y * width + x) * 3;
        image[i + 0] = x * 2;      // R
        image[i + 1] = y * 2;      // G
        image[i + 2] = 128;        // B (constant)
    }
}

// Save as raw RGB
image.save("gradient.raw");

// Convert with ImageMagick:
// $ convert -size 100x100 -depth 8 rgb:gradient.raw output.png

Audio Buffer

// Generate 440Hz sine wave (1 second)
var sample_rate = 44100;
var audio = @(sample_rate, TYPE_INT16);

var frequency = 440.0;
for (var i = 0; i < sample_rate; i = i + 1) {
    var t = i / sample_rate;
    var sample = sin(2.0 * 3.14159 * frequency * t) * 32767.0;
    audio[i] = sample;
}

audio.save("tone_440hz.raw");

Particle System

// 10,000 particles (x, y, vx, vy)
var num_particles = 10000;
var particles = @(num_particles * 4, TYPE_FLOAT);

// Initialize
for (var i = 0; i < num_particles; i = i + 1) {
    var base = i * 4;
    particles[base + 0] = random() * 800;  // x
    particles[base + 1] = random() * 600;  // y
    particles[base + 2] = (random() - 0.5) * 2;  // vx
    particles[base + 3] = (random() - 0.5) * 2;  // vy
}

Type Overflow Behavior

// Automatic wrapping for unsigned types
var b8 = @(4, TYPE_UINT8);

b8[0] = 255;   // OK
b8[1] = -1;    // Wraps to 255
b8[2] = 256;   // Wraps to 0
b8[3] = 300;   // Wraps to 44 (300 % 256)

print(b8[0]);  // 255
print(b8[1]);  // 255
print(b8[2]);  // 0
print(b8[3]);  // 44
πŸ’‘ Performance Tips
  • Use slice() for views instead of copying
  • Batch operations with fill() instead of loops
  • Binary I/O is much faster than text serialization
  • Buffers provide zero-copy FFI for C/C++ integration

Classes & Object-Oriented Programming

BuLang supports class-based OOP with methods and inheritance:


// Class definition 
class Player 
{
    def init(name, health) 
    {
        this.name = name
        this.health = health
    }

    def take_damage(amount) 
    {
        this.health = this.health - amount
    }

    def heal(amount) {
        this.health = this.health + amount
    }

    def status() {
        print(this.name + " - HP: " + this.health)
    }
}

// Using classes
player = Player.new("Hero", 100)
player.take_damage(20)
player.status()  // Hero - HP: 80



class Builder {
    var x, y;
    
    def init() {
        self.x = 0;
        self.y = 0;
    }
    
    def setX(v) {
        self.x = v;
        return self;
    }
    
    def setY(v) {
        self.y = v;
        return self;
    }
}

var b = Builder();
b.setX(10).setY(20);
print(b.x);  // 10
print(b.y);  // 20


class A {
    var a;
    def init() { 
        print("A.init()");
        self.a = 1; 
    }
    def show() { print("Level A: " + self.a); }
}

class B : A {
    var b;
    def init() { 
        print("B.init()");
        super.init();
        self.b = 2; 
    }
    def show() { 
        print("Level B: " + self.b);
        super.show();
    }
}

class C : B {
    var c;
    def init() {
        print("C.init()");
        super.init();
        self.c = 3;
    }
    def show() { 
        print("Level C: " + self.c);
        super.show();
    }
}

class D : C {
    var d;
    def init() {
        print("D.init()");
        super.init();
        self.d = 4;
    }
    def show() { 
        print("Level D: " + self.d);
        super.show();
    }
}

class E : D {
    var e;
    def init() {
        print("E.init()");
        super.init();
        self.e = 5;
    }
    def show() { 
        print("Level E: " + self.e);
        super.show();
    }
}

class F : E {
    var f;
    def init() {
        print("F.init()");
        super.init();
        self.f = 6;
    }
    def show() { 
        print("Level F: " + self.f);
        super.show();
    }
}

 

print("Creating F...");
var f = F();

print("\nCalling f.show():");
f.show();

print("\nAccessing all fields:");
print("f.a = " + f.a);
print("f.b = " + f.b);
print("f.c = " + f.c);
print("f.d = " + f.d);
print("f.e = " + f.e);
print("f.f = " + f.f);

Processes - Cooperative Multitasking

BuLang's unique process system allows you to handle thousands of concurrent tasks efficiently:

// Process example
process enemy_behavior(enemy_id) {
    while true {
        // Do something
        print("Enemy " + enemy_id + " acting")
        
        // Yield to next frame
        frame
        
        // Decision making
        if some_condition {
            break
        }
    }
}

// Spawn processes
 enemy_behavior(1)
 enemy_behavior(2)
 enemy_behavior(3)
Note: Processes use frame to pause execution and resume in the next update cycle. This enables smooth game loop integration.

Module System

BuLang features a revolutionary module system that achieves zero-overhead native function calls through compile-time resolution and advanced bit-packing techniques.

Performance: The module system uses 32-bit ModuleRef values (16+16 bit packing) for direct array indexing, eliminating hash lookups and string operations at runtime. This results in performance comparable to compiled languages while maintaining scripting flexibility.

Module Basics

BuLang provides two ways to use modules: import (namespaced) and using (flat).

Import - Namespaced Access

Import brings modules into scope but requires prefixed access:

// Import with namespace
            import timer;
            import net;

            // Use with prefix
            var t = timer.create(5.0);
            var socket = net.connect("server", 8080);

            if (timer.elapsed(t)) {
                print("Time's up!");
            }
            

Using - Flat Access

Using brings module functions into the global scope for convenient access:

// Using for flat access
            import raylib;
            using raylib;  // Must import first!

            // Now use without prefix
            InitWindow(800, 600, "Game");
            SetTargetFPS(60);

            while (!WindowShouldClose()) {
                BeginDrawing();
                ClearBackground(BLACK);
                DrawText("Hello!", 10, 10, 20, WHITE);
                EndDrawing();
            }
            

Multiple Imports

Import or use multiple modules at once:

// Multiple imports
            import timer, net, fs;
            using raylib, math;

            // Raylib and math are flat
            InitWindow(800, 600, "Game");
            var angle = math.sin(math.PI / 2);

            // Timer, net, fs use prefix
            var t = timer.create(1.0);
            var data = fs.read("config.json");
            

Import All (*)

Import all available modules at once:

// Import all modules
            import *;

            // All modules available with prefix
            var t = timer.create(1.0);
            var socket = net.connect("localhost", 8080);
            

Module Constants

Modules can export constants like numbers, strings, and booleans:

// Using module constants
            import math;

            var circle = math.PI * 2;        // 6.28...
            var half_pi = math.PI / 2;       // 1.57...

            print(math.E);                    // 2.71828...
            print(math.MAX_INT);             // 2147483647
            
Read-Only: Module constants are automatically protected. Attempting to assign to them (e.g., math.PI = 3;) results in a compile error.

Hybrid Approach

Combine import and using for optimal organization:

// Organize by usage frequency
            import timer, net, fs, json;
            using raylib, math;

            // High-frequency: flat (clean!)
            InitWindow(800, 600, "Game");
            var angle = sin(0.5);

            // Low-frequency: namespaced (organized!)
            var t = timer.create(5.0);
            var data = json.parse(fs.read("config.json"));
            var socket = net.connect("server", 8080);
            

Performance

The module system achieves exceptional performance through several innovations:

Feature Implementation Benefit
ModuleRef 32-bit value (16+16) Direct array indexing, zero hash lookups
Tree Shaking Compile-time analysis Only register functions actually used (~80% savings)
Constants Inline at compile time No runtime lookup, immediate values
Resolution Compile-time ID mapping Zero string operations at runtime

Performance Example

// Tree shaking in action
            // C++ side: Define 20 timer functions
            vm.defineModule("timer")
                ->addFunction("create", ..., 1)
                ->addFunction("elapsed", ..., 1)
                ->addFunction("reset", ..., 1)
                ->addFunction("pause", ..., 1)
                // ... 16 more functions

            // BuLang side: Use only 2
            import timer;

            var t = timer.create(5.0);
            if (timer.elapsed(t)) {
                print("Done!");
            }

            // Result: Only 2 functions registered!
            // 18 functions never loaded β†’ 90% memory saving!
            

Multi-Backend Support

The module system enables clean separation of different backends:

// Support multiple graphics libraries
            import raylib;
            import sdl;

            var useRaylib = true;

            if (useRaylib) {
                using raylib;
                InitWindow(800, 600, "Raylib Game");
            } else {
                using sdl;
                CreateWindow("SDL Game", 800, 600);
            }

            // Each backend has independent namespace!
            // No function name conflicts!
            

Defining Modules (C++)

Create modules on the C++ side using the fluent API:

// C++ module definition
            // main.cpp
            Interpreter vm;

            // Define timer module
            vm.defineModule("timer")
                ->addFunction("create", native_timer_create, 1)
                ->addFunction("elapsed", native_timer_elapsed, 1)
                ->addFunction("reset", native_timer_reset, 1);

            // Define math module
            vm.defineModule("math")
                ->addDouble("PI", 3.14159265358979)
                ->addDouble("E", 2.71828182845905)
                ->addInt("MAX_INT", 2147483647)
                ->addFunction("sin", native_math_sin, 1)
                ->addFunction("cos", native_math_cos, 1)
                ->addFunction("sqrt", native_math_sqrt, 1);

            // Compile and run
            vm.compile("game.bu");
            vm.run();
            

Best Practices

1. Use 'using' for High-Frequency Calls

// Graphics engine: many calls
            using raylib;

            // Clean game loop
            while (!WindowShouldClose()) {
                BeginDrawing();
                ClearBackground(BLACK);
                DrawCircle(100, 100, 50, RED);
                EndDrawing();
            }
            

2. Use 'import' for Low-Frequency or Clarity

// Utility modules: few calls
            import timer;
            import fs;

            // Clear which module each comes from
            var t = timer.create(1.0);
            var data = fs.read("config.json");
            

3. Organize Imports at Top

// File organization
            // Imports first
            import timer, net, fs, json;

            // Using for frequently used
            using raylib, math;

            // Constants
            var SCREEN_WIDTH = 800;
            var SCREEN_HEIGHT = 600;

            // Functions
            def gameLoop() {
                // ...
            }

            // Main logic
            InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Game");
            gameLoop();
            

Error Handling

The module system provides comprehensive compile-time validation:

// Error examples
            // Error: Module not defined
            import invalid_module;
            // Compile error: Module 'invalid_module' not defined

            // Error: Using without import
            using timer;
            // Compile error: Module 'timer' not imported. Use 'import timer;' first

            // Error: Function not found
            import timer;
            timer.invalid_function();
            // Compile error: 'invalid_function' not found in module 'timer'

            // Error: Assigning to constant
            import math;
            math.PI = 3;
            // Compile error: Invalid assignment target
            

Technical Details

ModuleRef Structure

Internally, module references use 32-bit integers:

// Bit layout
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚   16 bits        β”‚    16 bits       β”‚
            β”‚  Module ID       β”‚   Function ID    β”‚
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

            // Example: timer.create()
            Module ID:   5    (0x0005)
            Function ID: 2    (0x0002)
            PackedValue:      0x00050002

            // Runtime resolution:
            modules[5].functions[2]()  // Direct array access!
            

Compilation Pipeline

// How module calls are compiled
            BuLang Code:    timer.create(1.0)
                    ↓
            Parse:          identifier("timer") . identifier("create") (args)
                    ↓
            Resolve:        moduleId=5, funcId=2
                    ↓
            Pack:           value = (5 << 16) | 2 = 0x00050002
                    ↓
            Emit:           LOAD_CONST 0x00050002
                            LOAD_CONST 1.0
                            CALL 1
                    ↓
            Runtime:        modules[5].functions[2](vm, 1, args)
            

Memory Comparison

Approach Storage Lookup Speed
HashMap (old) String + pointer per function Hash + strcmp ~100ns
ModuleRef (new) 32-bit integer Direct array index ~1ns
100x Performance: ModuleRef achieves approximately 100x faster function resolution compared to traditional string-based hash lookups, making native calls nearly as fast as direct C function calls.

Complete Examples

Game with Timer and Graphics

// Complete game example
            import timer;
            using raylib;

            // Setup
            InitWindow(800, 600, "BuLang Game");
            SetTargetFPS(60);

            var gameTimer = timer.create(10.0);
            var score = 0;

            // Game loop
            while (!WindowShouldClose()) {
                // Update
                if (timer.elapsed(gameTimer)) {
                    score++;
                    timer.reset(gameTimer);
                }
                
                // Draw
                BeginDrawing();
                ClearBackground(BLACK);
                
                DrawText("Score: " + score, 10, 10, 20, WHITE);
                DrawText("Time-based game!", 10, 40, 16, GRAY);
                
                EndDrawing();
            }

            CloseWindow();
            

Network Client

// Network example
            import net, timer, json;

            // Connect to server
            var socket = net.connect("game.server.com", 8080);

            if (socket != nil) {
                // Send player data
                var playerData = {
                    name: "Player1",
                    level: 5
                };
                
                var jsonData = json.stringify(playerData);
                net.send(socket, jsonData);
                
                // Wait for response
                var timeout = timer.create(5.0);
                
                while (!timer.elapsed(timeout)) {
                    var response = net.receive(socket);
                    if (response != nil) {
                        var data = json.parse(response);
                        print("Server says: " + data.message);
                        break;
                    }
                }
                
                net.close(socket);
            }
            

Math-Heavy Simulation

// Physics simulation
            using math;  // Flat for frequent use

            class Particle {
                var x, y, vx, vy;
                
                def init(x, y) {
                    self.x = x;
                    self.y = y;
                    self.vx = cos(random() * PI * 2);
                    self.vy = sin(random() * PI * 2);
                }
                
                def update(dt) {
                    // Use math functions without prefix
                    self.x += self.vx * dt;
                    self.y += self.vy * dt;
                    
                    // Bounce off walls
                    if (self.x < 0 || self.x > 800) {
                        self.vx = -self.vx;
                    }
                    if (self.y < 0 || self.y > 600) {
                        self.vy = -self.vy;
                    }
                }
            }

            // Create particles
            var particles = [];
            for (var i = 0; i < 100; i++) {
                particles.push(Particle(400, 300));
            }

            // Update loop
            for (var i = 0; i < particles.length(); i++) {
                particles[i].update(0.016);
            }
            
Tip: The module system is designed for maximum flexibility. Mix and match import and using based on your needs. There's no performance penalty for either approach - both compile to the same efficient ModuleRef calls!

Raylib Integration

BuLang integrates seamlessly with Raylib for 2D/3D graphics:

// Simple Raylib game loop
def main() 
{
    InitWindow(800, 600, "BuLang Game");
    SetTargetFPS(60);

    var red = Color(255,0,0,255); // native raylib color
    var blue = Color(0,0,255,255);

    while not WindowShouldClose() 
    {
        BeginDrawing();
        ClearBackground(blue);
        
        // Draw rectangle
        DrawRectangle(100, 100, 50, 50, red);
        
        EndDrawing();
    }

    CloseWindow();
}

main()

API Reference

Core Functions

Function Description
frame Pause process until next update
yield Pause function until next update