Guide

Syntax

JS Bizubee
a && b a and b
a || b a or b
!(a && b) not a and b
!a || !b !a or !b
a < b && b < c a < b < c
a === b a == b
a == b no equivalent
a == null a?
(a == undefined)? undefined : a.b.c a?.b.c
(func == undefined)? undefined : func() func?()
Math.pow(a, b) a ^ b
Math.floor(a / b) a // b
return a return a, |< a or a >|
yield a yield a, << a or a >>
yield* a yield * a, << * a or a * >>
this.a @a or this.a

Line Breaks and Indentation

A line break in bizubee terminates a statement unless it ends in a strictly binary operator.

Bizubee also has optional indented blocks, such that


if 2 < 3 do
    doSomething()
else
    doSomethingElse()

Can also be written as


if 2 < 3 {
    doSomething()
} else {
    doSomethingElse()
}

Keep in mind that indentation is ignored within curly blocks.

Variables

All variables in Bizubee are block-scoped, as opposed to function scoped. Use var to declare variables, and const to declare constants.


if 2 < 6 do
    var a = 7
    const b = 6
    # a and b are defined here


# a and b are undefined out here

For-loops

For-In

For-in loops are used to loop over a collection of values, and compile directly to for-of loops in JavaScript. Any object that properly implements the Iterable interface can be iterated over with a for-in loop.


# Arrays are iterables!
const values = [3, 5, 6]

for value in values do
    console.log("value is ${i}")

# prints: 
# 3
# 5
# 6

For-On Loops

For-on loops iterate over an Async Iterable, and can only exist within async functions. There is currently no JS analog to the for-on loop, but there may be one soon. For now Bizubee has adopted the interface described in the proposal. The only difference is Symbol.asyncIterator is replaced by symbols.asyncIterator from the bizubee utils library.


import {symbols} from bizubee utils

const myAsyncIterable = {
    i       : 0
    values  : []

    next() -> {
        @i += 1
        return Promise.resolve({
            done: (@i == @values.length)
            value: @values[@i - 1]
        })
    }
    [symbols.asyncIterable]: () -> {
        return this
    }
}

# function must be async
myAsyncFunction() -> ~ {
    for value on myAsyncIterable do
        console.log(value)
}

myAsyncFunction()

# asynchronously prints:
# 7
# 4
# 9

Functions

Bizubee has function expressions and function declarations. Similar to JS, function declarations are hoisted to the top of their scope.



# divide is already defined
console.log(divide(6, 2))

# declare function divide
divide(numerator, denominator) -> {
    |< numerator / denominator
}

Whereas


# divide is not defined, so throws error
console.log(divide(6, 2))

const divide = (numerator, denominator) -> {
    |< numerator / denominator
}

# divide is defined here
console.log(divide(6, 2))

Unlike JS, function declarations are constant, and cannot be reset.

Function Modifiers

In has generator functions, async functions, and async-generator functions. Each of the three function types requires a specific modifier.

Generator Functions

Generator functions are defined by a * modifier after the function arrow. Generator functions in Bizubee are compile directly to their JS counterparts. See JS generator functions.


range(start, end, step = 1) -> * {
    var i = start
    while i < end do
        yield i
        i += step
}

for i in range(0, 3) do
    console.log(i)

# prints:
# 0
# 1
# 2

Async Functions

Async functions are defined by adding a ~ after the function arrow. Async functions can contain await expressions, which take a Promise/A+ argument. When an await is encountered, function execution is paused until the promise is resolved. Once the promise is resolved, the await expression evaluates to the resolution value, and function execution continues.


# ordinary function that returns a promise
httpGet(uri) -> {
    const base = "Failed with status"

    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()

        xhr.onreadystatechange = () => {
            if xhr.readyState == 4 do
                if xhr.status == 200 do
                    # everything went great
                    resolve(xhr.responseText)
                else
                    # oops
                    reject(new Error("${base} ${xhr.status}"))
        }

        xhr.open('GET', uri, true)
        xhr.send()
    })
}

# async function that returns a promise
fetchJSON(uri) -> ~ {
    const text = await httpGet(uri) # await promise
    return JSON.parse(text)
}

fetchJSON(uri).then((json) => {
    doSomethingWithJSON(json)
})

Async Generator Functions

Async generator functions are functions that implicitly return Async Generators which can be iterated over asynchronously with for-on loops. The ~* or comet operator is used as the modifier for async generator functions. Async generator functions can contain both await expressions, and yield statements.

An async generator function to iterate over a stream of JSON objects.


# ordinary function that returns a promise
httpGet(uri) -> {
    const base = "Failed with status"

    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()

        xhr.onreadystatechange = () => {
            if xhr.readyState == 4 do
                if xhr.status == 200 do
                    # everything went great
                    resolve(xhr.responseText)
                else
                    # oops
                    reject(new Error("${base} ${xhr.status}"))
        }

        xhr.open('GET', uri, true)
        xhr.send()
    })
}

const urls = [
    'http://some.domain/path/to/file1.json'
    'http://some.domain/path/to/file2.json'
    'http://some.domain/path/to/file42.json'
]

getJSONStream() -> ~* {
    for url in urls do
        const text = await httpGet(url)
        yield JSON.parse(text)
}

useJSONStream() -> ~ {
    for json on getJSONStream() do
        doSomethingWithJSON(json)
}

# start running async function
useJSONStream()

Fat Arrow vs Thin Arrow

Fat arrow functions are defined using => instead of ->. They are mostly analogous to fat-arrow functions in JavaScript, in that they do not have their own this value. Instead, this keeps the value of the parent scope in a fat-arrow function.


const myObj = {
    val: 55
    myMethod() -> {
        myFatArrowFunction() => {
            # this == myObj!
            return this.val
        }

        return 3 + myFatArrowFunction()
    }
}

# prints 58
console.log(myObj.myMethod())