TreeBurst brand image

TreeBurst


TreeBurst is a small, dynamically typed, embeddable programming language.

Features:

[header, ...contentItems, footer]
.filter(\?.size < 10)
.map(\(item) Button.new({
label: item.name
callback: \item.value += 10
}))
|> Document.append(...?)

Guide

Basic Syntax

Define a variable using the $ operator.

$val = 5

Reference a variable just using its name.

val

Comments can be used to annotate code.

// Single line commend
/* Multiline
comment
*/

The language supports the following primitive values, that can be used by a literal.

null // Specifies an empty value
void // Specifies a missing value, cannot be added to data-structures

// Number literals
5, 5.25, -28

// Boolean literals
true, false

There are multiple types of string literals. The only difference is the type of quote. You only need to escape the quote used to surround the string, you can choose a quote that is not contained in your string to avoid the need for escape sequences.

'String'
"Hello\nworld\x00"
`No "escape" needed`

There is also a template string literal, which can be used to create a string from other values. This is achieved by prefixing any string literal with $ and values are included by surrounding them with ${}.

$"The sum of ${value1} and ${value2} is ${value1 + value2}"

Expression separation

Expression are separated by the , token. However, in many cases this is not required. Expression are considered separate unless there is a syntax ambiguity. Generally, it is recommended to always use , in function calls and when there are multiple expression on the same line. Otherwise expression should be separated by newlines.

{a: 1 b: 2 c: 3} // Separation is not required but recommended
{a: 1, b: 2, c: 3} // Correct syntax

{
a: 1
b: 2
c: 3 // Separation by newline
}

The following example demonstrates a syntax ambiguity between grouping and function invocation. The parser will understand it as an invocation, which was probably not intended. Also included are

// Incorrect syntax
(1 + 2) (3 + 4)
// Separation by `,`
(1 + 2), (3 + 4)
// Separation by newline
(1 + 2)
(3 + 4)

Binary operators can bind operands over multiple lines, therefore newlines are not always sufficient for separation. The following example demonstrates an ambiguity between a binary subtraction and unary negation. The parser will understand it as a subtraction.

// Incorrect syntax
value
-2
// Explicit separation
value,
-2
// If subtraction was indented, indicate such by a space
value
- 2

Function invocations and indexing does not bind over multiple lines. There is no ambiguity in the following examples.

// Invocation
a(5)
// Two separate expression
a
(5)
// Indexing
array[10]
// Two separate expression
array
[10]

Functions

Later references to variables do not use $, only their name. Variables are accessible inside of the declaring function or all function declared within.

To create a function, use the \ operator. A function can specify explicit parameters and/or can act as a partial application of another operation. Partial application is performed using the placeholder ?. Each placeholder creates an implicit parameter and is replaced by reference to that parameter.

// Function that returns a constant value
\51

// Two parameter function
\(a, b) a + b

// Equivalent partial application
\? + ?

// If you want to reuse a parameter, placeholders cannot be used
\(a, b) a + b + a

// Function with a block allow for multiple operations.
// In this case placeholder cannot be used.
\(item) {
item.one()
item.two()
}

Functions implicitly return the result of the contained expression, the return function can be used to abort execution early with a return value.

\(item) {
item.name == null && return("Missing name")

return(item.name.length)
}

Function parameters can specify a default value. You can also create a function that takes a variable amount of parameters using a rest parameters, which consume all unbound arguments.

$func = \(a = 1, ...b, c = 2) ({a, b, c})

func() // `a` is `1`, `b` is [], `c` is `2`
func(3, 4) // `a` is `3`, `b` is [], `c` is `4`
func(3, 4, 5, 6) // `a` is `3`, `b` is [4, 5], `c` is `6`

The spread operator can be used to provide multiple arguments from one variable.

$func = \(a, b) a + b

$array = [1, 2]
func(...array)

Tables

A Table object is the simples composite type. It is an object that has multiple properties that can be accessed using the . operator. A table is created using the Table.new function.

Properties can be declared similarly to variables, using the $ operator. A property can only be declared once. Duplicate declaration or access to an undeclared property will generate an exception. As with any other composite type, a table cannot contain a void value.

$table = Table.new()
$table.property = 21

table.property = 21

The Table.new function can take a list or a map of properties to initialize the table. See the linked reference page for details.

A special type of function is a method. This is specified by naming the first parameter this. When this function is called by accessing it from a table, the table is inserted as the first argument.

$foo = Table.new()
$foo.method = \(this) this
foo.method() == foo

To access a property dynamically you can use the Table.getProperty function.

$foo = Table.new({ x: 5 })
Table.getProperty(foo, "x") // Returns: 5
Table.getProperty(foo, "y") // Returns: void

Array

An Array is a composite type that can store multiple value in order. Arrays start at 0 and have a limited length. They cannot be sparse. You can create an array using by surrounding elements between [ and ]. As with any other composite type, an array cannot contain a void value; therefore all void elements are ignored during creation.

[1, "value", void, true, null]
// Results in: [1, "value", true, null]

