Semicolons and parentheses

You don't have to use semicolons in MotorScript. It's quite simple, you can't even use semicolons. This is a design choice that's been made to keep everything a simple as possible. The same goes for parentheses. You don't have to use those if it's also clear without them.

General structure

Statements and code blocks

Each script is basically a list of statements. You can read more about which statements are available in the section below. A statement may require a 'block' after the statement declaration. For example, an if statements requires a code block to be followed after you declare the condition. A block is again, basically a list of statements. If a block only consists of a single statement, you don't have to put braces around it.

Grammatically, a block is defined as follows:

block
    : LAcc ( statement )* RAcc
    | statement
    ;

Scopes

All statements belong to a certain scope. A scope (also known as 'closure' or 'environment') makes all statements that are direct children of it bound to the scope of the child or parent scopes. So, statements are scope-bound. Consequently, code in a scope can reach declarations in the current scope or in any parent scope, but not in any descending scope. This concept is crucial when you want to declare a variable with a certain name more than once, but in different code blocks.

Example:

const text = "hello"
for i <- 1..10
{
    // Works, "text" can be accessed from this scope.
    const local_text = text
    const local_num = 6
}
// Does not work, "local_num" is not "in scope".
const num = local_num

1. Statements

1.1. Declaration

Assign the value of an expression to a variable or constant.

Variables can be defined in regular mode or in persistent mode. When a variable is declared persistent, it's value will be kept across multiple runs. The value that is provided at the right hand side of the equals sign is used as the initial value. So, for example, you declare a the variable myvar with an initial value of 2 and add one with myvar += 1. In all future runs, the value of myvar will be 3, even when no addition is performed.

Definition

declarationStatement
    : annotation? ( Const | Var ) Identifier Assign expression
    ;

Annotation parameters

mode

One of the following: persistent, shared.

Can be set multiple times.

Note: shared is only effective if persistent is also enabled.

Example

Regular declaration

var zero = 0
const text = "Hello"

Declaration in persistent mode

#[mode = "persistent"]
var persisted = 0
\tellraw(@a, "Persisted value: ", persisted)
persisted += 1

Persistent shared variable

First input (execute first):

#[mode = "persistent", mode = "shared"]
var persisted_and_shared = 5

Second input (execute after input 1):

#[mode = "persistent", mode = "shared"]
var persisted_and_shared = 0

\tellraw(@a, "Persisted and shared value: ", persisted_and_shared) 

If executed in proper order, the output will be Persisted and shared value: 5. Note that the initial assignment will only take place at the very first time the variable is accessed. As the shared mode is enabled, this may happen in either the first or the second input, depending on the order of execution.

1.2. Assignment

Assign the value of an expression to a reference.

Definition

assignStatement
    : reference assignment expression
    ;

assignment
    : Assign
    | AssignMultiply
    | AssignDivide
    | AssignModulus
    | AssignPlus
    | AssignMinus
    ;

Example

var counter = 0
counter  = 10
counter *= 4
counter /= 8
counter %= 3
counter += 5
counter -= 1

1.3. For loops

Execute a block of code based on the loop condition.

There are two types of for expressions currently supported:

  1. Infinite for loop;
  2. For-in loop, also known as a for-each loop.

Note that infinite for loops are not really useful in MotorScript because (at the moment) jump statements (like break, continue, return and goto) aren't supported.

Definition

forStatement
    : forInfinite
    | forIn
    ;

forInfinite
    : For block
    ;

forIn
    : For Identifier ( Comma Identifier )? InSgn expression block
    ;

Example

For infinite

for
{
    // code
}

For-in

for letter <- ["a", "b", "c"]
{
    // do something with letter
}

for i <- 0..10
{
    // iterates from 0 to 10 (inclusive)
}

1.4. If - else if - else

Execute a block of code if the given condition evaluates positively.

An if statement can be chained with unlimited else if checks and may be closed with a final else block.

Definition

ifStatement
    : ifStementFirstBlock ifStatementElseIfBlock* ifStatementElseBlock?
    ;

ifStatementFirstBlock
    : If expression block
    ;

ifStatementElseIfBlock
    : Else If expression block
    ;

