Squirrel Operators: Difference between revisions
No edit summary |
No edit summary |
||
(5 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
<div style="width:33.333%; top: 0px; position: sticky; height: 30px; display: inline-block; text-align: center; line-height: 30px; background-color:#BBBBBB;">[https://sigwiki.potato.tf/index.php/Squirrel_Data_Types BACK]</div><div style="width:33.333%; top: 0px; position: sticky; height: 30px; display: inline-block; text-align:center; line-height: 30px; background-color:#BBBBBB;">[https://sigwiki.potato.tf/index.php/An_Introduction_to_Squirrel HOME]</div><div style="width:33.333%; top: 0px; position: sticky; height: 30px; display: inline-block; text-align:center; line-height: 30px; background-color:#BBBBBB;">[https://sigwiki.potato.tf/index.php/Squirrel_Expressions NEXT]</div> | <div style="width:33.333%; z-index: 1; top: 0px; position: sticky; height: 30px; display: inline-block; text-align: center; line-height: 30px; background-color:#BBBBBB;">[https://sigwiki.potato.tf/index.php/Squirrel_Data_Types BACK]</div><div style="width:33.333%; z-index: 1; top: 0px; position: sticky; height: 30px; display: inline-block; text-align:center; line-height: 30px; background-color:#BBBBBB;">[https://sigwiki.potato.tf/index.php/An_Introduction_to_Squirrel HOME]</div><div style="width:33.333%; z-index: 1; top: 0px; position: sticky; height: 30px; display: inline-block; text-align:center; line-height: 30px; background-color:#BBBBBB;">[https://sigwiki.potato.tf/index.php/Squirrel_Expressions NEXT]</div> | ||
Now that you know how to create data and store it into variables, we can now go over how to modify and operate on that data. Operators are used for this purpose, they take one or more operands and produce a result value. Operators which take one operand are called unary operators, two operands are binary operators, and three are ternary operators. | Now that you know how to create data and store it into variables, we can now go over how to modify and operate on that data. Operators are used for this purpose, they take one or more operands and produce a result value. Operators which take one operand are called unary operators, two operands are binary operators, and three are ternary operators. | ||
Line 9: | Line 9: | ||
<span style="font-size:120%">'''Arithmetic'''</span> | <span style="font-size:120%">'''Arithmetic'''</span> | ||
The arithmetic operators <code>'''+, -, *, /, %'''</code> are used for, well, arithmetic! Technically we can use them on certain object types (more on in future sections), however by default they're for use with the integer, float, and (partially) string types. You should be familiar with most of these already. | |||
'''Unary Operators''' | '''Unary Operators''' | ||
Line 72: | Line 75: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
The addition operators <code>'''+'''</code> and <code>'''+='''</code> are | The addition operators <code>'''+'''</code> and <code>'''+='''</code> are the only operators able to handle string operands. If one operand is a string, Squirrel will try to convert the other operand to a string as well. | ||
This same type conversion happens between floats and integers for any arithmetic operator (not just addition). If one operand is a float and the other an integer, the resulting value will be a float. | This same type conversion happens between floats and integers for any arithmetic operator (not just addition). If one operand is a float and the other an integer, the resulting value will be a float. | ||
Line 89: | Line 92: | ||
<span style="font-size:120%">'''Assignment'''</span> | <span style="font-size:120%">'''Assignment'''</span> | ||
The assignment operator (<code>'''='''</code>) is used for assigning values to variables. It can be combined with the arithmetic operators to create arithmetic assignment operators (e.g. <code>'''x += 5'''</code>), which is a shortened version of: <code>'''x = x + 5'''</code>. | |||
{| class="wikitable" style="width:100%; margin:auto; text-align:center font-weight:bold; font-size:110%" | {| class="wikitable" style="width:100%; margin:auto; text-align:center font-weight:bold; font-size:110%" | ||
Line 143: | Line 149: | ||
<span style="font-size:120%">'''Increment/Decrement'''</span> | <span style="font-size:120%">'''Increment/Decrement'''</span> | ||
The increment and decrement operators are used (respectively) to increase or decrease a variable by 1. This can be done '''before''' the variable is returned in the case of the prefixed operators, or it can be done '''after''' the variable has been returned in the case of the postfixed operators. | |||
{| class="wikitable" style="width:100%; margin:auto; text-align:center font-weight:bold; font-size:110%" | {| class="wikitable" style="width:100%; margin:auto; text-align:center font-weight:bold; font-size:110%" | ||
|+ | |+ | ||
Line 176: | Line 186: | ||
y = x++; // y = 1, it got the value of x and then x got incremented, x = 2 | y = x++; // y = 1, it got the value of x and then x got incremented, x = 2 | ||
y = ++x; // y = 3, x got incremented and then y got x's value | y = ++x; // y = 3, x got incremented and then y got x's value, x = 3 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 184: | Line 194: | ||
<span style="font-size:120%">'''Relational'''</span> | <span style="font-size:120%">'''Relational'''</span> | ||
The relational operators compare the values of their operands and return a boolean based on their result. | |||
{| class="wikitable" style="width:100%; margin:auto; text-align:center font-weight:bold; font-size:110%" | {| class="wikitable" style="width:100%; margin:auto; text-align:center font-weight:bold; font-size:110%" | ||
|+ | |+ | ||
Line 234: | Line 248: | ||
z = x <= y; // true | z = x <= y; // true | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<hr> | |||
<span style="font-size:120%">'''Logical'''</span> | <span style="font-size:120%">'''Logical'''</span> | ||
The logical operators are primarily used in control flow to create condition based code. We'll go over control flow in a future section, for now just know that they exist and that you can reference back here if you forget what one does. | |||
{| class="wikitable" style="width:100%; margin:auto; text-align:center font-weight:bold; font-size:110%" | {| class="wikitable" style="width:100%; margin:auto; text-align:center font-weight:bold; font-size:110%" | ||
|+ | |+ | ||
Line 247: | Line 268: | ||
| ! | | ! | ||
| !x | | !x | ||
|true if x evaluates to false, otherwise | |true if x evaluates to false, otherwise true | ||
|- style="text-align:center" | |- style="text-align:center" | ||
|Logical AND | |Logical AND | ||
Line 261: | Line 282: | ||
For most purposes the above definitions for AND & OR are sufficient, however Squirrel does not simply return boolean true or false for these operators, rather it returns a specific operand passed to the operator based on the result. | For most purposes the above definitions for AND & OR are sufficient, however Squirrel does not simply return boolean true or false for these operators, rather it returns a specific operand passed to the operator based on the result. | ||
<pre> | |||
Logical AND will return the *first* operand that evaluates to *false*, if none do, it will return the *last* operand. | |||
Logical OR will return the *first* operand that evaluates to *true*, if none do, it will return the *last* operand. | |||
</pre> | |||
A common use case of this functionality with the logical OR operator is to fill a variable with the first value that evaluates to true. | |||
<syntaxhighlight lang="c#" line="1" start="1" style="font-weight:bold;> | |||
local var = othervar || otherothervar || 1; | |||
</syntaxhighlight> | |||
In some cases we may have access to variables that we don't know the value of, they could be null for all we know! This is a good way of providing "backup" values to use in our program. | |||
Line 276: | Line 305: | ||
local z = 5 && "0" // "0" | local z = 5 && "0" // "0" | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<hr> | |||
<span style="font-size:120%">'''Bitwise'''</span> | <span style="font-size:120%">'''Bitwise'''</span> | ||
&, |, | All data in computer memory is stored as sequences of bits; 0's and 1's. The bitwise operators work directly with those bits for integer values in Squirrel. As a beginner you aren't likely to use these operators unless you have a very specific reason to. We won't go over what each of these does under the hood as it's a bit out of scope for this language introduction, however if you want to learn more about bits and the bitwise operators, [https://www.learncpp.com/cpp-tutorial/bitwise-operators/ learncpp.com] goes over them quite nicely. (Just ignore the C++ specific stuff.) | ||
{| class="wikitable" style="width:100%; margin:auto; text-align:center font-weight:bold; font-size:110%" | |||
|+ | |||
!Operator | |||
!Symbol | |||
!Form | |||
!Operation | |||
|- style="text-align:center" | |||
|Bitwise NOT | |||
| ~ | |||
| ~x | |||
|Flips all bits in x; 1 becomes 0 and 0 becomes 1 | |||
|- style="text-align:center" | |||
|Bitwise AND | |||
| & | |||
| x & y | |||
|Each bit in x AND each bit in y | |||
|- style="text-align:center" | |||
|Bitwise OR | |||
| <nowiki>|</nowiki> | |||
| x <nowiki>|</nowiki> y | |||
|Each bit in x OR each bit in y | |||
|- style="text-align:center" | |||
|Bitwise XOR | |||
| ^ | |||
| x ^ y | |||
|Each bit in x Exclusive OR each bit in y | |||
|- style="text-align:center" | |||
|Left shift | |||
| << | |||
| x << y | |||
|Shift all bits in x left by integer y spaces | |||
|- style="text-align:center" | |||
|Right shift | |||
| >> | |||
| x >> y | |||
|Shift all bits in x right by integer y spaces | |||
|- style="text-align:center" | |||
|Unsigned right shift | |||
| >>> | |||
| x >>> y | |||
|Same as normal right shift (>>) but treat x as unsigned (only positive values, no negative ones) | |||
|} | |||
For day to day use, you're mostly going to be using bitwise AND along with bitwise OR to manipulate '''bit flags''', which are variables where each of the 32 bits (or however big the variable is) represents a boolean flag. So instead of having 32 different boolean variables, you can have 1 variable that represents 32 booleans! | |||
The m_fFlags datamap of [[CBaseEntity]] in the Source Engine is a real example of this, each of the 32 bits of the integer can represent a flag. Defined in the source code are macros which contain the values for these bits, as you can see below the left shift operator is used to shift the bits (more accurately the only bit) of integer 1 left by an increasing amount of places. | |||
<syntaxhighlight lang="c#" line="1" start="1" style="font-weight:bold;> | |||
#define FL_ONGROUND (1<<0) // At rest / on the ground | |||
#define FL_DUCKING (1<<1) // Player flag -- Player is fully crouched | |||
#define FL_ANIMDUCKING (1<<2) // Player flag -- Player is in the process of crouching or uncrouching but could be in transition | |||
#define FL_WATERJUMP (1<<3) // player jumping out of water | |||
#define FL_ONTRAIN (1<<4) // Player is _controlling_ a train, so movement commands should be ignored on client during prediction. | |||
... | |||
#define FL_UNBLOCKABLE_BY_PLAYER (1<<31) // pusher that can't be blocked by the player | |||
</syntaxhighlight> | |||
The middle part has been omitted for brevity, the first five macros are equivalent to the integer values: <code>'''1, 2, 4, 8, and 16'''</code> and the last is <code>'''2147483648'''</code>. In VScript, these have been defined in the [https://developer.valvesoftware.com/wiki/Team_Fortress_2/Scripting/Script_Functions/Constants#FPlayer FPlayer] enum and can be used in your programs when necessary, however the way to manipulate bit flags is a bit different than regular integers, you can't just try to add them together with the addition operator! | |||
To add bit flags together, use the bitwise OR (<code>'''|'''</code>) operator. | |||
< | <syntaxhighlight lang="c#" line="1" start="1" style="font-weight:bold;> | ||
// In real code, you would need to prefix these with "Constants.FPlayer." (e.g. Constants.FPlayer.FL_ONGROUND), this is just a brief demonstration | |||
local mybitflags = FL_ONGROUND | FL_DUCKING | FL_ANIMDUCKING; // 7 (1 + 2 + 4) | |||
</syntaxhighlight> | |||
To remove a flag, use the bitwise AND (<code>'''&'''</code>) operator along with the bitwise NOT (<code>'''~'''</code>) operator on the flag. | |||
< | <syntaxhighlight lang="c#" line="1" start="1" style="font-weight:bold;> | ||
// In real code, you would need to prefix these with "Constants.FPlayer." (e.g. Constants.FPlayer.FL_ONGROUND), this is just a brief demonstration | |||
local mybitflags = FL_ONGROUND | FL_DUCKING | FL_ANIMDUCKING; // 7 (1 + 2 + 4) | |||
/ | |||
+ | |||
+ | |||
printl(mybitflags & ~FL_ONGROUND); // 6 | |||
printl( mybitflags & ~(FL_ONGROUND | FL_ANIMDUCKING) ); // 2 | |||
</syntaxhighlight> | |||
To test if a flag is present, use the bitwise AND (<code>'''&'''</code>) operator and compare the result against 0 using the inequality operator. | |||
<syntaxhighlight lang="c#" line="1" start="1" style="font-weight:bold;> | |||
// In real code, you would need to prefix these with "Constants.FPlayer." (e.g. Constants.FPlayer.FL_ONGROUND), this is just a brief demonstration | |||
local mybitflags = FL_ONGROUND | FL_DUCKING | FL_ANIMDUCKING; | |||
local testflags = FL_ONGROUND | FL_DUCKING; | |||
printl( (mybitflags & testflags) != 0 ) // true | |||
printl( (mybitflags & FL_ONTRAIN) != 0 ) // false | |||
</syntaxhighlight> |
Latest revision as of 23:00, 31 October 2023
Now that you know how to create data and store it into variables, we can now go over how to modify and operate on that data. Operators are used for this purpose, they take one or more operands and produce a result value. Operators which take one operand are called unary operators, two operands are binary operators, and three are ternary operators. This section is quite long so don't worry about memorizing every single operator, just browse through and become familiar with the various types of operators and their uses, you'll have plenty of examples throughout the rest of the guide to work with.
Arithmetic
The arithmetic operators +, -, *, /, %
are used for, well, arithmetic! Technically we can use them on certain object types (more on in future sections), however by default they're for use with the integer, float, and (partially) string types. You should be familiar with most of these already.
Unary Operators
Operator | Symbol | Form | Operation |
---|---|---|---|
Unary minus | - | -x | Negation of x |
local x = 50;
x = -x; // -50
Binary Operators
Operator | Symbol | Form | Operation |
---|---|---|---|
Addition | + | x + y | x plus y |
Subtraction | - | x - y | x minus y |
Multiplication | * | x * y | x multiplied by y |
Division | / | x / y | x divided by y |
Remainder | % | x % y | the remainder from: x divided by y |
local v = 100 + 100; // 200
local w = 0 - 2; // -2
local x = 5.5 * 100; // 550.0
local y = 20 / 2; // 10
local z = 23 % 2; // 1
The addition operators +
and +=
are the only operators able to handle string operands. If one operand is a string, Squirrel will try to convert the other operand to a string as well.
This same type conversion happens between floats and integers for any arithmetic operator (not just addition). If one operand is a float and the other an integer, the resulting value will be a float.
local str1 = "Hello,";
local str2 = " World!";
str1 += str2; // Hello World!
// See next section for +=
local float = 10 - 7.0 // 3.0
Assignment
The assignment operator (=
) is used for assigning values to variables. It can be combined with the arithmetic operators to create arithmetic assignment operators (e.g. x += 5
), which is a shortened version of: x = x + 5
.
Operator | Symbol | Form | Operation |
---|---|---|---|
Assignment | = | x = y | assign value y to variable x |
Addition Assignment | += | x += y | assign x + y to variable x |
Subtraction Assignment | -= | x -= y | assign x - y to variable x |
Multiplication Assignment | *= | x *= y | assign x * y to variable x |
Division Assignment | /= | x /= y | assign x / y to variable x |
Remainder Assignment | %= | x %= y | assign x % y to variable x |
local x = 100;
x += 5; // equivalent to: x = x + 5
x -= 5; // 100
x *= 100; // 10,000
x /= 2; // 5,000
x %= 2; // 0
Increment/Decrement
The increment and decrement operators are used (respectively) to increase or decrease a variable by 1. This can be done before the variable is returned in the case of the prefixed operators, or it can be done after the variable has been returned in the case of the postfixed operators.
Operator | Symbol | Form | Operation |
---|---|---|---|
Pre-increment | ++ | ++x | increment x by 1, then return x |
Post-increment | ++ | x++ | copy x, increment variable x by 1, then return the copy |
Pre-decrement | -- | --x | decrement x by 1, then return x |
Post-decrement | -- | x-- | copy x, decrement variable x by 1, then return the copy |
local x = 1;
local y;
y = x++; // y = 1, it got the value of x and then x got incremented, x = 2
y = ++x; // y = 3, x got incremented and then y got x's value, x = 3
Relational
The relational operators compare the values of their operands and return a boolean based on their result.
Operator | Symbol | Form | Operation |
---|---|---|---|
Equality | == | x == y | true if x equals y, false otherwise |
Inequality | != | x != y | true if x does not equal y, false otherwise |
Less than | < | x < y | true if x is less than y, false otherwise |
Greater than | > | x > y | true if x is greater than y, false otherwise |
Less than or equal to | <= | x <= y | true if x is less than or equal to y, false otherwise |
Greater than or equal to | >= | x >= y | true if x is greater than or equal to y, false otherwise |
local x = 5;
local y = 5;
local z;
z = x == y; // true
z = x != y; // false
z = x > y; // false
z = x >= y; // true
z = x < y; // false
z = x <= y; // true
Logical
The logical operators are primarily used in control flow to create condition based code. We'll go over control flow in a future section, for now just know that they exist and that you can reference back here if you forget what one does.
Operator | Symbol | Form | Operation |
---|---|---|---|
Logical NOT | ! | !x | true if x evaluates to false, otherwise true |
Logical AND | && | x && y | true if both operands evaluate to true, otherwise false |
Logical OR | || | x || y | true if either operand evaluates to true, otherwise false |
For most purposes the above definitions for AND & OR are sufficient, however Squirrel does not simply return boolean true or false for these operators, rather it returns a specific operand passed to the operator based on the result.
Logical AND will return the *first* operand that evaluates to *false*, if none do, it will return the *last* operand. Logical OR will return the *first* operand that evaluates to *true*, if none do, it will return the *last* operand.
A common use case of this functionality with the logical OR operator is to fill a variable with the first value that evaluates to true.
local var = othervar || otherothervar || 1;
In some cases we may have access to variables that we don't know the value of, they could be null for all we know! This is a good way of providing "backup" values to use in our program.
Certain values evaluate to false, these are: false, null, 0, 0.0
. Everything else evaluates to true.
local v = 0 && 1; // 0
local w = null || "bark"; // "bark"
local x = "" || true; // ""
local y = !0; // true
local z = 5 && "0" // "0"
Bitwise
All data in computer memory is stored as sequences of bits; 0's and 1's. The bitwise operators work directly with those bits for integer values in Squirrel. As a beginner you aren't likely to use these operators unless you have a very specific reason to. We won't go over what each of these does under the hood as it's a bit out of scope for this language introduction, however if you want to learn more about bits and the bitwise operators, learncpp.com goes over them quite nicely. (Just ignore the C++ specific stuff.)
Operator | Symbol | Form | Operation |
---|---|---|---|
Bitwise NOT | ~ | ~x | Flips all bits in x; 1 becomes 0 and 0 becomes 1 |
Bitwise AND | & | x & y | Each bit in x AND each bit in y |
Bitwise OR | | | x | y | Each bit in x OR each bit in y |
Bitwise XOR | ^ | x ^ y | Each bit in x Exclusive OR each bit in y |
Left shift | << | x << y | Shift all bits in x left by integer y spaces |
Right shift | >> | x >> y | Shift all bits in x right by integer y spaces |
Unsigned right shift | >>> | x >>> y | Same as normal right shift (>>) but treat x as unsigned (only positive values, no negative ones) |
For day to day use, you're mostly going to be using bitwise AND along with bitwise OR to manipulate bit flags, which are variables where each of the 32 bits (or however big the variable is) represents a boolean flag. So instead of having 32 different boolean variables, you can have 1 variable that represents 32 booleans!
The m_fFlags datamap of CBaseEntity in the Source Engine is a real example of this, each of the 32 bits of the integer can represent a flag. Defined in the source code are macros which contain the values for these bits, as you can see below the left shift operator is used to shift the bits (more accurately the only bit) of integer 1 left by an increasing amount of places.
#define FL_ONGROUND (1<<0) // At rest / on the ground
#define FL_DUCKING (1<<1) // Player flag -- Player is fully crouched
#define FL_ANIMDUCKING (1<<2) // Player flag -- Player is in the process of crouching or uncrouching but could be in transition
#define FL_WATERJUMP (1<<3) // player jumping out of water
#define FL_ONTRAIN (1<<4) // Player is _controlling_ a train, so movement commands should be ignored on client during prediction.
...
#define FL_UNBLOCKABLE_BY_PLAYER (1<<31) // pusher that can't be blocked by the player
The middle part has been omitted for brevity, the first five macros are equivalent to the integer values: 1, 2, 4, 8, and 16
and the last is 2147483648
. In VScript, these have been defined in the FPlayer enum and can be used in your programs when necessary, however the way to manipulate bit flags is a bit different than regular integers, you can't just try to add them together with the addition operator!
To add bit flags together, use the bitwise OR (|
) operator.
// In real code, you would need to prefix these with "Constants.FPlayer." (e.g. Constants.FPlayer.FL_ONGROUND), this is just a brief demonstration
local mybitflags = FL_ONGROUND | FL_DUCKING | FL_ANIMDUCKING; // 7 (1 + 2 + 4)
To remove a flag, use the bitwise AND (&
) operator along with the bitwise NOT (~
) operator on the flag.
// In real code, you would need to prefix these with "Constants.FPlayer." (e.g. Constants.FPlayer.FL_ONGROUND), this is just a brief demonstration
local mybitflags = FL_ONGROUND | FL_DUCKING | FL_ANIMDUCKING; // 7 (1 + 2 + 4)
printl(mybitflags & ~FL_ONGROUND); // 6
printl( mybitflags & ~(FL_ONGROUND | FL_ANIMDUCKING) ); // 2
To test if a flag is present, use the bitwise AND (&
) operator and compare the result against 0 using the inequality operator.
// In real code, you would need to prefix these with "Constants.FPlayer." (e.g. Constants.FPlayer.FL_ONGROUND), this is just a brief demonstration
local mybitflags = FL_ONGROUND | FL_DUCKING | FL_ANIMDUCKING;
local testflags = FL_ONGROUND | FL_DUCKING;
printl( (mybitflags & testflags) != 0 ) // true
printl( (mybitflags & FL_ONTRAIN) != 0 ) // false