ObSL is a small interpreted, thread safe, dynamically typed programming language designed for game logic inside
the
(work in progress) Obliberry Game
Engine, made to be easily extendable.
- Documentation
- Features
- Building from Source
- Quick Start
- Language Basics
- Printing
- Operators
- Control Flow
- Loops
- Arrays
- Functions
- Structs
- Objects
- Modules
- Error Handling
- Built-in Library
- Reflection
- Garbage Collection
- Examples
- Todo
- Resources & Acknowledgments
- License
- Dynamically typed variables
- Functions with default parameters
- Closures and first-class functions
- Structs with custom constructors
- Dynamic objects
- Dynamic arrays with negative indexing
- Module importing
- Exception handling
- Runtime reflection
- Built-in regex support
- Automatic garbage collection with cyclic reference handling
- C++20 compatible compiler (GCC, Clang, MSVC)
- CMake
- Git
git clone https://github.com/torkelicious/ObSL.git
cd ObSLmkdir build
cd build
cmake ..
cmake --build .| Option | Default | Description |
|---|---|---|
OBSL_BUILD_RUNTIME |
ON |
Builds the obsl_runtime CLI executable. Set to OFF to build only the obsl static library useful when embedding ObSL into another project (e.g. the Obliberry Game Engine) that doesn't need the standalone CLI/REPL. |
cmake .. -DOBSL_BUILD_RUNTIME=OFF./obsl_runtimeOnly available when
OBSL_BUILD_RUNTIMEisON(the default).
println "Hello, World!";
Variables are dynamically typed:
var score = 100;
var name = "Player";
var alive = true;
var nothing = null;
Variables are declared using the var keyword:
var number = 100;
var text = "hello";
var enabled = true;
Variables can change type:
var value = 10;
value = "Now a string";
ObSL provides print and println.
print "Hello ";
print "World";
Output:
Hello World
println "Hello";
println "World";
Output:
Hello
World
var a = 10 + 5;
var b = 10 - 5;
var c = 5 * 2;
var d = 10 / 2;
var remainder = 10 % 3;
a == b;
a != b;
a > b;
a >= b;
a < b;
a <= b;
true and false;
true or false;
!true;
// Alternative syntax
true && false;
true || false;
a = 10;
a += 5;
a -= 2;
a *= 3;
a /= 2;
a++;
a--;
var bitAnd = 5 & 3;
var bitOr = 5 | 3;
var bitXor = 5 ^ 3;
var bitNot = ~0;
var left = 2 << 3;
var right = 16 >> 2;
var score = 100;
if (score > 50) {
println "Passed!";
} else {
println "Failed!";
}
var value = 2;
switch (value) {
case 1:
println "One";
case 2:
println "Two";
default:
println "Unknown";
}
The is operator checks runtime type:
var value = 10;
if (value is number) {
println "Number";
}
number
string
boolean / bool
null / nil
function / fn
array
object
Note: These are the type names accepted by
is, which are distinct from the strings returned by the reflection functiontype_of()most notably,isusesfunction/fnwheretype_of()returns"callable". See the Standard Library Reference fortype_of()'s exact return values.
var result = 5 + 3;
if (result is number) {
println "Math result";
}
is binds tighter than equality checks:
(x == y) is boolean;
var value = null;
if (value is null) {
println "Empty";
}
if (value is nil) {
println "Also empty";
}
var i = 0;
while (i < 5) {
println i;
i++;
}
var total = 0;
for (var i = 0; i < 10; i++) {
total += i;
}
var numbers = [10, 20, 30];
var sum = 0;
foreach (var number in numbers) {
sum += number;
}
for (var i = 0; i < 10; i++) {
if (i == 5) {
break;
}
}
Arrays are dynamic and support negative indexing:
var arr = [10, 20, 30];
println arr[0];
arr[-1] = 99;
arr[-1] // last element
arr[-2] // second last element
println arr.len;
arr.push(100);
var item = arr.pop();
arr.clear();
fn sayHello(name) {
println "Hello " + name;
}
sayHello("Player");
fn add(a, b) {
return a + b;
}
var result = add(5, 10);
fn damage(amount = 10) {
return amount;
}
println damage();
fn createAdder(amount) {
fn add(value) {
return value + amount;
}
return add;
}
var addFive = createAdder(5);
println addFive(10);
struct Vector3 {
x;
y;
z;
}
struct Player {
health = 100;
name = "Unknown";
}
Example:
var player = Player();
println player.health;
var obj = Object();
obj.value = 42;
obj.printValue = printValue;
using "assets/scripts/module.obsl";
try {
var result = sqrt("invalid");
} catch (err) {
println err;
}
assert(player != null, "Player missing");
throw("Something failed!");
to_string(value);
to_fixed(value, decimals);
to_num(string);
sqrt(x);
pow(a, b);
abs(x);
min(a, b);
max(a, b);
floor(x);
ceil(x);
round(x);
random();
clamp(value, min, max);
sin(x);
cos(x);
tan(x);
atan2(y, x);
pi();
rad(degrees);
deg(radians);
lerp(start, end, t);
map_value(value, inMin, inMax, outMin, outMax);
len(text);
to_upper(text);
to_lower(text);
trim(text);
contains(text, value);
starts_with(text, value);
substring(text, start, length);
replace(text, old, new);
regex_match(text, pattern);
regex_search(text, pattern);
regex_replace(text, pattern, replacement);
regex_find_all(text, pattern);
file_exists(path);
read_file(path);
write_file(path, data);
get_file_ext(path);
read();
readln();
clock();
sleep_thread(seconds);
get_env(name);
exit(code);
type_of(value);
Returns:
null
bool
number
string
array
object
callable
get_arity(function);
has_field(object, "name");
get_fields(object);
ObSL includes automatic garbage collection with cyclic reference handling.
See the Examples/ directory for additional runnable usage examples.
- Proper Linter
- LSP?
This is still very much a toy language :)
ObSL's design and implementation were informed by:
- Crafting Interpreters by Robert Nystrom
- A Quick Guide to Interpreter Design in Modern C++ by Ayman Alheraki
| Library | License | Purpose | More |
|---|---|---|---|
| nlohmann/json v3.12.0 | MIT | JSON output for --lint mode |
docs/THIRD_PARTY_LICENSES.md |
ObSL is licensed under the MIT License. See third-party dependency licenses in THIRD_PARTY_LICENSES.md.