RedBunny (.redb) Programming Language
RedBunny is a small, fast, compiled programming language with dynamic-feeling variables, a compact function syntax, and an LLVM IR backend. The compiler is written in C and lowers source code into LLVM IR before invoking clang to emit native machine binaries.
LLVM IR Backend
Direct compilation to LLVM intermediate representation linked with native C runtime.
Compact Grammar
Clean syntax with single parameter indices (arg1, arg2) and format string interpolation.
Zero-Dep C Engine
Compiler codebase written in clean C with custom hand-written lexer and recursive descent parser.
1. Installation
One command downloads RedBunny, builds the compiler, installs it, and runs the hello-world example.
Linux / macOS
curl -fsSL https://raw.githubusercontent.com/Mohamedboukerche22/redbunny/main/scripts/get.sh | bash
Installs to ~/.local. Prefix it with PREFIX=/some/path to install elsewhere. Requires curl or wget, tar, and a C toolchain; the installer installs LLVM, CMake and build tools on common distributions.
Windows
irm https://raw.githubusercontent.com/Mohamedboukerche22/redbunny/main/scripts/get.ps1 | iex
Installs to %LOCALAPPDATA%\RedBunny. Set $env:PREFIX before running to install elsewhere. Requires PowerShell 5+; the installer installs LLVM, CMake and Ninja through Chocolatey.
From a source checkout
Clone the repository, then run the platform installer.
Linux / macOS
chmod +x scripts/install.sh
./scripts/install.sh
The installer uses CMake to build and copies redb to /usr/local/bin/redb and libredb_runtime.a to /usr/local/lib/. You can override the path via PREFIX=$HOME/.local ./scripts/install.sh.
Windows
Set-ExecutionPolicy Bypass -Scope Process -Force
.\scripts\install.ps1
Manual CMake Build
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
sudo cmake --install build
Vim/Neovim: syntax highlighting, .redb filetype detection, comments and indentation are in editors/vim. See editors/vim/README.md for package, runtimepath and lazy.nvim setup.
2. Hello World
Save the following program in a file named hello.redb:
@include "std"
main() {
writeln("Hello, World!");
}
Compile and run with the redb executable:
redb hello.redb -o hello && ./hello
4. Program Structure
Every executable program must define exactly one main() block. Top-level scope is strictly restricted to:
@include "..."declarationsmain() { ... }entry point- Function assignments like
name = do(...) { ... }
main() returns an integer exit code to the operating system. A bare return exits with 0, while return 42 exits with code 42.
5. Variables and Types
Variables are introduced by assignment. The first assignment fixes the static LLVM IR type:
main() {
name = "Mohamed" -- str
age = 20 -- int (signed 64-bit)
pi = 3.14159 -- double (64-bit IEEE)
alive = true -- bool
writeformat("Name: {name}, Age: {age}, pi: {pi}, alive: {alive}\n")
}
Type Reference Table
| Type | LLVM / Memory Equivalent | Description |
|---|---|---|
| int | i64 | Signed 64-bit integer |
| float | float | 32-bit IEEE floating point |
| double | double | 64-bit IEEE floating point (default for decimals) |
| str | i8* | Null-terminated UTF-8 byte string |
| bool | i1 | Boolean literal (true / false) |
| void | void | Written as {} in function signatures |
Arrays
An array holds many values of one element type. Declare one with array<T>, where T is int, float, double, str, auto, or another array type.
main() {
array<int> nums(5) -- 5 elements, all 0
nums[0] = 10
nums[4] = 40
writeln(nums[0] + nums[4]) -- 50
writeln(nums.size()) -- 5
nums.resize(7) -- grow to 7; new elements are 0
writeln(nums.size()) -- 7
array<auto> alias = nums -- element type copied from nums (int)
alias[1] = 3
writeln(nums[1]) -- 3: alias and nums are the same array
array<array<int>> matrix(2) -- two inner arrays, each starts empty
array<int> row(3)
row[2] = 9
matrix[1] = row
writeln(matrix[1][2]) -- 9
}
Array forms
| Form | Meaning |
|---|---|
array<int> a(5) | 5 elements, all 0 (0.0 for double, empty for str) |
array<int> a | Empty array (size 0) |
array<int> a = b | a refers to the same array as b |
array<auto> a = b | a takes the element type of b |
array<auto> a(5) | No initializer, so the element type defaults to int |
a[i], a[i] = x | Read or write element i (indexes start at 0) |
a.size() | Number of elements |
a.resize(n) | Change the size; new elements are zero or empty |
array<array<int>> m(3) | Nested arrays; m[0][1] = 7 works |
Arrays can be passed to and returned from functions:
total = do(array<int> -> int) {
return arg1[0] + arg1[1]
}
- An index outside
0tosize() - 1stops the program with a runtime error. - Assigning one array to another does not copy it; both names share the elements.
- Inner arrays created by
array<array<T>> m(n)start empty. Callresize()on them before indexing. - A whole array cannot be printed with
writeorwriteln. Print one element at a time. arrayandautoare reserved words.
6. Operators
Arithmetic and comparison operators supported by RedBunny backend:
-- String concatenation uses +
full = "Red" + "Bunny"
-- Numerical arithmetic
x = (10 + 2 * 5) / 4
-- Comparison & Logical operators
is_valid = (x >= 3) && !false
7. Output Functions
RedBunny runtime provides 3 primary formatted output primitives:
write(val): Prints without a trailing newline.writeln(val): Prints value followed by a newline.writeformat("..."): Variable interpolation inside template strings. Use{{and}}for literal braces.
main() {
item = "Laptop"
price = 999.99
writeformat("Item: {item} | Price: ${price}\n")
writeformat("Literal brace demo: {{item}}\n")
}
8. Control Flow
If / Else Statements
if (score >= 90) {
writeln("A")
} else if (score >= 80) {
writeln("B")
} else {
writeln("C")
}
Inclusive Range For Loop
The range loop for(i in start to end) is inclusive on both ends:
-- Runs 10 times from 0 through 9
for(i in 0 to 9) {
writeln(i)
}
Descending Range For Loop
Use downTo to count downward. Both ends are inclusive:
-- Runs 5 times: 5, 4, 3, 2, 1
for(i in 5 downTo 1) {
writeln(i)
}
While Loop, Break & Continue
n = 0
while (n < 10) {
n = n + 1
if (n == 2) { continue }
if (n == 6) { break }
write(n)
}
9. Functions & Signatures
RedBunny uses a unique signature format for defining functional variables. Parameters are bound to positional names arg1, arg2, etc.
-- Takes two ints, returns int
add = do(int, int -> int) {
return arg1 + arg2
}
-- Function taking no args returning int (=> is an alias for ->)
get_answer = doto({} => int) {
return 42
}
-- Recursion: Functions are hoisted before main
fact = do(int -> int) {
if (arg1 <= 1) { return 1 }
return arg1 * fact(arg1 - 1)
}
main() {
writeln(add(10, 20))
writeln(fact(5)) -- Output: 120
}
10. Includes (@include)
Include standard or user module source files. Includes resolve relative to the source file first, then the standard library directory (lib/ in a checkout or share/redb/lib when installed). Set REDB_STDLIB to override. The suffix .redb is optional.
@include "std"
@include "mathlib"
main() {
writeln(square(9))
}
11. Interactive Playground & LLVM IR Visualizer
Test RedBunny syntax, simulate program execution, or view generated LLVM IR code directly in your browser:
12. Command Line Interface
Usage: redb <file.redb> [options]
| Flag / Option | Description |
|---|---|
| -o <path> | Write compiled executable binary to specified path. |
| -S, --emit-ir | Write LLVM IR file only without invoking Clang linker step. |
| --keep-ir | Keep generated .ll IR file alongside binary. |
| --run | Compile and immediately execute output program. |
Environment Variables
REDB_CC: C compiler executable used to link IR (default:clang).REDB_RUNTIME: Explicit path to static runtime archivelibredb_runtime.a.REDB_STDLIB: Directory containing standard library modules (default:lib/in a checkout orshare/redb/libwhen installed).
13. Repository Layout & C Architecture
The compiler is written cleanly across modular C files:
| Path | Compiler Phase Responsibility |
|---|---|
| include/redb.h | Tokens, AST data structures, shared types, and function prototypes. |
| src/lexer.c | Source text stream tokenizer into Lexer token streams. |
| src/parser.c | Recursive-descent parser building Abstract Syntax Tree (AST). |
| src/include.c | File-relative source include resolution (@include). |
| src/typecheck.c | Scope resolution, static type inference, string template lowering. |
| src/codegen.c | Lowers AST nodes into formatted LLVM IR. |
| runtime/runtime.c | Native C runtime library called by generated LLVM code. |
| lib/mathlib.redb | Standard math module: powers, roots, trig, exp/ln, int/double min/max and constants. |
| src/main.c | CLI driver, flags, Clang invocation, and program execution. |
14. Runtime C ABI
Generated LLVM IR emits calls into the C runtime ABI (libredb_runtime.a):
void redb_write(const char *str);
void redb_writeln(const char *str);
void redb_write_int(long long value);
void redb_write_double(double value);
void redb_write_bool(int value);
void redb_newline(void);
char *redb_str_concat(const char *left, const char *right);
int redb_str_eq(const char *left, const char *right);
void *redb_alloc(size_t size);
void redb_writeformat(const char *fmt, ...);
15. Current Limitations (v0.1.0)
Deliberate Boundaries in v0.1.0:
3. Comments & Semicolons
RedBunny uses distinct comment token markers. Semicolons are optional across all statements.