SQF Syntax: Difference between revisions

From Bohemia Interactive Community
Jump to navigation Jump to search
m (Added additional text to make allUnits nular example more clear)
m (Fix example)
 
(41 intermediate revisions by 5 users not shown)
Line 1: Line 1:
__TOC__
{{TOC|side}} {{Table/RVScripting}}
'''Status Quo Function''' is the successor of '''[[SQS_syntax|Status Quo Script]]''' which is deprecated since [[Armed Assault]].
'''SQF''' stands for '''S'''tatus '''Q'''uo '''F'''unction - a successor of [[SQS Syntax|'''S'''tatus '''Q'''uo '''S'''cript]], which is deprecated since [[:Category:ArmA: Armed Assault|{{arma1}}]] but could still be used in {{arma3}}.  
SQF was first introduced in [[Operation_Flashpoint:_Resistance_Introduction|Operation Flashpoint: Resistance]] together with the [[call]] operator in [[:Category:Introduced_with_Operation_Flashpoint:_Resistance_version_1.85|update 1.85]].
SQF was first introduced in [[{{ofpr}}]] together with the [[call]] operator in [[:Category:Introduced with Operation Flashpoint: Resistance version 1.85|update 1.85]].


An SQF expression has to be terminated via either {{Inline code|;}} or {{Inline code|,}}.
<small>
Example:
* "Status Quo" was a code name for {{Link|https://en.wikipedia.org/wiki/Operation_Flashpoint|Operation Flashpoint}}
_num = 10;
* "Combined Arms" was a code name for {{Link|https://en.wikipedia.org/wiki/ARMA_(series)|{{arma}}}}
_num = _num + 20; [[systemChat]] _num;
* "Futura" was a code name for {{Link|https://en.wikipedia.org/wiki/ARMA_3|{{arma3}}}}.
In the above example, there are three expressions.
</small>
# {{Inline code|_num {{=}} 10}}
# {{Inline code|_num {{=}} _num + 20}}
# {{Inline code|[[systemChat]] _num}}
All get separated, not via a new line but the {{Inline code|;}}.


SQF also allows [[Array|arrays]] and [[Block|codeblocks]] to have expressions inside of them.


An [[Array|array]] will always start with {{Inline code|[}} and ends with {{Inline code|]}}. The [[Array|array]] may contain another [[Data_Types|value type]] including other [[Array|arrays]].
The SQF Language is fairly simple in how it is built.
In fact: there are barely any actual language structures at all.
 
The functionality is provided via so called ''[[:Category:Arma 3: Scripting Commands|operators]]'' (or more commonly known [[:Category:Arma 3: Scripting Commands|scripting commands]]).
Those [[:Category:Scripting Commands Arma 3|operators]] are one of the following types: [[#Nular Operators|Nular]]{{sic}}, [[#Unary Operators|Unary]], or [[#Binary Operators|Binary]].
 
 
== Terminating an expression ==
 
An SQF expression has to be terminated via either <sqf inline>;</sqf> (preferred by convention!) or <sqf inline>,</sqf>.
 
<sqf>
_num = 10;
_num = _num + 20; systemChat str _num;
</sqf>
In the above example, there are three expressions:
# <sqf inline>_num = 10</sqf>
# <sqf inline>_num = _num + 20</sqf>
# <sqf inline>systemChat str _num</sqf>
All are separated by <sqf inline>;</sqf> and not the line return - they could all be inlined and it would not impact the code.
 
 
== Brackets ==
 
* <sqf inline>()</sqf> - Round brackets are used to override the default [[Order of Precedence]] or improve legibility.
* <sqf inline>[]</sqf> - Square brackets define [[Array]]s.
* <sqf inline>{}</sqf> - Curly brackets enclose instances of the [[Code]] [[:Category:Data Types|Data Type]]. They are also used in [[Control Structures]].


A [[Block|codeblock]] is a separate [[Data_Types|value type]] containing numerous SQF expressions. It will be executed when used with eg. [[call]] and can be assigned to a [[Variables|variable]] just like any other [[Data_Types|value type]]. [[Block|Codeblocks]] start with {{Inline code|<nowiki>{</nowiki>}} and end with {{Inline code|<nowiki>}</nowiki>}}


== Language Structure ==
== Whitespaces ==
The SQF Language is fairly simple in how it is built.
 
In fact: there are barely any actual language structures at all.
Whitespace consists of tabs and/or space characters.
 
For the
  purposes of the
    engine
      The 'line' begins at the first non whitespace character.
 
Similarly, trailing whitespace at the end of a line or statement is also ignored.
 
 
== Blank Lines ==
 
Blank lines are lines containing nothing but whitespace and are therefore ignored by the engine.
 


The functionality is provided via so called ''[[:Category:Scripting_Commands_Arma_3|operators]]'' (or more commonly known [[:Category:Scripting_Commands_Arma_3|scripting commands]]).
== Comments ==
Those [[:Category:Scripting_Commands_Arma_3|operators]] are one of the following types: [[#nular|Nular]], [[#unary|Unary]],  or [[#binary|Binary]].


=== Comments ===
A comment is additional text that gets ignored when a script is parsed.
A comment is additional text that gets ignored when a script is parsed.
They serve as future reference and are often used to explain a specific part of the code.
They serve as future reference and are often used to explain a specific part of the code.


In SQF, there are two kind of comments:
In SQF, there are two kind of comments:
{{codecomment|// In-line comment that ends on new line}}
<sqf>
// in-line comment that ends on new line
{{codecomment|/* Block Comment that can span above multiple lines and ends on the following character combination: */}}
 
/* block comment that can span above multiple lines
and ends on the following character combination: */
</sqf>
 
A comment can occur anywhere but inside a [[String|string]].
A comment can occur anywhere but inside a [[String|string]].
For example, the following would be valid:
For example, the following would be valid:
1 + {{codecomment|/* Some random comment in an expression. */}} 1
<sqf>1 + /* some random comment in an expression */ 1</sqf>


It should be mentioned that there is a [[comment]] unary [[:Category:Scripting_Commands_Arma_3|operator]] that should not be used as it will actually be executed (thus taking time to execute) but does nothing besides consuming a [[String|string]].
It should be mentioned that there is a [[comment]] unary [[:Category:Arma 3: Scripting Commands|operator]] that should not be used as it will actually be executed (thus taking time to execute) but does nothing besides consuming a [[String|string]].
There is no benefit in using it and the reason it exists is solely for backward compatibility.
There is no benefit in using it and the reason it exists is solely for backward compatibility.
Another way to make a ''comment'' that way, is to just place a [[String|string]]: {{Inline code|[...]; "i can be considered as a comment but should not be used"; [...]}}
Another way to make a ''comment'' that way, is to just place a [[String|string]]: <sqf inline>/* some code */ "I can be considered as a comment but should not be used"; /* some other code */</sqf>


''Comments are removed during the [[PreProcessor_Commands|preprocessing]] phase.'' This is important to know as that prevents usage in eg. a [[String|string]] that gets compiled using the [[compile]] unary [[:Category:Scripting_Commands_Arma_3|operator]] or when only using [[loadFile]].
''Comments are removed during the [[PreProcessor_Commands|preprocessing]] phase.'' This is important to know as that prevents usage in e.g a [[String|string]] that gets compiled using the [[compile]] unary [[:Category:Scripting Commands|operator]] or when only using [[loadFile]].


=== <span id="nular">Nular Operators</span> ===
A nular operator is more or less a computed [[Variables|variable]]. Each time accessed, it will return the current state of something.
It is tempting to think of a nular [[:Category:Scripting_Commands_Arma_3|operator]] as nothing more but a magic [[Variables|global variable]], but it is important to differentiate!


Consider following example in a mission with eg. 5 units:
== Nular Operators ==
{{codecomment|// Put the result of [[allUnits]] into a [[Variables|variable]].}}
 
_unitsArray = [[allUnits]];
A {{sic|nular|nullar}} operator is more or less a computed [[Variables|variable]]. Each time accessed, it will return the current state of something.
It is tempting to think of a nular{{sic}} [[:Category:Arma 3: Scripting Commands|operator]] as nothing more but a magic [[Variables|global variable]], but it is important to differentiate!
{{codecomment|// Display the current [[Array|array]] size using [[systemChat]].}}
 
[[systemChat]] [[str]] [[count]] _unitsArray;
Consider following example in a mission with e.g. 5 units:
<sqf>
{{codecomment|// Create a new unit in the player group.}}
// put the result of allUnits into a Variable
[[group]] [[player]] [[createUnit]] ["B_RangeMaster_F", [[position]] [[player]], [], 0, "FORM"];
_unitsArray = allUnits;
 
{{codecomment|// Output the [[Array|array]] size again}}
// display the current Array size using systemChat
[[systemChat]] [[str]] [[count]] _unitsArray;
systemChat str count _unitsArray;
 
{{codecomment|// Output the size of [[allUnits]]}}
// create a new unit in the player group
[[systemChat]] [[str]] [[count]] [[allUnits]];
group player createUnit ["B_RangeMaster_F", getPosATL player, [], 0, "NONE"];
 
// output the Array size again
systemChat str count _unitsArray;
 
// output the size of allUnits
systemChat str count allUnits;
</sqf>


Now, what would the output of this look like?
Now, what would the output of this look like?
Line 69: Line 108:
  System: 6
  System: 6


As you can see, {{Inline code|_unitsArray}} was not automatically updated as it would have been if it was not generated each time. If [[allUnits]] was just a [[Variables|global variable]] with a reference to some internal managed array, our private [[Variables|variable]] should have had reflected the change as [[Data_Types|value types]] are passed by reference.
As you can see, <sqf inline>_unitsArray</sqf> was not automatically updated as it would have been if it was not generated each time. If [[allUnits]] was just a [[Variables|global variable]] with a reference to some internal managed array, our private [[Variables|variable]] should have had reflected the change as [[Data_Types|value types]] are passed by reference.
The reason for this is because [[allUnits]] and other nular operators just return the current state of something and do not return a reference to eg. an [[Array|array]] containing all units.
The reason for this is because [[allUnits]] and other nular{{sic}} operators just return the current state of something and do not return a reference to eg. an [[Array|array]] containing all units.
It is generated each time, which is why some of theese operators are more expensive to run then ''just'' using a [[Variables|variable]].
It is generated each time, which is why some of theese operators are more expensive to run then ''just'' using a [[Variables|variable]].


=== <span id="unary">Unary Operators</span> ===
 
The unary [[:Category:Scripting_Commands_Arma_3|operators]] are [[:Category:Scripting_Commands_Arma_3|operators]] that expect an argument on their right side ({{Inline code|unary &lt;argument&gt;}}). They always will take the first argument that occurs.
== Unary Operators ==
 
The unary [[:Category:Arma 3: Scripting Commands|operators]] are operators that expect an argument on their right side ({{hl|unary &lt;argument&gt;}}). They always will take the first argument that occurs.


A common mistake would be the following:
A common mistake would be the following:
{{codecomment|// Create some [[Array|array]] containing three arrays.}}
<sqf>
_arr = [[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2]];
// create some Array containing three arrays
_arr = [[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2]];
{{codecomment|// Wrongly use the [[select]] operator to get the count of the third array.}}
 
[[count]] _arr [[select]] 2; <span style="color: Red;">// Error</span>
// wrongly use the select operator to get the count of the third array
count _arr select 2; // error
</sqf>


Now, what went wrong?
Now, what went wrong?


Let's put some brackets in the right places to make the mistake understandable:
Let's put some brackets in the right places to make the mistake understandable:
([[count]] _arr) [[select]] 2; <span style="color: Red;">// Error</span>
<sqf>(count _arr) select 2; // error</sqf>


Due to the nature of unary [[:Category:Scripting_Commands_Arma_3|operators]], count instantly consumes our [[Variables|variable]] ''_arr'' and returns the number ''3''.
Due to the nature of unary [[:Category:Arma 3: Scripting Commands|operators]], count instantly consumes our [[Variables|variable]] ''_arr'' and returns the number ''3''.
The ''3'' then is passed to [[select]] which does not knows what to do with a number as left argument and thus errors out.
The ''3'' then is passed to [[select]] which does not knows what to do with a number as left argument and thus errors out.


To do it correctly, one would have to put the {{Inline code|_arr select 2}} in brackets.
To do it correctly, one would have to put the <sqf inline>_arr select 2</sqf> in brackets.
The correct code thus would be:
The correct code thus would be:
{{codecomment|// Create an [[Array|array]] containing three [[Array|arrays]].}}
<sqf>
_arr = [[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2]];
// create an array containing three Arrays
_arr = [[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2]];
{{codecomment|// Use Brackets to correctly get count of the third [[Array|array]].}}
 
[[count]] (_arr [[select]] 2); {{codecomment|// Good :) will evaluate to 2}}
// use brackets to correctly get count of the third Array
count (_arr select 2); // good :) will evaluate to 2
</sqf>
 
 
== Binary Operators ==


=== <span id="binary">Binary Operators</span> ===
Binary [[:Category:Arma 3: Scripting Commands|operators]] expect two arguments ({{hl|&lt;1st argument&gt; binary &lt;2nd argument&gt;}}) and are executed according to their {{Link|#precedence}}. If their precedence is equal, they are executed left to right.
Binary [[:Category:Scripting_Commands_Arma_3|operators]] expect two arguments ({{Inline code|&lt;1st argument&gt; binary &lt;2nd argument&gt;}}) and are executed according to their [[#precedence|precedence]]. If their [[#precedence|precedence]] is equal, they are executed left to right.


As example, we will look into the following expression:
As example, we will look into the following expression:
{{codecomment|// Create a nested [[Array|array]] with 5 levels.}}
<sqf>
_arr = [[[[[1]]]]];
// create a nested Array with 5 levels
_arr = [[[[[1]]]]];
{{codecomment|// Receive the nested number with some random math expressions.}}
 
_arr [[select]] 0 [[select]] 1 [[a_minus_b|-]] 1 [[select]] 15 [[a_/_b|/]]  3 [[a_minus_b|-]] 5 [[select]] 0 [[select]] 10 [[a_*_b|*]] 10 [[a_plus_b|+]] 4 [[a_*_b|*]] 0 [[a_minus_b|-]] 100{{codecomment|// Evaluates to 1.}}
// receive the nested number with some random math expressions
_arr select 0 select 1 - 1 select 15 / 3 - 5 select 0 select 10 * 10 + 4 * 0 - 100 // evaluates to 1
</sqf>


Now, let us analyze why this is happening for the first few expressions:
Now, let us analyze why this is happening for the first few expressions:
Line 115: Line 164:
# 1 is loaded
# 1 is loaded
# 1 is loaded
# 1 is loaded
# [[a_minus_b|-]] is executed with the result of 4. & 5.
# [[-]] is executed with the result of 4. & 5.
# [[select]] is executed with the result of 3. & 6.
# [[select]] is executed with the result of 3. & 6.
# ...
# ...


If we now would put brackets at the correct spots, the expression will get clearer:
If we now would put brackets at the correct spots, the expression will get clearer:
((((_arr [[select]] 0) [[select]] (1 [[a_minus_b|-]] 1)) [[select]] ((15 [[a_/_b|/]]  3) [[a_minus_b|-]] 5)) [[select]] 0) [[select]] (((10 [[a_*_b|*]] 10) [[a_plus_b|+]] (4 [[a_*_b|*]] 0)) [[a_minus_b|-]] 100)
<sqf>((((_arr select 0) select (1 - 1)) select ((15 / 3) - 5)) select 0) select (((10 * 10) + (4 * 0)) - 100)</sqf>
 
As you can see the [[a_*_b|*]] and [[a_/_b|/]] are executed first which matches their [[#precedence|precedence]]. Afterwards, the [[a_plus_b|+]] and [[a_minus_b|-]] [[:Category:Scripting_Commands_Arma_3|operators]] will get executed followed by our [[select]] operator, which are executed from the left to the right.


===Rules of Precedence===
As you can see the <sqf inline>*</sqf> and <sqf inline>/</sqf> are executed first which matches their [[Order of Precedence|precedence]].
{| class="wikitable"
Afterward, the <sqf inline>+</sqf> and <sqf inline>-</sqf> [[:Category:Scripting Commands|operators]] will get executed followed by our [[select]] operator, which are executed from the left to the right.
|-
!Precedence
!Type of Operator
|-
|11 '''Highest Precedence'''
|NULAR OPERATORS, VARIABLES, VALUES OR BRACED EXPRESSIONS
|-
|10
|UNARY OPERATORS
|-
|9
|<code>[[a_hash_b|#]]</code>
|-
|8
|<code>[[a_^_b|^]]</code>
|-
|7
|<code>[[a_*_b|*]], [[a_/_b|/]], [[mod|%]], [[mod]], [[atan2]]</code>
|-
|6
|<code>[[a_plus_b|+]], [[a_minus_b|-]], [[max]], [[min]]</code>
|-
|5
|<code>[[else]]</code>
|-
|4
|''BINARY OPERATORS''
|-
|3
|<code>[[==]], [[!=]], [[a_greater_b|&gt;]], [[a_less_b|&lt;]], [[a_less=_b|&gt;=]], [[a_less=_b|&lt;=]], [[config_greater_greater_name|&gt;&gt;]]</code>
|-
|2
|<code>[[and|&&]], [[and]]</code>
|-
|1 '''Lowest Precedence'''
|<code>[[a or b|<nowiki>||</nowiki>]] [[a or b|or]]</code>
|}


== See also ==
* [[Control Structures]]
* [[SQS syntax]]
* [[Function]]
* [[Statement]]
* [[Block]]
* [[Missions#Locations_of_Mission_Files|Location of script files]]
* [[SQS to SQF conversion]]


[[Category: Syntax]] [[Category:Scripting Topics]]
[[Category: Syntax]]

Latest revision as of 16:51, 24 April 2024

Real Virtuality Scripting
Terminology ArgumentIdentifierExpressionOperandOperatorsParameterStatementVariablesMagic VariablesFunction
Syntax SQF SyntaxSQS SyntaxOrder of PrecedenceControl Structures
Tutorials Introduction to Arma ScriptingCode Best PracticesExample CodeCode OptimisationMission OptimisationMultiplayer ScriptingSQS → SQF
Data Types General ArrayBooleanCodeConfigControlDiary RecordDisplayEden EntityEden IDEditor ObjectGroupHashMapLocationNamespaceNumberObjectScript Handle
SideStringStructured TextTaskTeamTeam MemberNaNAnythingNothingVoidSwitch TypeWhile TypeWith TypeFor TypeIf Type
Special Arrays Array of Eden EntitiesColorDateParticleArrayPositionUnit Loadout ArrayVector3DWaypoint
Scripting Commands Scripting CommandsScripting Commands by Functionality
Scripting Functions Scripting FunctionsFunctions by Functionality
Debugging Common Scripting ErrorsDebugging TechniquesException handling
Advanced Event ScriptsEvent HandlersPreProcessor CommandsInitialisation OrderPerformance Profiling

SQF stands for Status Quo Function - a successor of Status Quo Script, which is deprecated since Armed Assault but could still be used in Arma 3. SQF was first introduced in Operation Flashpoint: Resistance together with the call operator in update 1.85.


The SQF Language is fairly simple in how it is built. In fact: there are barely any actual language structures at all.

The functionality is provided via so called operators (or more commonly known scripting commands). Those operators are one of the following types: Nularsic, Unary, or Binary.


Terminating an expression

An SQF expression has to be terminated via either ; (preferred by convention!) or ,.

_num = 10; _num = _num + 20; systemChat str _num;

In the above example, there are three expressions:

  1. _num = 10
  2. _num = _num + 20
  3. systemChat str _num

All are separated by ; and not the line return - they could all be inlined and it would not impact the code.


Brackets


Whitespaces

Whitespace consists of tabs and/or space characters.

For the
 purposes of the
   engine
     The 'line' begins at the first non whitespace character.

Similarly, trailing whitespace at the end of a line or statement is also ignored.


Blank Lines

Blank lines are lines containing nothing but whitespace and are therefore ignored by the engine.


Comments

A comment is additional text that gets ignored when a script is parsed. They serve as future reference and are often used to explain a specific part of the code.

In SQF, there are two kind of comments:

// in-line comment that ends on new line /* block comment that can span above multiple lines and ends on the following character combination: */

A comment can occur anywhere but inside a string. For example, the following would be valid:

1 + /* some random comment in an expression */ 1

It should be mentioned that there is a comment unary operator that should not be used as it will actually be executed (thus taking time to execute) but does nothing besides consuming a string. There is no benefit in using it and the reason it exists is solely for backward compatibility. Another way to make a comment that way, is to just place a string: /* some code */ "I can be considered as a comment but should not be used"; /* some other code */

Comments are removed during the preprocessing phase. This is important to know as that prevents usage in e.g a string that gets compiled using the compile unary operator or when only using loadFile.


Nular Operators

A nularsic operator is more or less a computed variable. Each time accessed, it will return the current state of something. It is tempting to think of a nularsic operator as nothing more but a magic global variable, but it is important to differentiate!

Consider following example in a mission with e.g. 5 units:

// put the result of allUnits into a Variable _unitsArray = allUnits; // display the current Array size using systemChat systemChat str count _unitsArray; // create a new unit in the player group group player createUnit ["B_RangeMaster_F", getPosATL player, [], 0, "NONE"]; // output the Array size again systemChat str count _unitsArray; // output the size of allUnits systemChat str count allUnits;

Now, what would the output of this look like?

System: 5
System: 5
System: 6

As you can see, _unitsArray was not automatically updated as it would have been if it was not generated each time. If allUnits was just a global variable with a reference to some internal managed array, our private variable should have had reflected the change as value types are passed by reference. The reason for this is because allUnits and other nularsic operators just return the current state of something and do not return a reference to eg. an array containing all units. It is generated each time, which is why some of theese operators are more expensive to run then just using a variable.


Unary Operators

The unary operators are operators that expect an argument on their right side (unary <argument>). They always will take the first argument that occurs.

A common mistake would be the following:

// create some Array containing three arrays _arr = [[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2]]; // wrongly use the select operator to get the count of the third array count _arr select 2; // error

Now, what went wrong?

Let's put some brackets in the right places to make the mistake understandable:

(count _arr) select 2; // error

Due to the nature of unary operators, count instantly consumes our variable _arr and returns the number 3. The 3 then is passed to select which does not knows what to do with a number as left argument and thus errors out.

To do it correctly, one would have to put the _arr select 2 in brackets. The correct code thus would be:

// create an array containing three Arrays _arr = [[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2]]; // use brackets to correctly get count of the third Array count (_arr select 2); // good :) will evaluate to 2


Binary Operators

Binary operators expect two arguments (<1st argument> binary <2nd argument>) and are executed according to their precedence. If their precedence is equal, they are executed left to right.

As example, we will look into the following expression:

// create a nested Array with 5 levels _arr = [[[[[1]]]]]; // receive the nested number with some random math expressions _arr select 0 select 1 - 1 select 15 / 3 - 5 select 0 select 10 * 10 + 4 * 0 - 100 // evaluates to 1

Now, let us analyze why this is happening for the first few expressions:

  1. _arr is loaded
  2. 0 is loaded
  3. select is executed with the result of 1. & 2.
  4. 1 is loaded
  5. 1 is loaded
  6. - is executed with the result of 4. & 5.
  7. select is executed with the result of 3. & 6.
  8. ...

If we now would put brackets at the correct spots, the expression will get clearer:

((((_arr select 0) select (1 - 1)) select ((15 / 3) - 5)) select 0) select (((10 * 10) + (4 * 0)) - 100)

As you can see the * and / are executed first which matches their precedence. Afterward, the + and - operators will get executed followed by our select operator, which are executed from the left to the right.