Precedence in ARMA 3 SQF

One of the oddities of ARMA 3 scripting is the lack of any discussion or documentation on precedence. Although the basic mathematical operators such as + and / follow the regular rules—the ones you learn in school —those rules doesn't explain what code such as the following will return:

1 min 3 * 2

Hopefully, however, this table will:

Lowest Precedence1|| or
2&& and
3== != > < >= <= >>
4All other binary operators.
5else
6+ - max min
7* / % mod atan2
8^
Highest Precedence9All unary operators.

Unary operators—functions such as getPos that take a single argument on their right hand side—and binary operators—those that take an argument on either side like setPos—are the fundamental building blocks of the SQF language and make up practically the entire language, including parts like if statements and for loops.

When ARMA evaluates code, it calculates the higher precedence operators first. If two adjacent operators have the same precedence, or are the same operator, it calculates the result from left to right—which also matches the conventional mathematical rules:

ExpressionResult
1 - 1 - 1-1
(1 - 1) - 1-1
1 - (1 - 1)1
[1, 0, 0] vectorDiff [1, 0, 0] vectorAdd [1, 0, 0][1, 0, 0]
[1, 0, 0] vectorDiff ([1, 0, 0] vectorAdd [1, 0, 0])[-1, 0, 0]

Learning and applying the detailed precedence rules of any language isn't always a good idea. Often code is easier to read when extra parenthesis are used, even if you do know the rules by heart (and more so for anyone else that hasn't studied them). With that in mind, there are a few key rules worth remembering:

One final point is that precedence issues don't always just produce the wrong answer, but they also frequently cause script compile errors. For example, the following will always cause an error:

str true || false

The reason for the error is that ARMA evaluates str true first, because str—like all unary operators— has a higher precedence than the binary operator ||. This first calculation step results in:

"true" || false

Which then throws an error, because the || operator only accepts boolean arguments.

Extra Credit

One entry in the precedence table that sticks out is else, which has a higher precedence than other binary functions—and in particular, then. Consider a typical if expression:

if (_condition) then { 1 } else { 2 }

The operators involved in this expression are:

if BooleanIfType
IfType then (Code | ElseType)Any
Code else CodeElseType

Without the higher precedence of else, evaluating from left to right, the above expression would be equivalent to:

((if _condition) then { 1 }) else { 2 }

Assuming for now that _condition is true, the above would result in 1 being passed as the left hand side of the else, which isn't permitted.

What actually happens is that else is given a higher precedence than the then operator. The two code blocks are passed in and are returned as an ElseType object, which the then operator can then selects from once the condition is evaluated.