Skip to content
Simon816 edited this page May 17, 2020 · 10 revisions

Command Block Language (CBL)

CBL is a high level programming language with similarities to C++. It has special syntax to make common tasks easier to write.

CBL is designed so the end user never has to write commands directly, the compiler is smart enough to figure out how to take an expression and generate a sequence of commands to implement it.

Syntax

Statements

If statement

Grammar:

"if" "(" expression ")" statement ["else" statement]

Example:

if (some_condition) {
    // Condition is true
} else {
    // Condition is false
}

some_condition must reduce to a numerical variable in Command IR. A non-zero value executes the true block, otherwise a zero value will result in execution of the false block.

While statement

Grammar:

"while" "(" expression ")" statement

Example:

while (condition_holds()) {
    // Loop body
}

Evaluates the condition expression using the same semantic as the if statement.

The while statement body is executed in a loop until the condition returns a zero value. The condition is evaluated every loop iteration before the body is executed.

Do while statement

Grammar:

"do" statement "while" "(" expression ")" ";"

The same as the while statement except the condition is evaluated at the end of the body (i.e. at least one execution of the body occurs).

For statement

Grammar:

"for" "(" expression? ";" expression? ";" expression? ")" statement

Example:

for (i = 0; i < 10; i++) {
    // Loop body
}

Similar to the while loop, but allows an optional initialization and post-body expression.

The first expression in the triplet is evaluated once only, before anything else. The second expression is the condition, it is evaluated every loop iteration and must return a non-zero value for the loop to continue. The last expression is evaluated after the loop body is evaluated.

Any expression in the triplet can be omitted. An omission of the condition will cause an infinite loop unless a break statement exists in the loop body.

For in statement

Grammar:

"for" "(" IDENT "in" expression ")" statement

Example:

for (entity in Game.entities) {
    // Loop body
}

Iterates over an iterable expression. Currently the only allowable iterable are instances of EntityCollection. In this case, each entity in the collection is set to the entity identifier in turn. The loop body executes with the ability to reference the current entity.

This example generates an /execute command: execute as @e run {loop body}

At statement

Grammar:

"at" "(" expression ")" statement

Example:

at (sender.pos + (0.0, 1.0, 0.0)) {
    // Body
}

Evaluates the body of the statement "at" the given entity position. The notion of being "at" a position is a feature of Minecraft's commands which determines the in-game position in which relative coordinates (among other things) are relative to. The position is implicitly available during execution.

The expression must evaluate to a value of type Entity or RuntimeEntityPos. RuntimeEntityPos is an implicit type that exists when the .pos attribute of an entity is referenced.

Internally, this uses the /execute, either execute at or execute positioned.

IR Statement

Grammar:

"ir" "(" [expression ("," expression)*] ")" IR_BLOCK

Example:

ir(foo, bar) {
    insn $arg0, $arg1
}

Embeds the Command IR instructions directly into the output. Expressions passed in through parameters are converted to variables $arg0..$argN. Only types that can be converted to IR types can be passed in.

Block statement

Grammar:

"{" statement* "}"

Example:

{
    int x = 10;
    foo();
}

A block statement is a list of zero or more statements. The statements are evaluated in sequence. Within a block statement a new scope is created: Variables defined inside the block are inaccessible from outside the statement. Accessible definitions before the block statement remain accessible inside the block.

Continue and Break statements

Grammar:

"continue" ";" -> continue_statement
"break" ";" -> break_statement

These statements are only valid within a surrounding loop.

continue will cause a jump in execution to the end of the loop body.

break will exit the loop entirely.

Return statement

Grammar:

"return" expression? ";"

Exits the current function. If the function's return type is void then the expression must be omitted. Otherwise the result of the expression must have the same type as the function's return type.

Expression statement

Grammar:

expression? ";"

Evaluates the expression.

Expressions

CBL expressions are very similar (if not identical) to many C-like languages. Refer to the C++ expression reference for operators and precedence.

Listed below are expressions specific to CBL.

Filter expression

Grammar:

"filter" "(" IDENT "in" expression ")" "{" (expression ";")* "}"

Example:

filter (e in Game.entities) {
    e.type == Entities.armor_stand;
    e.has_tag("MyTag");
};

filter will apply a list of conditions that must be true to each item in the input expression. Currently it only handles the EntityCollection type.