ifStatementElseBlock
    : Else block
    ;

Example

var two = 2
if two / 2 == 1
{
    // OK
}
else if two == 3
{
    // hmmm
}
else
{
    // ...
}

1.5. Execute chains

Execute a code block in a certain execution scope.

Definition

executeStatement
    : executeChainPart+ block
    ;

executeChainPart
    : executeAs
    | executeAt
    | executeIn
    | executePositioned
    ;

executeAs : As selector ;
executeAt : At selector ;
executeIn : In resource ;
executePositioned
    : Positioned position
    | Positioned As selector
    ;

Example

General execution scopes

as @p
{
    // Executes this code as the nearest player.
}

at @p
{
    // Executes at the location of the nearest player.
    // Same position, rotation, and dimension.
}

in $the_end
{
    // Executes in the dimension "The End".
}

Chaining parts

as @e[limit=1] positioned `100 50 100`
{
    // Executes this code as a single entity,
    // with it's coordinates being 100, 50, 100 (x, y, z).
}

as @a in $overworld
{
    // Executes this code for each online player,
    // but it's dimension being "Overworld".
}

1.6. Call

Call a function.

Definition

callStatement
    : Call expression
    ;

Notes

  • This is an unsafe feature. Use it with care.
  • The provided expression must resolve to a resource.
  • No checks are performed to check whether the given resource is valid.
  • The function being called can be any ordinary Minecraft function.
  • Scopes are not preserved when calling functions. This means that you theoretically cant't do recursive calls. Also, you can quickly run into run-time issues when function 1 calls function 2 which calls function 1.

Example

#(unsafe = true)
as @a
{
    // Execute the function $:say_name as every online player
    call $:say_name
}

1.7. Raw

Append a raw command to the current scope's output file.

Definition

rawStatement
    : Raw expression ( Comma expression )*
    ;

Notes

  • This is an unsafe feature. Use it with care.
  • No checks are performed to check whether the given raw command is valid.
  • Multiple expressions can be concatenated using a comma (,).
  • All provided expressions must have the capability ToConstantString or implement the interface IProvidesMinecraftForm. This means that you can provide a lot of different constant values which are transformed to a string representation at the given position in the raw command.

Example

#(unsafe = true)
raw "say hello"
raw "tp ", @a, " ", `100 50 100`
raw "tellraw @a [\"Raw command!\"]"

1.8. Schedule

Execute a block of code after a given amount of ticks.

Definition

scheduleStatement
    : Schedule expression block
    ;

Example

schedule 20
{
    \tellraw(@a, "I'm executed after 20 ticks")
}

2. Expressions

An expression is something that represents a value. Basically, everything that's not a statement is an expression.

  • All expressions can be wrapped in parentheses if necessary.
  • All expressions can be negated by prepending an exclamation mark (!). Grammatically everything is negatable, but on compile not everything is of course.

2.1. Multiplication, division, modulo

Multiply/divide the left expression with the right expression or perform modulo on the left expression with the right expression.

Definition

expression
    : ...
    | expression expressionMulDivMod expression ;

expressionMulDivMod
    : Multiply
    | FSlash
    | Modulus
    ;

Example

var num = 5
var mul = num * 3 // mul = 15
val mod = num % 2 // mod = 1

2.2. Addition, subtraction

Add or subtract the right expression to/from the left expression.

Definition

expression
    : ...
    | expression expressionPlusMinus expression
    ;

expressionPlusMinus
    : Plus
    | Minus
    ;

Example

var num = 4
var add = num + 8 // add = 12
var sub = num - 9 // sub = -5

2.3. Equality comparison

Compare two expressions. The comparison behavior differs based on the given operator.

Often used in an if statement. This expression results in a boolean value.

Available operators:

  • <=: less than or equal to;
  • <: less than;
  • ==: equal to;
  • !=: not equal to;
  • >=: greater than or equal to;
  • >: greater than;
  • matches: left expression matches the right expression

Note: in case of the matches operator, the right expression must resolve to a range.

Definition

expression
    : ...
    | expression equalityOperator expression
    ;

