4. Expressions
Expressions are pieces of code that compute values. They combine variables, literals, operators, and function calls to produce a result. Expressions are the foundation of all computation in MSC, from simple arithmetic to complex logic.
4.1. What is an Expression?
An expression is anything that evaluates to a value. The simplest expressions are just literals or variables:
@player {{5}}
@player {{"Hello"}}
@player {{mymap::score}}
5
Hello
10
More complex expressions combine values with operators:
@player {{5 + 5}}
@player {{mymap::score * 2}}
@player {{mymap::score > 8 && true}}
10
20
true
Expressions can also include function or method calls (see Functions and Types for details):
@player {{math::sqrt(16.0D)}}
@player {{"hello".toUpperCase()}}
4.0
HELLO
4.2. The {{ }} Syntax
Expressions in MSC are written inside double curly braces: {{ }}. This syntax can be used in two places. Firstly, in script operators like @player, and @bypass, expressions are evaluated and their results are inserted into the text:
@define Int x = 5
@player The value of x is {{x}}
@player Double that is {{x * 2}}
@bypass /teleport @s {{x * 5}} 64 {{x + 10}}
The value of x is 5
Double that is 10
The last line becomes /teleport @s 25 64 15 after the expressions are evaluated, and this is where the player will be teleported.
Secondly, expressions can be embedded inside string literals:
@define String message = "The answer is {{5 + 5}}"
@player {{message}}
The answer is 10
This is useful for building complex strings that include computed values.
4.3. Operators
Operators combine values to produce new values. MSC supports arithmetic, comparison, logical, and assignment operators.
Technical Detail
If you have a background in computer science or mathematics, you will find these operators familiar. You can skip to the next section to see a list of all operators in order of precedence.
4.3.1. Arithmetic Operators
Arithmetic operators work on numbers:
@player {{10 + 3}}, {{10 - 3}}, {{10 * 3}}
@player {{10 / 3}}, {{10 % 3}}
@player {{2 ^ 8}}
13, 7, 30
3, 1
256.0
The ^ operator is exponentiation: 2 ^ 8 computes \(2^8 = 256\). This returns a Double, rather than an integer.
The % operator is modulo (which computes remainder after division). Division between two integers produces an integer result (truncated). To get a decimal result, use a Float or Double:
@player {{10 / 3}} (integer) vs {{10.0 / 3.0}} (precise)
3 (integer) vs 3.3333333 (precise)
The + operator can also be used to concatenate strings:
@player {{"Hello" + " " + "World"}}
@player {{"Score: " + 100}}
Hello World
Score: 100
When a string is added to a number, the number is converted to a string.
4.3.2. Comparison Operators
Comparison operators compare values and return a Boolean:
@player {{5 > 3}}
@player {{5 < 3}}
@player {{5 >= 5}}
@player {{5 <= 4}}
@player {{5 == 5}}
@player {{5 != 3}}
true
false
true
false
true
true
Note that == checks for equality of value, while != checks for inequality. Make sure to use double equals for comparison, as a single equals sign is used to set the value of a variable.
4.3.3. Logical Operators
Logical operators combine Boolean values:
@player {{true && true}}
@player {{true && false}}
@player {{true || false}}
@player {{false || false}}
@player {{!true}}
true
false
true
false
false
The && operator is logical AND (true if both sides are true). The || operator is logical OR (true if either side is true). The ! operator negates a Boolean.
4.3.4. Assignment Operators
Assignment operators are used with @var to modify variables:
@define Int x = 10
@var x += 5
@player {{x}}
@var x -= 3
@player {{x}}
@var x *= 2
@player {{x}}
15
12
24
The operators +=, -=, *=, /=, and %= are shorthand. For example, x += 5 is equivalent to x = x + 5.
4.3.5. Incorrect Operators
Sometimes an operator is used with incompatible types. This causes an error:
@player {{Player("Ajdj123321") + true}}
[Scripts] Operator '+' is not applicable on types: Player, Boolean
This means that you can’t add a Player and a Boolean together (which makes sense: what would that even mean?). When in doubt, keep expressions simple and use parentheses to make the evaluation order explicit.
4.4. Operator Precedence
When an expression contains multiple operators, they are evaluated in a specific order. Operators with higher precedence are evaluated first. Operators with the same precedence are evaluated left to right. You can override precedence using parentheses.
You are probably familiar with standard arithmetic precedence from mathematics, where multiplication comes before addition. MSC follows similar rules, with some additions for logical and assignment operators.
From highest to lowest precedence:
Operator |
Description |
|---|---|
|
Parentheses (used to override the default precedence) |
|
Logical NOT (turning true to false and vice versa) |
|
Exponentiation (raising to a power) |
|
Multiplication, division, modulo |
|
Addition/concatenation, subtraction |
|
Comparison (less than, greater than, and their non-strict variants) |
|
Equality and inequality |
|
Logical AND (both sides must be true) |
|
Logical OR (at least one side must be true) |
|
Assignment (evaluated last) |
For example:
@player {{5 + 3 * 2}}
@player {{(5 + 3) * 2}}
11
16
In the first expression, 3 * 2 is evaluated first (giving 6), then 5 + 6 gives 11. In the second, the parentheses force 5 + 3 to be evaluated first.
A more complex example:
@player {{5 > 3 && 2 < 4 || false}}
This evaluates as ((5 > 3) && (2 < 4)) || false, which becomes (true && true) || false, which is true.
4.5. Short-Circuit Evaluation
Technical Detail
MSC uses short-circuit evaluation. With &&, if the left side is false, the right side is skipped (since false && anything is always false). With ||, if the left side is true, the right side is skipped (since true || anything is always true).
The logical operators && and || use short-circuit evaluation. This means they stop evaluating as soon as the result is determined.
For &&, if the left side is false, then the expression will be false regardless of the right side (as false && false and false && true are both false). Therefore, the compiler skips even checking what the right side is. This can have consequences if the right side has side effects (like function calls).
@define Boolean result = false && namespace::someExpensiveFunction()
The function is never called because false && anything is always false.
For ||, if the left side is true, the right side is never evaluated:
@define Boolean result = true || someExpensiveFunction()
The function is never called because true || anything is always true.
This behavior is especially useful for null checks:
@define Player target = Player("someone")
@if target != null && target.getHealth() > 0
@player Target is alive!
@fi
If target is null, the && short-circuits and target.getHealth() is never called. This prevents a NullPointerException. Without short-circuit evaluation, checking for null and using the variable would require separate @if statements (see Scripts for conditional operators).
4.6. Type Behavior
Operators behave differently depending on the types of their operands. Understanding this helps avoid unexpected results.
Arithmetic on integers produces integers:
@player {{7 / 2}}
3
To get a decimal result, at least one operand must be a Float or Double:
@player {{7.0 / 2}}
@player {{7 / 2.0D}}
3.5
3.5
String concatenation with + converts non-strings automatically:
@player {{"Value: " + 42}}
@player {{"Is active: " + true}}
Value: 42
Is active: true
However, the order matters. Expressions are evaluated left to right:
@player {{1 + 2 + " apples"}}
@player {{"apples: " + 1 + 2}}
3 apples
apples: 12
In the first example, 1 + 2 is evaluated first (giving 3), then 3 + " apples" produces the string. In the second, "apples: " + 1 produces "apples: 1", then adding 2 gives "apples: 12".
Not all types support all operators. Attempting an unsupported operation causes an error:
@player {{Block(0, 0, 0, "world") + 5}}
Operator '+' is not applicable on types: Block, Int
When in doubt, keep expressions simple and use parentheses to make the evaluation order explicit.