You can use the length property to get the length of the array.

[1, 2, 3].length // Returns: 3

You can use a spread operator to include multiple elements from one variable.

$x = [1, 2]
$y = [true, false]
$z = [...x, ...y, ...x]
// Results in: [1, 2, true, false, 1, 2]

Array elements are accessed using the [] operator (see Array.prototype.k_at). Indexing outside the range of the array generates an exception. You can use a negative index to index from the end of the array.

$x = [1, 2, 3]
x[0] // Returns: 1
x[-1] // Returns: 3
x[-2] // Returns: 2

You can use the Array.prototype.tryAt method to access elements that may be outside of the array range.

$x = [1, 2]
x.tryGet(0) // Returns: 1
x.tryGet(5) // Returns: void
x.tryGet(5) = 6 // The array is extended to fit the new element

For additional functions see the Array reference.

Map

A Map object is a composite type that allows the storage of values indexed by unique keys. Primitive types are compared by value but composite types are compared by reference.

You can create a map by surrounding the entries between { and }. Entries can be simple, indexed by a constant string key or computed, indexed by a value returned from an expression. You can also create an entry from a variable, that will have the key of the name of the variable. As with any other composite type, a map cannot contain a void value; therefore all void elements are ignored during creation.

$variable = 28

{ variable, simpleKey: "value", [getComplicatedKey() + 52]: "other" }

You can use the length property to get the entry count of the map.

{a, b, c}.length // Returns: 3

A map can be indexed using the [] operator (see Map.prototype.k_at).

$map = {}
map["a"] == void

map["a"] = 5 // Creates a new property
map["a"] == 5
map.length == 1

map["a"] = 6 // Modifies an existing property
map["a"] == 6

map["a"] = void // Deletes a property
map["a"] == void
map.length == 0

If you only use a constant string key, you can use the direct index operator -> for a simpler syntax.

map->a
// Equivalent to:
map["a"]

Macros

Macros are special functions prefixed by @ that execute during bytecode compilation. Instead of operating on values, they take expressions as arguments and emit code.

@while(true) \{
print("Forever")
}

[1, 2, 3].@foreach \print(?)

Bytecode compilation happens the first time a function is called. At this point, the function scope will be used to resolve the macro. If the macro is used as a method, the Table.prototype object will be used. If the macro is not found, the its execution will be deferred to the point, where the function execution first reaches the macro. This allows to use macros that are methods of custom classes. Next time the function executes, there will be no macro, only the emitted code.

Currently macros can only be created using native code. While you can define a macro from code, there is no API to work with expressions.

Reference

@constexpr

src/main/java/bt7s7k7/treeburst/runtime/Realm.java:785

Overloads:

Evaluates the value during compilation and returns it every time.


@if

src/main/java/bt7s7k7/treeburst/runtime/Realm.java:686

Overloads:

Selects expressions to execute based on predicates. Expected arguments follow a repeating pattern of condition + result, where the condition expression should return a Boolean or a value that can be converted to such. These pairs are evaluated in order, where if the result of the condition expression is true, the result expression is evaluated and returned. Otherwise the evaluation of the result is skipped and the next pair is evaluated. Optionally, a fallback expression may be added as the last argument, which will be evaluated and returned if no conditions return true.


@while

src/main/java/bt7s7k7/treeburst/runtime/Realm.java:741

Overloads:

Repeatedly evaluates the predicate expression, which is expected to return a Boolean or be convertible to such. If true is returned, the body expression is executed, otherwise the cycle is terminated.


Array

Allows the storage of an ordered list of elements, each indexed by a number starting from 0.


Boolean

Represents a truth value of either true or false.


Function

Represents a repeatedly executable subprogram, that may take input in the forms of arguments and may return a single value.


Map

Allows the storage of an unordered set of entries, indexed by a key, which may be any type of value.


Number

Represents a real number.


String

Represents a string of characters.


Table

Represents an object with properties.


false

src/main/java/bt7s7k7/treeburst/runtime/Realm.java:218

Type: Boolean

Constant value of false


goto

src/main/java/bt7s7k7/treeburst/runtime/Realm.java:777

Overloads:

Switches execution to a label with the specified name. This label must be in a block that is at the same level as this invocation or in a parent block that is still in the same function.


null

src/main/java/bt7s7k7/treeburst/runtime/Realm.java:219

Object representing an empty value


range

src/main/java/bt7s7k7/treeburst/runtime/Realm.java:658

Overloads:


return

src/main/java/bt7s7k7/treeburst/runtime/Realm.java:771

Overloads:

Aborts the execution of the current function, optionally retuning the provided value.


true

src/main/java/bt7s7k7/treeburst/runtime/Realm.java:217

Type: Boolean

Constant value of true


unreachable

src/main/java/bt7s7k7/treeburst/runtime/Realm.java:652

Overloads:

Specifies that this portion of the code should not be reachable in standard operation. If it is reached an exception is generated.


void

src/main/java/bt7s7k7/treeburst/runtime/Realm.java:220

Object representing a missing or non-existent value.