equalityOperator
    : OpLessThanOrEqualTo
    | OpLessThan
    | OpEqualTo
    | OpNotEqualTo
    | OpGreaterThanOrEqualTo
    | OpGreaterThan
    | OpMatches
    ;

Example

var num = 9

if num < 7 // false
{
    ...
}

if num matches 5.. // true
{
    ...
}

2.4. Conditional combination (and/or)

Perform a conditional combination on two or more expressions.

Often used in an if statement. This expression results in a boolean value.

The and combination has a higher priority than the or combination. That means that a && b || c works the same as (a && b) || c.

Definition

expression
    : ...
    | expression ( conditionalAnd expression )+
    | expression ( conditionalOr expression )+
    ;

conditionalOr
    : OpOr
    ;

conditionalAnd
    : OpAnd
    ;

Example

var a = 5
var b = 7

if a == 5 || b == 5
{
    // This block is executed
    ...
}

if a == 5 && b == 5
{
    // This block is not executed
    ...
}

2.5. Reference

A reference is an expression that refers to a certain value. This can practically be anything. Invoking a function is a reference as well, because you're referring to a value that will be produced by an invocation.

Each reference has a base. This can be a variable or constant (which must be present in the current scope) or it can be a so-called 'internal reference'. An internal reference is a reference to a built-in value, like game commands.

To this reference base, you can append reference parts, like [0] to get the first item, or .hello to get the hello property, or ( <arguments separated by a comma > ) to perform an invocation.

If a reference supports named arguments, you can put <name> = <exprssion> as an argument. Let's say there is a function called test and it has three parameters, first, second and third. And all values need to be a string. So, invoking the function test is normally done by passing all arguments in order: \test("1st", "2nd", "3rd"). But, with named arguments the following invocation will be exactly the same: \test("1st", third = "3rd", second = "2nd"). Note: first you need to provide all unnamed arguments after which you may provide named arguments. That means that you can't do this: \test(second = "2nd", "first", "third").

Definition

reference
    : ( referenceVarOrConst | referenceInternal ) referencePart*
    ;

referenceVarOrConst
    : Identifier
    ;

referenceInternal
    : BSlash Identifier
    ;

referencePart
    : path referenceInvocation
    | path
    | referenceInvocation
    ;

referenceInvocation
    : LParen arguments? RParen
    ;

arguments
    : ( argumentUnnamed ( Comma argumentUnnamed )* )+ ( Comma argumentNamed )*
    | argumentNamed ( Comma argumentNamed )*
    ;

argumentUnnamed
    : expression
    ;

argumentNamed
    : Identifier Assign expression
    ;

Example

Referencing variables and constants

var a = 6
const b = "hello"

Referencing internal references

var a = \internal_reference.example_var_property
const b = \internal_reference.example_const_property

Finding by string or index

const data = {
    description: "Potato",
    amouts: [6, 4, 32]
}

// Get the "description" property of "data"
const descr = data.description
// Get the "amounts" property of "data" and find the 2nd element.
const last_amount = data.amounts[2]

Reference invocation

const str = "Hello"
const substr = str.substring(1).substring(2) // substr = "lo"
// Compiled to the in-game "say" command.
\say("Hello MotorScript!")
// If supported by the reference, you can shuffle argument positions by adding named arguments.
\example_internal("text", 1337)
\example_internal(arg1 = 1337, arg0 = "text")

2.6. Selector

A selector is an expression that represents a filtered collection of in-game entities. All selectors start with the at symbol (@) followed by one of the letters e, r, p, s or a. If the at symbol is followed by something that is not in this list, it is assumed to be @e[name=<value followed by the @ sign>].

(TODO: Explain the default selectors)

All selectors can have optional properties. All selector properties are validated on compile-time. (TODO: List available properties)

Definition

selector
    : ( selectorDefault | selectorEntityName ) properties?
    ;

selectorDefault : SelectorDefault ;
selectorEntityName : AtSgn Identifier ;

scoresMap
    : LAcc scoresMapKeyValue? ( Comma scoresMapKeyValue )* RAcc
    ;

scoresMapKeyValue
    : Identifier '=' range
    ;

Example

