-
Notifications
You must be signed in to change notification settings - Fork 29
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.
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.
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.
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).
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.
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}
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
.
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.
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.
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.
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.
Grammar:
expression? ";"
Evaluates the expression.
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.
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
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.
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 decimal
s.
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
}
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++.
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
.
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.
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 SI {
int foo;
void bar() {
}
}
A singleton is similar to a type
except only one instance exists.