Syntax

The syntax of Oasis is very simple.

Variable Definitons

To define a variable, use the let keyword.

let foo = 1

To make a constant, use the const keyword.

let const foo = 1

To assign a new value to a defined variable, no keyword is needed.

let foo = 1
foo = 2

You can also modify a variable’s value by using the different forms of the = operator.

let foo = 1
foo = 2
foo += 1
foo -= 1

Relative Expressions A relative expression is an expression that is evaluated whenever it’s used. It’s titled so because it’s an expression relative to another.

let foo = 12
rel bar = foo * 2

io:print(bar) // 24
foo = 13
io:print(bar) // 26

A relative expression cannot share a name with a variable. Relative expressions are

Indexing

To get a property of something, use :. For example:

// JavaScript:
foo.bar // property bar of foo

// oasis:
foo:bar // property bar of foo

To get the index of an array, use :(index). For example:

// JavaScript:
foo[12] // element at index 12 of foo

// oasis:
foo:(12) // element at index 12 of foo

Block Statements

For most block statements, a marker for the beginning of a block is not necessary. All blocks must end with the end keyword.

if 1 == 1
    io:print("woah! 1 is equal to 1!!")
end

if 2 == 2
    io:print("woah! 2 is equal to 2!!")
else
    io:print("woah! 2 is not equal to 2!!")
end

// block passed to a function
5:times do
    io:print("hello") // prints "hello" 5 times
end

Literals

Oasis has string literals, number literals, boolean literals, list literals, dictionary literals, and char literals.

let foo = "hello"
let bar = 1
let baz = true
let qux = [1, 2, 3]
let quux = {"foo" | "hello", "bar" | 1}
let corge = 'a'

Functions

Functions only exist in the form of function literals. These are practically lambdas.

let foo = fn(x)
    return x * x
end

// Function with no parameters

let fooBar = fn
    // does something or other
end

foo(2) // 4

// You can also pass functions to functions!

let bar = fn(x, y)
    return x(y)
end

bar(fn(n) return 1 / n end, 5) // 1/5
// alternatively
bar(fn(n) => 1 / n, 5)

// There is a function shorthand, for single-expression functions.
let square = fn(x) => x * x

Prototypes

Prototypes also only exist in literal form.

let foo = proto
    x = 2
    y = fn(n) => this:x * n
end

io:print(foo:x) // 2
io:print(foo:n(4)) // 8

// Prototypes can also inherit

let bar = proto : foo
    z = 5
end

io:print(bar:x) // 2
io:print(bar:y(3)) // 6
io:print(bar:z) // 5

You can clone a prototype with the clone keyword.

let foo = proto
    x = 1
end

let bar = foo
foo:x = 3
io:print(bar:x) // 3

let baz = clone foo
foo:x = 5
io:print(foo:x) // 5
io:print(bar:x) // 5
io:print(baz:x) // 3

Exceptions

To run a block of code and catch any exceptions, use the test keyword. The catch code goes in the error block.

test
    let foo = 1 / 0
error(e) // you can use '_' to ignore the exception
    io:print("woah! I caught an exception!")
end

Loops

For loops have two different forms. The first one is the traditional for loop.

for let i = 0 | i < 10 | i += 1
    io:print(i)
end

The second one is the iterator for loop. This is a more modern form of for loop.

for i in range(0, 10)
    io:print(i)
end

There is also the while loop.

while true
    io:print("woah! I'm in a loop!")
end

You can also use the break keyword to break out of a loop.

while true
    io:print("woah! I'm in a loop!")
    break
end

You can also use the continue keyword to skip the rest of the loop.

while true
    if true
        continue
    end
    io:print("woah! I'm in a loop!") // this will never print
end

List Comprehensions

List comprehensions are syntatic sugar for mapping a function over a list.

let foo = [1, 2, 3, 4, 5]
let bar = {fn(i) => i * 2 of foo} // [2, 4, 6, 8, 10]

Operators

Here’s a rundown of all of Oasis’s operators.

Arithmetic

1 + 2 // addition: 3
1 - 2 // subtraction: -1
1 * 2 // multiplication: 2
1 / 2 // division: 0.5
1 % 2 // modulus: 1

Directional evaluation

These are the directional evaluation operators. They are used to evaluate expressions in a specific direction. They are always evaluated left-to-right, but depending on the direction of the arrow, it will return the first or last expression. These are identical to the comma operator in C, but with direction.

1 |> 2 |> 3 // right evaluation: 3
1 <| 2 <| 3 // left evaluation: 1

Comparison

1 == 2 // equality: false
1 != 2 // inequality: true
1 < 2 // less than: true
1 > 2 // greater than: false
1 <= 2 // less than or equal to: true
1 >= 2 // greater than or equal to: false

true and true // logical and: true
true or false // logical or: true
not true // logical not: false

null ? 1 // null coalescing: 1