const all_entities = @e
const all_rabbits = @e[type = $rabbit]
const five_nearest_entities = @e[sort = "nearest", limit = 5]
const sender = @s
const random_player_with_score = @r[scores = {objective_name = 70..}]

2.7. Literal value

A literal value represents a fixed value that is expressed in source code.

Literal values are categorized in the following way:

  • String: any text.
  • Boolean: true or false.
  • Numeric: numeric values, split in two subcategories:
    • Decimal: numeric values with a decimal point, which are:
      • Double: double value.
      • Float: float value.
    • Whole: whole numeric values (integers), which are:
      • Long: long value (64 bits).
      • Integer: integer value (32 bits).
      • Short: short value (16 bits).
      • Byte: byte value (8 bits).

By default, a decimal numeric value is a double and a whole numeric value is an integer. You can append a certain suffix to indicate that the numeric value is of a different type than the default one:

Type Suffix
Double d, D
Float f, F
Long l, L
Integer -
Short s, S
Byte b, B

Note: all numeric values are signed. There is no support for unsigned value.

Definition

literalValue
    : literalString
    | literalBoolean
    | literalNumeric
    ;

literalNumeric
    : literalDecimal
    | literalWhole
    ;

literalDecimal
    : literalDouble
    | literalFloat
    ;

literalWhole
    : literalLong
    | literalInteger
    | literalShort
    | literalByte
    ;

literalString : String ;
literalBoolean : Boolean ;
literalDouble : Double ;
literalFloat : Float ;
literalLong : Long ;
literalInteger : Integer ;
literalShort : Short ;
literalByte : Byte ;

Example

const string = "Hello, this is the string content. You can escape special characters by using a backslash."
const multi_line = "Hello,\nI'm on the second line."

// Raw strings are indicated with apostrophes.
// These kinds of strings allow any character to be put in, even quotation marks.
// You can't use any escape sequences in these kinds of strings (except \' to use the apostrophe literally).
const raw_string = '"Hello World," James said.'

// Leading zeroes are allowed to be ommitted
const double = .234 // In case of a decimal number, you don't have to add the leading zero before the decimal point.
const float = 345.5f
const long = 2349234234234L
const int = 1343453
const short = 65000s
const byte = 127b

2.8. Range

A range indicates a range of values numeric values.

There are four different range types: 1. Exact: the range is only valid for one certain value; 2. FromTo: the range is valid from value a to value b (inclusive); 3. OrBelow: the range is valid for all values equal to or below the given value; 4. OrAbove: the range is valid for all values equal to or above the given value.

Definition

range
    : rangeExact
    | rangeFromTo
    | rangeOrBelow
    | rangeOrAbove
    ;

rangeExact : rangePart ;
rangeFromTo : rangePart DotDot rangePart ;
rangeOrBelow : DotDot rangePart ;
rangeOrAbove : rangePart DotDot ;

rangePart
    : literalNumeric
    | reference
    ;

Example

const range = 5.. // 5 or above

// Technically the same as "if 8 >= 5"
if 8 matches range
{
    // This block will be executed
    ...
}

2.9. NBT

Named Binary Tag is the primary way to represent data structures. You can find more technical details about the NBT format on Minecraft Coalition Wiki.

Definition

nbt
    : nbtCompound
    | nbtArray
    | nbtList
    | reference
    | literalValue
    ;

nbtCompound
    : LAcc nbtCompoundKeyValue* RAcc
    ;

nbtCompoundKeyValue
    : nbtCompoundKey Colon nbtCompoundValue Comma?
    ;
nbtCompoundKey
    : Identifier
    | String
    ;
nbtCompoundValue
    : nbt
    ;

nbtArray
    : LBrack nbtArrayType SColon ( nbtArrayValue ( Comma nbtArrayValue )* )? RBrack
    ;
nbtArrayType
    : 'B' // Byte array
    | 'I' // Int array
    | 'L' // Long array
    ;
nbtArrayValue
    : literalByte
    | literalInteger
    | literalLong
    ;

nbtList
    : LBrack ( nbtListValue ( Comma nbtListValue )* )? RBrack
    ;
nbtListValue
    : nbt
    ;

Example

General-purpose data structures