Each entity in the collection is tested to match all of the filter conditions (conditions are ANDed together), the filter expression returns another EntityCollection object with the filters applied.

Important note: Filter expressions are only evaluated when something requests an item from the result, (e.g. with the for .. in statement).

Filter expressions can be combined to create a compound filter:

EntityCollection filter1 = filter(e in Game.entities) {
    e.type == Entities.armor_stand;
}
EntityCollection filter2 = filter(e in filter1) {
    e.has_tag("MyTag");
}

filter2 is an equivalent filter to the example above.

Filters are designed to compile into a selector, the above example compiles into the command: execute as @e[type=minecraft:armor_stand,tag=MyTag] run

Await expression

Grammar:

"await" unary_expression

Example:

async void my_async() {
    await another_async();
}

Part of the async/await pattern. To call an async function, await must be prefixed before a function call expression. Await is only allowed if the current function is async.

Await will cause the current execution to pause until the function being awaited triggers a resume. It may seem as though a "pause" would inhibit the execution of the function being awaited, but it works with the means of a synchronization primitive: the game tick (Game.tick()). Although not literally, imagine the code is executed in a virtual machine driven by the game tick: each tick an arbitrary amount of code is executed. If the code triggers the synchronization primitive then the virtual machine pauses. The rest of Minecraft's engine continues until the next tick, once the next tick occurs the VM is woken up to resume where it left off.

Vector expression

Grammar:

"(" expression ("," expression)+ ")"

Example:

(10.5, 20.0, -3.5)

A convenience expression to construct a vector with a fixed length with each component holding the given values.

Currently only vec3d is supported, i.e a 3-dimension vector of decimals.

Declarations

Variable declaration

Grammar:

(1) type_name IDENT
(2) type_name IDENT "[" INT_CONSTANT "]"

Example:

int x;

Declares a variable of the given type with the given name. (1) declares a single variable, (2) creates an array of the variable with a fixed length.

Currently the only array type supported is int.

Global and local variable declarations can have an optional initializer:

int globalVal = 1;

void main() {
    int x = 10; // Assignment initializer
    mytype t(1, 2, 3); // Constructor initializer
}

Function declaration

Grammar:

func_declaration: async_func_decl
async_func_decl: ASYNC? inline_func_decl
inline_func_decl: INLINE? func_declaration_
func_declaration_: type_name (type_name "::")? (IDENT | "operator" OPERATORS) "(" param_list ")"

function_definition: event_handler? func_declaration block_statement

Example:

inline void my_type::foo(int param) {
}

A function declaration states that a function with the given signature exists but does not define (give the implementation of) it.

A function definition declares the function and also provides the implementation.

Function declarations can also be an operator overload, the same as in C++.

Constructor declaration

Grammar:

ctor_declaration: "constructor" "(" param_list ")"

ctor_definition: ctor_declaration block_statement

Example:

constructor(int param) {
    this.foo = param;
}

Semantically the same as a function definition but with a return type of void.

Event handlers

Grammar:

event_handler: "[" "Event" conditional_expression [":" "{" (event_condition ("," event_condition)*)? "}"] "]"
event_condition: IDENT ("." IDENT)* ":" conditional_expression

Example:

[Event Events.placed_block : {event.item.item: "minecraft:stone"} ]
void on_placed_stone() {
}

This example adds an event handler onto the on_placed_stone function. The function will be executed whenever a player places a stone block.

See Event Handlers in the Command IR documentation for how it's implemented.

Type declaration

Example:

type MyType {
    int prop1;
    decimal prop2;
    constructor(int p1, decimal p2) {
        this.prop1 = p1;
        this.prop2 = p2;
    }
    int operator +(int i) {
        return i + this.prop1;
    }
}

Custom types can be defined using the type declaration.

Variable declarations must appear before any function declarations, and cannot appear after the first function is declared. (This is because the exact structure in memory must be known for the this parameter).

Every function attached to a custom type has an implicit this parameter. The this parameter is automatically inserted into the definition and into any invocations of the function.

The this parameter is an instance of the type, all variable members have their current values.

Operator overloading enables type instances to be used in expressions involving operators, as long as an overload exists.

Singleton declaration

singleton SI {
    int foo;
    void bar() {
    }
}

A singleton is similar to a type except only one instance exists.

Clone this wiki locally