A minimalist interpreter for the bloa scripting language.
- Variables and expressions (arithmetic, logic, comparison)
- Control flow: if/else, while, for/foreach/for-in, repeat, break, continue, try/except
let/varlocal declarations- Functions with parameters
- Classes with methods and inheritance (
extends) - Modules:
usefor importing,requirefor including files - Built-in functions: print, range, len, str, int, float, append, ref, deref, set_ref, is_ref, copy, clone, slice, sorted, sum, min, max, type, vars, keys, get, set, mysql_connect, mysql_query, mysql_exec, mysql_close, mysql_escape
- Lists, strings, numbers, booleans
- I/O: say (print), ask (input)
echo,isset,unsetnewobject creation for Java-style instantiation- BAAR archive support for
.baarpackages and built-inbaar_*helpers - cURL support:
curl_get,curl_post,curl_request - SQLite utilities:
sqlite_query,sqlite_exec - JSON utilities:
json_parse,json_stringify - CSV utilities:
csv_parse,csv_stringify - Filesystem helpers:
path_is_absolute,path_normalize,file_ext - Exception handling: try/except
- Standard library in
stdlib/(math, io, string)
// This is a single-line comment
say "Hello"
/* This is a
block comment */
say "World"
x = 42
p = ref("x")
say deref(p)
set_ref(p, 100)
say x
say is_ref(p)
y = 10
p = &y
say *p
set_ref(p, 20)
say y
let x = 42
var y = x + 10
say "Result: " + str(y)
if (x > 10) {
say "big"
} else {
say "small"
}
foreach (range(0, 5) as value) {
if (value == 3) {
continue;
}
if (value == 4) {
break;
}
say value
}
for (item in ["a", "b", "c"]) {
say item
}
try {
read_file("missing.txt")
} except {
say "Failed to read file"
}
function greet(name) {
say "Hello, " + name
}
greet("World")
class Animal {
function speak(self) {
say "Animal sound"
}
}
class Dog extends Animal {
function speak(self) {
say "Woof!"
}
}
d = Dog()
d.speak()
class Counter {
function __init__(self, start) {
self.value = start
}
function inc(self) {
self.value = self.value + 1
}
}
c = new Counter(10)
c.inc()
say c.value
files = [
["main.bloa", "say \"Hello from archive\"\n"],
["data.txt", "archive payload"]
]
baar_create("app.baar", files)
list = baar_list("app.baar")
say list
content = baar_read("app.baar", "data.txt")
say content
A .baar file can also be executed directly with bloa app.baar. The interpreter will choose main.bloa, index.bloa, the first .bloa entry, or the first file in the archive.
response = curl_get("https://example.com/data")
json = curl_post("https://example.com/api", "payload")
response = curl_request("https://example.com/api", "PUT", "body")
rows = sqlite_query("data.db", "SELECT id, name FROM users")
for (rows as row) {
say row[0] + ": " + row[1]
}
sqlite_exec("data.db", "CREATE TABLE IF NOT EXISTS users(id INTEGER, name TEXT)")
use math;
say "Square root of 4: " + str(sqrt(4))
The standard library is built-in and automatically available, providing functions without requiring external files:
sqrt(x): Square rootpow(base, exp): Powersin(x),cos(x),tan(x): Trigonometric functionslog(x),exp(x): Logarithm and exponentialabs(x): Absolute valuefloor(x),ceil(x),round(x): Rounding functionspi(),e(): Mathematical constants
read_file(path): Read file content as stringwrite_file(path, content): Write string to fileexists(path): Check if file/directory existslist_dir(path): List directory contents as list of stringsmkdir(path): Create directoryrmdir(path): Remove directoryremove(path): Remove filecopy_file(from, to): Copy filemove(from, to): Move/rename filefile_size(path): Get file size in bytesis_dir(path): Check if path is a directory
len(s): String length (built-in)split(s, delim): Split string by delimiterjoin(list, sep): Join list of strings with separatorsubstr(s, start, len?): Substringfind(s, sub): Find substring positionreplace(s, old, new): Replace all occurrencesto_upper(s),to_lower(s): Case conversiontrim(s): Remove leading/trailing whitespacestarts_with(s, prefix): Check if string starts with prefixends_with(s, suffix): Check if string ends with suffixcontains(s, sub): Check if string contains substringreverse(s): Reverse stringrepeat(s, n): Repeat string n times
random_int(max)orrandom_int(min, max): Random integerrandom_float(max)orrandom_float(min, max): Random floatnow(): Current timestamp in millisecondssystem(cmd): Run shell command and return exit statusshell(cmd): Run shell command and return stdout outputgetenv(name): Read environment variablesetenv(name, value): Set environment variablesleep(ms): Sleep for millisecondspwd()/cwd(): Get current working directorypath_join(a, b, ...): Join path segmentspath_is_absolute(path): Check if a path is absolutepath_normalize(path): Normalize a filesystem pathfile_ext(path): Get the file extensionbasename(path): Get filename componentdirname(path): Parent directorymkdirs(path): Create directories recursivelyglob(pattern): Find files using wildcard patternsjson_parse(text): Parse JSON text into objects and listsjson_stringify(value): Serialize a value to JSON textcsv_parse(text, delim?): Parse CSV text into rowscsv_stringify(rows, delim?): Serialize rows into CSV textbase64_encode(text): Encode text to Base64base64_decode(text): Decode Base64 textuuid4(): Generate a random UUID v4 stringregex_match(text, pattern): Match text against a regular expressionregex_replace(text, pattern, replacement): Replace text using regex
mysql_connect(host, user, pass, db)ormysql_connect(host, user, pass, db, port): Open MySQL connection and return connection idmysql_query(conn, sql): Execute SELECT/statement and return rows as list of listsmysql_exec(conn, sql): Execute an update/insert/delete and return affected rows countmysql_escape(conn, value): Escape string for SQL safetymysql_close(conn): Close the MySQL connection
All functions are available globally.
say "Square root of 16: " + str(sqrt(16))
say "Pi value: " + str(pi())
say "Uppercase: " + to_upper("hello")
conn = mysql_connect("127.0.0.1", "user", "pass", "testdb")
rows = mysql_query(conn, "SELECT id, name FROM users")
for (row as record) {
say "id=" + row[0] + " name=" + row[1]
}
affected = mysql_exec(conn, "UPDATE users SET active=1 WHERE active=0")
say "Updated: " + str(affected)
mysql_close(conn)
mkdir -p build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build .After building, run:
cmake --build build --target checkThis executes a small test harness that verifies JSON, CSV, filesystem, base64, regex, and other runtime helpers.
To create Debian packages for amd64 and i386 (requires multilib tools):
# amd64
mkdir -p package_amd64/DEBIAN package_amd64/usr/local/bin
cp build/bloa package_amd64/usr/local/bin/
# edit control fields then
chmod 0755 package_amd64/DEBIAN
sudo dpkg-deb --build package_amd64 bloa_0.2.0-alpha_amd64.deb
# i386 (after installing g++-multilib libc6-dev-i386)
rm -rf build32 && mkdir build32 && cd build32
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32 ..
cmake --build .
mkdir -p package_i386/DEBIAN package_i386/usr/local/bin
cp bloa ../package_i386/usr/local/bin/
# edit control & build
chmod 0755 package_i386/DEBIAN
sudo dpkg-deb --build package_i386 bloa_0.2.0-alpha_i386.deb
# all architecture (use when you just want a generic package, e.g. containing
# scripts or the prebuilt binary for the current host; the package is marked
# "Architecture: all" so dpkg will install it regardless of machine type)
mkdir -p package_all/DEBIAN package_all/usr/local/bin
# copy whatever payload makes sense; we'll just include amd64 binary here as an
# example, but you can replace it with a shell wrapper or source archive
cp build/bloa package_all/usr/local/bin/
cat > package_all/DEBIAN/control <<'EOF'
Package: bloa
Version: 0.2.0-alpha
Section: utils
Priority: optional
Architecture: all
Maintainer: bloa <noreply@local>
Description: bloa language runtime (architecture independent "all" package)
EOF
chmod 0755 package_all/DEBIAN
sudo dpkg-deb --build package_all bloa_0.2.0-alpha_all.deb```
This is the 1.0.0-RC1 release candidate of BLOA.
A GitHub Actions workflow (.github/workflows/release.yml) builds both architectures and packages .deb files when a tag is pushed.
See older changelogs/1.0.0.md for release notes on version 1.0.0-RC1.