const my_data = {
    key: "value",
    arrays: {
        ints: [I; 1, 2, 3, 4],
        longs: [L; 7, 8, 9]
    },
    "Spaces can be used": "if you use the string notation for the compound key" 
}
// Values can be retrieved using a reference

Passing data tags

// Summons a small armor stand without base plate at the relative position ~ ~ ~.
\summon($armor_stand, `~ ~ ~`, {
    Small: true,
    NoBasePlate: true
})

2.10. Position

A position is a three-item tuple containing values for x, y and z axes respectively.

The coordinates may be relative. To indicate a component of the position tuple to be relative, you must prefix a tilde (~) or a caret (^), depending on the behavior you want to accomplish.

The tilde notation is used as an offset for the x, y or z value. The caret notation is slightly different. If the caret notation is used on the x component, the value is used for a relative coordinate translated into the left direction. If used on the y component, it represents an upwards offset. If used on the z component, it represents a forward offset.

Note: the values of x, y and z are transformed to a double value on compile time.

Note: the caret notation is depending on how the targeted entity is positioned and in which direction it is facing.

Definition

position
    : Btick positionPart positionPart positionPart Btick
    ;

positionPart
    : literalNumeric
    | positionOffsetTilde
    | positionOffsetCaret
    ;

positionOffsetTilde
    : Tilde literalNumeric?
    ;

positionOffsetCaret
    : Caret literalNumeric?
    ;

Example

at `0 0 0`
{
    // This code is executed at the zero point of the current dimension.
    ...
}
// Teleport all players five blocks up and ten blocks forward.
\teleport(@a, `^ ^5 ^10`)

2.11. Resource

A resource is identified by a namespace and a path. There are no grammatical rules for what a certain resource indicates. A resource may have a set of properties attached as well as a NBT compound value as payload.

Note: at this point in time, if a resource is detected to be a valid block identifier, it's properties are validated on compile time.

A resource is identified by a dollar sign, followed by an identifier. Optionally, you can specify a certain namespace to use. If no namespace is used, it is assumed to be "minecraft". The namespace is identified by prefixing the resource identifier with the desired namespace followed by a colon. If you only prefix the resource identifier with a colon, the namespace of the current build is used. This is especially useful when referring to resources within the current project. Please check out the examples below.

Definition

resource
    : Dollar ( ( Identifier ( Dot Identifier )* )? Colon )? resourceName properties? nbtCompound?
    ;

resourceName
    : Identifier ( FSlash Identifier )*
    ;

Example

Expressing resources in the current namespace

#(unsafe = true)
call $:other_function

Expressing a block resource with validated properties

// Place a repeater with 3 ticks of delay, facing west, at 10 50 10 (x, y, z).
\setblock(`10 50 10`, $repeater[delay = 3, facing = "west"])
// Give all players a repeater that, when placed, will be set to 3 ticks of delay, always facing west.
\give(@a, $repeater{
    BlockStateTag: {
        delay: 3,
        facing: "west"
    }
})
// Check if the block at `1 2 3` is a jukebox with a record in it.
if `1 2 3` == $jukebox[has_record = true]
{
    // This code block may get executed
    ...
}

3. Compiler instructions

A compiler instruction is a piece of code that is meant to enable special features in the compiler. All compiler instructions must be declared at the top of input before any statement.

Compiler instructions are grammatically defined as follows:

compilerInstruction
    : Hash LParen Identifier Assign expression RParen
    ;

3.1. Tags

Add the generated function to a certain tag.

Please refer to the Minecraft wiki for more information about tags.

Definition

Identifier: tags.

Accepted values:

  • resource

Example

// Add this function to the tag $tick (same as $minecraft:tick).
// This is a built-in tag which makes this function execute every tick. 
#(tags = $tick)

3.2. Unsafe

Enable or disable unsafe mode.

Definition

Identifier: unsafe.

Accepted values: boolean.

Default: disabled (false).

Example

#(unsafe = true)
// Now the following statement compiles:
call $:other_function

4. Annotations

Some statements or expressions accept options via the an annotation. Annotation values are documented if you can use them.

Annotations are grammatically defined as follows:

annotation
    : Hash properties
    ;