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.







Grouping symbol


() [] .

Field operation



- ! ~

Negative sign, logical negation, bit flip



* / %

M ultiplication, division, and remainder



+ -

Addition, subtraction



<< >>

Move left, move right




Bitwise AND




Bitwise XOR




Bitwise OR




Concatenation operator



< <= > >=

Greater than, greater than or equal to



== !=

Equal to, not equal to




Logical AND




Logical OR



? :

Conditional operator



&= \| = ^= <<= >>=



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.





Unary minus

- expr


Plus/string concatenation

expr + expr


Minus sign

expr - expr


Multiplication sign

expr * expr


Division sign

expr / expr


Take the remainder

expr % expr

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.





Less than

expr < expr


Less than or equal to

expr <= expr



expr == expr


not equal to

expr != expr


greater or equal to

expr >= expr


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.





Logical AND

expr && expr


Logical OR

expr \|\| expr


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.




Bit flip



Bitwise and

expr & expr


Bitwise or

expr \| expr


Bitwise exclusive or

expr ^ expr


Shift left

expr << expr


Shift right

expr >> expr

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




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.