rebo
Rebo is an expression-based scripting language with syntax heavily inspired by rust. It is statically typed and has backwards type inference. When executing a script, rebo fully analyses and checks the source code including all types, before it runs. While that approach is usually used for AOT (Ahead Of Time compilers), the execution of rebo is interpreted and thus embeddable on all systems. This approach also allows defining types and functions after their first use.
Samples
A few samples to see rebo in action.
fibonacci
Recursive:
#![allow(unused)] fn main() { fn fib_rec(i: int) -> int { match i { 0 => 0, 1 => 1, i => fib_rec(i - 1) + fib_rec(i - 2), } } assert_eq(fib_rec(6), 8); }
Iterative:
#![allow(unused)] fn main() { fn fib_iter(mut i: int) -> int { if i <= 1 { return i; } let mut a = 0; let mut b = 1; i -= 2; while i >= 0 { let temp = b; b += a; a = temp; i -= 1; } b } assert_eq(fib_iter(90), 2880067194370816120); }
Merge Sort
#![allow(unused)] fn main() { fn merge_sort<T>(array: List<T>) -> List<T> { merge_sort_internal(array, 0, array.len()) } fn merge_sort_internal<T>(array: List<T>, start: int, end: int) -> List<T> { let len = end - start; if len == 1 { return List::of(array.get(start).unwrap()); } // split let middle = start + len/2; let left = merge_sort_internal(array, start, middle); let right = merge_sort_internal(array, middle, end); let sorted = List::new(); let mut left_index = 0; let mut right_index = 0; // merge while left_index < left.len() && right_index < right.len() { let left = left.get(left_index).unwrap(); let right = right.get(right_index).unwrap(); if left <= right { sorted.push(left); left_index += 1; } else { sorted.push(right); right_index += 1; } } while left_index < left.len() { sorted.push(left.get(left_index).unwrap()); left_index += 1; } while right_index < right.len() { sorted.push(right.get(right_index).unwrap()); right_index += 1; } sorted } let array = List::of(5, 4, 7, 2, 1, 3, 2); let sorted = merge_sort(array); assert_eq(sorted, List::of(1, 2, 2, 3, 4, 5, 7)); }
Features
See the features of rebo in action with code examples.
Hello World
#![allow(unused)] fn main() { print("Hello, World!"); }
Expressions
Everything in rebo is an expression.
#![allow(unused)] fn main() { let a = 0; let mut a = a + 1; let mut c = add_one(a); c += { let c = 1337; c + 42 }; assert(c == 1381); }
Math and Numbers
#![allow(unused)] fn main() { assert_eq(1 + 2 * 3, 7); assert_eq(5 - -5 - 10, 0); assert_eq(0b1 + 0x2 * 3 * 4, 25); assert_eq(1 ^ 3, 2); assert_eq(5 % 2, 1); assert_eq(float::to_bits(0.0), 0); assert_eq(float::from_bits(0x4060B66666666666), 133.7); // parse numbers let a = "1337"; let b = "42."; let c = "abc"; let d = "-1337"; let e = "-42."; assert(a.parse_int() == Result::Ok(1337)); assert(b.parse_int() == Result::Err(())); assert(c.parse_int() == Result::Err(())); assert(d.parse_int() == Result::Ok(-1337)); assert(e.parse_int() == Result::Err(())); assert(a.parse_float() == Result::Ok(1337.)); assert(b.parse_float() == Result::Ok(42.)); assert(c.parse_float() == Result::Err(())); assert(d.parse_float() == Result::Ok(-1337.)); assert(e.parse_float() == Result::Ok(-42.)); }
Control Flow
#![allow(unused)] fn main() { // if - else if - else assert(if true { 1337 } else { panic("F") } + 5 == 1342); // match assert(match 1 { 0 => 21, 1 => 1337, _ => 42, } == 1337); let res = match 1 { 0 => "it was a 0", foo => f"it was something else: {foo}", }; assert_eq(res, "it was something else: 1"); }
Loop, While, For
#![allow(unused)] fn main() { // loop let val = loop { break "loop"; }; assert_eq(val, "loop"); // loop labels let val = 'outer: loop { loop { break 'outer "outer loop" } }; assert_eq(val, "outer loop"); // while let mut i = 0; while i < 3 { i += 1; } assert_eq(i, 3); // while with labels let mut i = 0; 'outer: while true { while true { i += 1; break 'outer; } }; assert_eq(i, 1); // for loop let list = List::of(1337, 21); let mut sum = 0; for i in list { sum += i; } assert(sum == 1358); let mut sum = 0; 'outer: for i in list { for i in list { sum += i; break 'outer; } } assert_eq(sum, 1337); }
Functions
#![allow(unused)] fn main() { // functions can be used before their definition assert(foo(10, 20) == 60); fn foo(mut x: int, mut y: int) -> int { x += 10; y += 20; x + y } fn choose(cond: bool, a: int, b: int) -> int { if cond { return a; } b } assert_eq(choose(true, 1, 2), 1); assert_eq(choose(false, 1, 2), 2); }
Advanced
#![allow(unused)] fn main() { // functions as first-class citizens let new: fn<T>() -> List<T> = List::new; let test = assert; test(f"{new()}" == "[]"); // anonymous functions struct NewList { new: fn<T>() -> List<T>, } let new_list = NewList { new: fn<T>() -> List<T> { List::new() }, }; let new = new_list.new; assert(f"{new()}" == "[]"); }
Types
#![allow(unused)] fn main() { // unit - rebo's void type let unit = (); assert(unit == ()); // strings // string lengths let s = "uiae"; assert(s.len_utf8() == 4); assert(s.len_utf16() == 4); assert(s.len_utf32() == 4); assert(s.len_grapheme_clusters() == 4); assert(s.len_legacy_grapheme_clusters() == 4); // format strings let foo = 4; assert(f"{foo * 10 + 2} is the answer" == "42 is the answer"); assert(f"{1337:#8x}" == " 0x539"); assert(f"{"uiae":?}" == "\"uiae\""); // string slicing let s = "abcdef"; assert(s.slice(1) == "bcdef"); assert(s.slice(-1) == "f"); assert(s.slice(1, 3) == "bc"); assert(s.slice(0, -1) == "abcde"); assert(s.slice(1, -1) == "bcde"); assert(s.slice(-2, -1) == "e"); let s = "αβγδεζ"; assert(s.slice(1) == "βγδεζ"); assert(s.slice(-1) == "ζ"); assert(s.slice(1, 3) == "βγ"); assert(s.slice(0, -1) == "αβγδε"); assert(s.slice(1, -1) == "βγδε"); assert(s.slice(-2, -1) == "ε"); }
Statics
#![allow(unused)] fn main() { assert(MY_STATIC == 42); static mut MY_STATIC = 42; MY_STATIC = 1337; use_static(); assert(MY_STATIC == 21); fn use_static() { assert(MY_STATIC == 1337); MY_STATIC = 21; } }
Structs
#![allow(unused)] fn main() { struct Foo { a: int, b: string, } let mut foo = Foo { a: 1337, b: "uiae" }; assert(f"{foo}" == "Foo { a: 1337, b: uiae }"); let foo2 = Foo { a: 1337, b: "dtrn" }; assert(foo != foo2); foo.b = "dtrn"; assert(foo == foo2); struct Inner { x: int } struct Outer { inner: Inner } let mut outer = Outer { inner: Inner { x: 1337, } }; assert(outer.inner.x == 1337); outer.inner.x = 420; assert(outer.inner.x == 420); outer.inner = Inner { x: 69 }; assert(outer.inner.x == 69); let mut other_inner = Inner { x: 42 }; outer.inner = other_inner; assert(outer.inner.x == 42); other_inner.x = 21; assert(outer.inner.x == 21); }
Methods
#![allow(unused)] fn main() { struct Foo { a: int, b: string, } impl Foo { fn new(a: int, b: string) -> Foo { Foo { a: a, b: b } } fn a(self) -> int { self.a } fn b(self) -> string { self.b } } assert(f"{Foo::new(1337, "uiae"):?}" == "Foo { a: 1337, b: \"uiae\" }"); let foo = Foo::new(42, "uiae"); assert(foo.a() == 42 && foo.b() == "uiae"); assert(Foo::a(foo) == 42 && Foo::b(foo) == "uiae"); struct Something {} impl Something { fn returns_self(self) -> Something { self } } let foo = Something {}; assert(foo.returns_self().returns_self() == foo); }
Enums
#![allow(unused)] fn main() { // enums enum Value { Unit, Integer(int), Float(float), } // enum matching fn value_to_string(value: Value) -> string { match value { Value::Unit => "unit", Value::Integer(i) => f"integer: {i}", _ => "something else", } } let unit = Value::Unit; let i = Value::Integer(1337); let f = Value::Float(42.); assert(f"{unit}, {i}, {f}" == "Unit, Integer(1337), Float(42)"); assert(value_to_string(unit) == "unit"); assert(value_to_string(i) == "integer: 1337"); assert(value_to_string(f) == "something else"); }
Generics
#![allow(unused)] fn main() { // generics struct GenericFoo<T> { bar: GenericBar<T>, } struct GenericBar<U> { t: U, } let foo = GenericFoo { bar: GenericBar { t: 1337 } }; assert(foo.bar.t + 1 == 1338); fn id<T>(t: T) -> T { t } assert(id(id(1337)) == 1337); fn id1<U, V>(u: U, v: V) -> V { v } fn id2<T>(t: T) -> T { id1(42, t) } assert(id2(1337) == 1337); // defined in stdlib: // enum Option<T> { // Some(T), // None, // } let a = Option::Some(1337); let b = Option::Some(42); let c = Option::None; assert(a.unwrap() + b.unwrap() + c.unwrap_or(21) == 1400); let a = Option::Some(42); let b: Option<Option<string>> = Option::Some(Option::Some("uiae")); assert(f"{a}, {b}, {b.unwrap().unwrap()}" == "Some(42), Some(Some(uiae)), uiae"); // defined in stdlib: // enum Result<T, E> { // Ok(T), // Err(E), // } let a = Result::Err("error"); let b = Result::Err("error"); assert(a == b); let a = Result::Ok(1337); let b = Result::Ok(42); assert(f"{a}, {b}, {a.unwrap() + b.unwrap()}" == "Ok(1337), Ok(42), 1379"); }
Includes
#![allow(unused)] fn main() { // filename: test-include.re static MY_INCLUDED_STATIC = "uiae"; fn included_fn() -> int { MY_STATIC } static DEFINED_IN_INCLUDE = 1; fn defined_in_include() -> int { 2 }; assert_eq(5, DEFINED_AFTER_INCLUDE); assert_eq(7, defined_after_include()); "this is the inclusion result" }
#![allow(unused)] fn main() { static mut MY_STATIC = 42; assert_eq(1, DEFINED_IN_INCLUDE); assert_eq(2, defined_in_include()); let value = include "test-include.re"; assert_eq(value, "this is the inclusion result"); static DEFINED_AFTER_INCLUDE = 5; fn defined_after_include() -> int { 7 } assert(MY_INCLUDED_STATIC == "uiae"); assert(included_fn() == 42); }
Collections
List<T>
#![allow(unused)] fn main() { let list = List::new(); assert(f"{list}" == "[]"); list.push(1337); list.push(42); assert(f"{list}" == "[1337, 42]"); assert(list.get(0) == Option::Some(1337)); assert(list.get(1).unwrap() == 42); assert(list.get(2) == Option::None); let list2 = List::of(1, 2, 3); assert(f"{list2}" == "[1, 2, 3]"); list.set(1, 21).unwrap(); assert(list.get(1).unwrap() == 21); }
Map<K, V>
#![allow(unused)] fn main() { let mut map = Map::new(); map.insert("a", 1337); map.insert("b", -42); assert(f"{map}" == "{a: 1337, b: -42}"); assert(f"{map.keys()}" == "[a, b]"); assert(f"{map.values()}" == "[1337, -42]"); assert(map.get("a") == Option::Some(1337)); assert(map.remove("b") == Option::Some(-42)); assert(map.get("b") == Option::None); }
Set<T>
#![allow(unused)] fn main() { let mut set = Set::new(); set.insert(1337); set.insert(42); assert(set.len() == 2); assert(set.insert(1337) == false); assert(set.len() == 2); assert(set.contains(42)); assert(set.remove(42)); assert(!set.contains(42)); }