# Operators

Operators are natural building blocks of languages, so it's no surprise ABS has its fair share of them.

As a general rule, you should understand that ABS does not allow to use operator on different types, with a couple exceptions. Here is a list of operators you can use, with examples on how to make the most out of them.

# ==

Equality operator, one of the few that can be used between arguments of different type:

1 == 1 # true
1 == "hello world" # false

# !=

Not equals operator, one of the few that can be used between arguments of different type:

1 != 1 # false
1 != "hello world" # true

# +

Addition, can be used to merge arrays and combine strings as well:

1 + 1 # 2
"hello" + " " + "world" # "hello world"
[1] + [2] # [1, 2]

# +=

Compound addition:

a = 10
a += 1 # a is now 11

# -

Subtraction:

0 - 1 # -1

# -=

Compound subtraction:

a = 10
a -= 1 # a is now 9

# *

Multiplication:

1 * 2 # 2

# *=

Compound multiplication:

a = 10
a *= 10 # a is now 100

# /

Division:

5 / 5 # 1

# /=

Compound division:

a = 10
a /= 2 # a is now 5

# in

Membership test operator (find whether a needle is in the haystack):

1 in [1, 2, 3] # true
9 in [1, 2, 3] # false
9 in 9 # unknown operator: NUMBER in NUMBER
"str" in "string" # true
"xyz" in "string" # false
"x" in {"x": 1} # true
"y" in {"x": 1} # false

# !in

Negative membership test operator (find whether a needle is not in the haystack):

1 !in [1, 2, 3] # false
9 !in [1, 2, 3] # true
9 !in 9 # unknown operator: NUMBER in NUMBER
"str" !in "string" # false
"xyz" !in "string" # true
"x" !in {"x": 1} # false
"y" !in {"x": 1} # true

# **

Mathematical exponentiation:

2 ** 2 # 4
2 ** 0 # 1

# **=

Compound exponentiation:

a = 10
a **= 0 # a is now 1

# %

Modulo:

19 % 5 # 4

# %=

Compound modulo:

a = 19
a %= 5 # a is now 4

# >

Greater than:

10 > 0 # true
0 > 10 # false

When used with strings, > redirects the input (left argument) to a file (right argument):

"Hi, there" > "file.txt"
`cat /etc/hosts` > "copy-of-etc-hosts.bkp"

# >=

Greater or equal than:

1 >= 1 # true
2 >= 1 # true

# <

Lower than:

10 < 0 # false
0 < 10 # true

# <=

Lower or equal than:

1 <= 1 # true
1 <= 2 # true

# <=>

The combined comparison operator allows to test whether a number is lower, equal or higher than another one with one statement:

5 <=> 5 # 0
5 <=> 6 # -1
6 <=> 5 # 1

# &&

Logical AND, which supports short-circuiting (opens new window):

true && true # true
true && false # false
1 && 2 # 2
1 && 0 # 0
0 && 2 # 0
"" && "hello world" # ""
"hello" && "world" # "world"

# ||

Logical OR, which supports short-circuiting (opens new window):

true || true # true
true || false # true
1 || 2 # 1
1 || 0 # 1
"" || "hello world" # "hello world"
"hello" || "world" # "hello"

# .

Property accessor, used to access properties or methods of specific variables:

hello = {"to_who": "the world"}
hello.to_who # "the world"

There are some builtin functions that you can access through the property accessor:

"hello".len() # 5

(a comprehensive list of function is documented in the "Types and functions" section of the documentation)

# ?.

Optional chaining operator, used to access properties in a "safe" way.

Given the following object:

test = {"property": 1}

An error would be raised if you were trying to access a non-existing property such as test.something.something_else:

ERROR: invalid property 'something_else' on type NULL
	[1:15]	test.something.something_else

Optional chainig prevents those errors from being raised, auto-magically converting non-existing properties and methods to NULL:

test?.something?.something_else # null
test?.something?.something_else() # null

# ..

Range operator, which creates an array from start to end:

1..10 # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
10..1 # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

# !

Negation:

a = true
!a # false

# !!

Even though there is no double negation operator, using 2 bangs will result into converting the argument to boolean:

!!1 # true
!!0 # false
!!"" # false
!!"hello" # true

# ~

The tilde (meaning "around") is used to do similarity comparisons (x "is almost" y).

In case of strings, it will compare them case-insensitively

"hello" == "HELLO" # false
"hello" ~ "HELLO" # true

In case of integers, it will compare their integer values:

1 == 1.5 # false
1 ~ 1.5 # true

When in front of a number, it will instead be used as a bitwise NOT:

~0 # -1
~"hello" # ERROR: Bitwise not (~) can only be applied to numbers, got STRING (hello)

# &

Bitwise AND:

1 & 1 # 1
1 & "hello" # ERROR: type mismatch: NUMBER & STRING

# |

Bitwise OR:

1 | 1 # 1
1 | "hello" # ERROR: type mismatch: NUMBER | STRING

# ^

Bitwise XOR:

1 ^ 1 # 0
1 ^ "hello" # ERROR: type mismatch: NUMBER ^ STRING

# >>

Bitwise right shift:

1 >> 1 # 0
1 >> "hello" # ERROR: type mismatch: NUMBER >> STRING

When used with strings, >> appends the input (left argument) to a file (right argument):

"Hi" >> "file.txt"
" " >> "file.txt"
"there!" >> "file.txt"

`cat file.txt` # Hi there!

# <<

Bitwise left shift:

1 << 1 # 2
1 << "hello" # ERROR: type mismatch: NUMBER << STRING