3. Expression¶
3.1 Basics¶
An expression (Statement) is composed of one to more operands and operators, and a result can be obtained by evaluating the expression. This result is called the value of the expression. The operand can be a literal value, variable, function call or sub-expression, etc. Simple expressions and operators can also be combined into more complex expressions. Similar to the four arithmetic operations, the precedence of operators affects the evaluation order of expressions. The higher the precedence of the operator, the earlier the expression is evaluated.
Operators and expressions¶
Berry provides some unary operators (Unary Operator) and binary
operators (Binary Operator). For example, the logical AND operator
&&
is a binary operator, and the logical negation operator !
is
a unary operator. Some operators can be either unary operators or binary
operators. The specific meaning of such operators depends on the
context. For example, operator -
is a unary symbol in expression
-1
, but it is a binary minus sign in expression 1-2
.
Operator combination expression¶
Both the left and right sides of a binary operator can be subexpressions, so you can use binary operators to combine expressions. A more complex expression often has multiple operators and operands. At this time, the order of evaluation of each sub-expression in the expression may affect the value of the expression. The precedence and associativity of operators guarantee the uniqueness of the expression evaluation order. For example, a combined expression:
1 + 10/2 * 3
The four arithmetic operations in daily use will first calculate the
division expression 10/2
, then the multiplication expression, and
finally the addition expression. This is because multiplication and
division have higher priority than addition.
operand type¶
In the operation of expressions, the operands may have types that do not
match the operators. In addition, binary operators usually require the
left and right operands to be of the same type. The expression
’10’+10
is wrong. You cannot add a string to an integer. The problem
with the expression -’b’
is that you cannot take a negative value on
a string. Sometimes a binary operator has different operand types but
can perform operations. For example, when adding an integer to a real
number, the integer object will be converted to a real number and added
to another real number object. The logical AND and logical OR operators
allow the operands on both sides of the operator to be of any type. In
logical expressions, they will always be converted to the boolean
type according to certain rules.
Another situation is that operators can be overloaded when using custom classes. In essence, you can interpret this operator arbitrarily, and it is up to you to decide what type of its operand should be.
3.1.1 Priority and associativity¶
In a compound expression composed of multiple operators, the precedence and associativity of the operators determine the order of evaluation of the expressions. The precedence and associativity of each operator are given in Table 1.1.
The precedence specifies the order of evaluation between different
operators, and expressions with higher precedence operators will be
evaluated first. For example, the process of evaluating the expression
1+2*3
will first calculate the result of 2*3
, and then the
result of the addition expression. Using parentheses can improve the
evaluation order of low-priority expressions. For example, in the
evaluation of expression (1+2)*3
, the result of expression 1+2
in the parentheses is calculated first, and then the multiplication
expression outside the parentheses is calculated.
Associativity refers to the evaluation order of the operands on both
sides of the operator, where the operands may be subexpressions. For
example, in the addition expression expr1 + expr2
, the value of
expr1
is calculated first and then the value of expr2
, because
the addition operator is left-associative.
priority |
Operator |
Description |
Associativity |
---|---|---|---|
1 |
|
Grouping symbol |
|
2 |
|
Field operation |
left |
3 |
|
Negative sign, logical negation, bit flip |
left |
4 |
|
M ultiplication, division, and remainder |
left |
5 |
|
Addition, subtraction |
left |
6 |
|
Move left, move right |
left |
7 |
|
Bitwise AND |
left |
8 |
|
Bitwise XOR |
left |
9 |
|
Bitwise OR |
left |
10 |
|
Concatenation operator |
left |
11 |
|
Greater than, greater than or equal to |
left |
12 |
|
Equal to, not equal to |
left |
13 |
|
Logical AND |
left |
14 |
|
Logical OR |
left |
15 |
|
Conditional operator |
right |
16 |
|
Assignment |
right |
Operator list
Use brackets to increase priority¶
Parentheses can be used when we need operators with lower precedence to be evaluated first. During expression evaluation, the value of the expression in parentheses is calculated first. In other words, for the entire expression, the expression in parentheses is equivalent to an operand, regardless of the composition of the expression in parentheses.
3.2 Operator¶
3.2.1 Arithmetic Operators¶
Arithmetic operators are used to implement arithmetic operations. These operators are similar to the mathematical symbols we usually use. The arithmetic operators provided by Berry are shown in Table 1.2.
Operator |
Description |
Example |
---|---|---|
|
Unary minus |
|
|
Plus/string concatenation |
|
|
Minus sign |
|
|
Multiplication sign |
|
|
Division sign |
|
|
Take the remainder |
|
Arithmetic Operator
Binary operator +
In addition to being a plus sign, it is also a
string concatenation. When the operand of this operator is a string,
string concatenation will be performed to concatenate two strings into a
longer string. To be precise, +
as a string concatenation is no
longer in the category of arithmetic operators.
The binary operator %
is the remainder symbol. Its operands must be
integers. The result of the remainder operation is the remainder after
dividing the left operand by the right operand. For example, the result
of 11%4
is 3
. The real number type cannot do divisible, so the
remainder is not supported.
In general, arithmetic operators do not satisfy the commutative law. For
example, the values of the expressions 2/4
and 4/2
are not the
same.
All arithmetic operators can be overloaded in the class. The overloaded operators are not necessarily limited to their original functional design, but are determined by the programmer.
3.2.2 Relational operators¶
Relational operators are used to compare the magnitude of the operands. The six relational operators supported by Berry are given in Table 1.3.
Operator |
Description |
Example |
---|---|---|
|
Less than |
|
|
Less than or equal to |
|
|
equal |
|
|
not equal to |
|
|
greater or equal to |
|
|
more than the |
|
Relational operator
By comparing the magnitude relationship of the operands or judging
whether the operands are equal, evaluating the relational expression
will produce a Boolean result. When the relationship is satisfied, the
value of the relationship expression is true
, otherwise it is
false
. Relational operators ==
and !=
can use any type of
operand, and allow the left and right operands to have different types.
Other relational operators allow the use of the following combinations
of operands:
integer relop integer
real relop real
integer relop real
real relop integer
string relop string
In relational operations, the equal sign ==
and inequality sign
!=
satisfy the commutative law. If the left and right operands are
of the same type or are both numeric types (integer and real number),
the operands are judged to be equal according to the value of the
operands, otherwise the operands are considered unequal. Equality and
inequality are reciprocal operations: if a==b
is true, then a!=b
is false, and vice versa. Other relational operators do not satisfy the
commutative law, but have the following properties: <
and >=
are
reciprocal operations, and >
and <=
are reciprocal operations.
Relational operations require that the operands must be of the same
type, otherwise it is an incorrect expression.
Instances can overload operators as methods. If the relational operator is overloaded, the program needs to ensure the above properties.
Among the relational operators, ==
and !=
operators have more
relaxed requirements than <
, <=
, >
and >=
, which only
allow comparisons between the same types. In actual program development,
the judgment of equality or inequality is usually simpler than the
judgment of size. Some operation objects may not be able to judge the
size but can only judge the equality or inequality. This is the case
with the Boolean type.
logical operators¶
Logical operators are divided into three types: logical AND, logical OR and logical NOT. As shown in Table 1.4.
Operator |
Description |
Example |
---|---|---|
|
Logical AND |
|
|
Logical OR |
|
|
Logical negation |
|
Logical Operators
For the logical AND operator, when the values of both operands are
true
, the value of the logical expression is true
, otherwise it
is false
.
For the logical OR operator, when the values of both operands are
false
, the value of the logical expression is false
, otherwise
it is true
.
The role of the logical negation operator is to flip the logical state
of the operand. When the operand value is true
, the logical
expression value is false
, otherwise the value is true
.
Logical operators require that the operand is of Boolean type, and if the operand is not of Boolean type, it will be converted. See section [section::type_bool] for conversion rules.
Logic operations use an evaluation strategy called Short-circuit evaluation (short-circuit evaluation). This evaluation strategy is: for the logical AND operator, the second operand will be evaluated if and only if the left operand is true; for the logical OR operator, if and only if the left operand is false Will evaluate the right operand. The nature of short-circuit evaluation causes the code in the logical expression to not all run.
Bitwise Operator¶
Bit operators can implement some binary bit operations, and bit operations can only be used on integer types. The detailed information of bit operators is shown in Table 1.5. Bit operation refers to the operation of binary bits directly on integers. Logical operations can be extended to bit operations. Taking logical AND as an example, we can perform this operation on each binary bit to achieve bitwise AND, such as 110b AND 011b = 010b. Bit operations also support shift operations, which move numbers on a binary basis.
Operator |
Example |
|
---|---|---|
|
Bit flip |
|
|
Bitwise and |
|
|
Bitwise or |
|
|
Bitwise exclusive or |
|
|
Shift left |
|
|
Shift right |
|
Bitwise operator
Although it can only be used for integers, bit operations are still
versatile. Bit operations can implement many optimization techniques. In
many algorithms, using bit operations can save a lot of code. For
example, to determine whether a number n
is a power of 2, we can
judge whether the result of n & (n - 1)
is 0
. In some languages
with high execution efficiency, shift operations can also be used to
optimize multiplication and division (usually there is no obvious effect
in scripting languages).
The bitwise AND operator “&
” is a binary operator, which performs
the binary AND operation of two integer operands: only when the binary
bits corresponding to the operands are all 1
, the result It was
1
. For example, 1110b & 0111b = 0110b.
The bitwise OR operator “|
” is a binary operator, which performs a
binary-bit OR operation on two integer operands: only when the binary
bits corresponding to the operands are both 0
, the bit of the result
It was 0
. For example, 1000b | 0001b = 1001b.
The bitwise exclusive OR operator “^
” is a binary operator, which
performs binary exclusive OR operation on two integer operands: when the
binary bits corresponding to the operands are different, the bit value
of the result is 1
. For example,
1100b ^ 0101b = 1001b.
The left shift operator “<<
” is a binary operator, which moves the
left operand to the left by the number of bits specified by the right
operand on a binary basis. For example
00001010b << 3 = 01010000b.The right shift operator “>>
”
is a binary operator, which shifts the left operand to the right by the
number of bits specified by the right operand on a binary basis. For
example, 10100000b >> 3 = 00010100b.
The bitwise flip operator “~
” is a unary operator, and the result of the
expression is to flip the value of each binary bit of the operand. For
example, ∼
10100011b = 01011100b.
The following are some examples of using bit operations. Usually we don’t use binary directly. The results in the examples have been converted into common bases.
1 << 1 # 2
168 >> 4 # 10
456 & 127 # 72
456 | 127 # 511
0xA5 ^ 0x5A # 255
~2 # -3
Assignment operator¶
The assignment operator only appears in the assignment expression, and the operand of the operator must be a writable object. The assignment expression has no result, so continuous assignment operations cannot be used.
Simple assignment operator¶
The simple assignment operator =
can be used for variable
assignment. If the left operand variable is not defined, the variable
will be defined. The assignment operator is used to bind the value of
the right operand with the left operand. This process is also called
“assignment”. Therefore, the left operand cannot be a constant, nor can
it be any object that cannot be written. These are some legal assignment
expressions:
a = 45 b ='string' c = 0
And the following assignment expression is wrong:
1 = 5 # Trying to assign a constant 1
a = b = 0 # Continuous assignment
When assigning nil
, integer, real and Boolean types to variables,
the value of the object will be passed to the left operand, but for
other types, the assignment operation just passes the reference of the
object to the left operand. Since strings, functions, and class types
are read-only, all passing references will not have side effects, but
you must be extra careful with instance types.
Compound Assignment Operator¶
Compound assignment operators are operators that combine binary operators and assignment operators. They are practical extensions to simple assignment operators. Compound assignment operators can simplify the writing of some expressions. Table 1.6 lists all the compound assignment operators
Operator |
Description |
---|---|
|
Addition assignment |
|
Subtraction assignment |
|
Multiplication assignment |
|
Preliminary assignment |
|
Remainder assignment |
|
Bitwise AND assignment |
|
Bitwise OR assignment |
|
Bitwise XOR assignment |
|
Left shift assignment |
|
Right shift assignment |
Bit operator
The compound assignment expression performs the binary operation
corresponding to the compound assignment operator on the left operand
and the right operand, and then assigns the result to the left operand.
Taking +=
as an example, the expression a += b
is equivalent to
a = a + b
. The compound assignment operator is also an assignment
operator, so it has a lower priority. The binary operator corresponding
to the compound assignment operator is always evaluated after the right
operand, so an expression like a *= 1 + 2
should be equivalent to
a = a * (1 + 2)
.
Unlike the simple assignment operator, the left operand of the compound assignment operator must participate in the evaluation, so the compound assignment expression does not have the function of defining variables. The assignment operator itself cannot be overloaded in the class. Users can only overload the binary operator corresponding to the compound assignment operator. This also ensures that the compound assignment operator will always conform to the basic characteristics of assignment operations.
domain operator and subscript operator¶
Domain operator .
is used to access an attribute or member of an
object. You can use domain operators for both types of modules and
instances:
l = list[]
l.push('item 0')
s = l.item(0) #'item 0'
The subscript operator []
is used to access the elements of an
object, for example
l[2] = 10 # Read by index
n = l[2] # Write by index
Classes that support subscript reading must implement the item
method, and classes that support subscript writing must implement the
setitem
method. The map and list in the standard container implement
these two methods, so they support reading and writing using the
subscript operator. The string supports subscript reading, but does not
support subscript writing (strings are read-only values):
'string'[2] #'r'
'string'[2] ='a' # error: value'string' does not support index assignment
Currently, strings support integer subscripts, and the range of subscripts cannot exceed the length of the string.
Conditional Operator¶
The conditional operator (? :
) is similar to the if else
statement, but the former can be used in expressions. The usage form of
the conditional operator is:
cond ? expr1 : expr2
cond is the expression used to judge the condition. The evaluation process of the conditional operator is: first find the value of cond, if the condition is true, evaluate expr1 and return the value, otherwise, the value of expr2 ] Evaluate and return the value. expr1 and expr2 can have different types, so the following is correct:
result = scope <6?'bad': scope
This expression first determines whether scope
is less than 6
,
if it is, it returns bad
, otherwise it returns the value of
scope
. Regardless of the condition of the conditional expression,
only one of expr1 or expr2 will be executed, similar to the
short-circuit characteristic of logical AND and logical OR operations.
Nested Condition Operators¶
One conditional operator can be nested in another conditional operator, that is, the conditional expression can be used as cond or expr of another conditional expression. For example, use conditional expressions to divide scores into three levels: excellent, good, and bad:
result = scope >= 9?'excellent': scope >= 6?'good':'bad'
The first condition checks whether the score is not lower than 9
points. If it is, execute the branch after ?
and return
’excellent’
; otherwise, execute the branch after :
, which is
also a conditional expression. The condition checks whether the score is
not lower than 6
, if it is, it returns ’good’
, otherwise it
returns ’bad’
.
The conditional operator satisfies the right associativity, so the value of the branch expression must be evaluated first to get the value of the conditional expression. Therefore, in a nested conditional expression, the nested conditional expression is evaluated first, and then the outer conditional expression is evaluated.
Priority of conditional operators¶
Since the precedence of conditional expressions is very low (second only to assignment operators), it is often necessary to add parentheses outside the conditional expressions. For example, when a conditional expression is used as an operand of an arithmetic expression, parentheses will have different effects on the result:
result = 10 * (sign <0? -1: 1) # the result is -10 or 10
result = 10 * sign <0? -1: 1 # the result is -1 or 1
The result of the first expression is correct, and the second expression
takes 10 * sign < 0
as a condition to judge, which does not meet the
expectation of the conditional expression as the right operand of the
multiplication.
Concatenation Operator¶
+
operator¶
When the left and right operands are both strings, the +
operator is
used to connect the two strings, and the new string obtained is the
value of the expression. Therefore, this operator is often used for
string concatenation:
result ='abc' + '123' # the result is'abc123'
+
Operators can also be used to connect two list instances:
result = [1, 2] + [3, 4] # the result is [1, 2, 3, 4]
Unlike the list.push
method, the +
operator merges two lists
into a larger list object, with the elements of the left operand at the
head of the result list, and the elements of the right operand at the
end of the result list.
..
operator¶
..
is a special operator. If the left operand is a string, the
behavior of the expression is to concatenate the left and right operands
into a new string (automatic conversion if the right operand is not a
string):
result ='abc' + 123 # the result is'abc123'
The ..
operator is often used when concatenating a string and a
non-string value.If the left operand is a list instance, the ..
operator will append the right operand to the end of the list, and then
use this list as the value of the expression:
result = [1, 2] .. 3 # the result is [1, 2, 3]
This process will directly modify the left operand, which is very
similar to the push
method of list
(except for strings which are
immutable objects). The join operation of list can be executed in chain:
result = [1, 2] .. 3 .. 4 # the result is [1, 2, 3, 4]
All values in this process will be appended to the leftmost list object.
If the left and right operands are both integers, use the ..
operator to get an integer range object:
result = 1 .. 10 # the result is (1..10)
This object is used to represent a closed interval of integers, where the left operand is the lower limit and the right operand is the upper limit. Such integer range objects are often used for iteration.