4. Statement¶
Berry is an imperative programming language. This paradigm assumes that programs are executed step by step. Normally, Berry statements are executed sequentially, and this program structure is called sequential structure. Although the sequence structure is very basic, branch structures and loop structures are usually used in actual programs. Berry provides several control statements to realize this complex flow structure, such as conditional statements and iteration statements.
Except for line comments, carriage returns or line feeds (“\r” and
“\n”) are only used as blank characters, so statements can be
written across lines. In addition, you can write multiple statements on
the same line.
You can add a semicolon at the end of the statement to indicate the end of the statement, but the interpreter can usually split the statement automatically without using a semicolon. You can use semicolons to tell the interpreter how to parse the code for the code that will be ambiguous. However, it is better not to write ambiguous code.
4.1 Simple sentence¶
4.1.1 Expression statement¶
Expression statements are mainly statements composed of assignment
expressions or function call expressions. Other expressions can also
form sentences, but they have no meaning. For example, expression
1+2 is a sentence written alone, but it has no effect. The following
routines give examples of expression statements and function statements:
a = 1 # Assignment statement
print(a) # Function call statement
The first line is a simple assignment statement that assigns the literal value
1 to the variable a. The second line is a function call
statement, which prints the value of variable a by calling the
print function.
Cross-line expressions are written in the same way as single-line expressions, and no special line continuation symbols are required. E.g:
a = 1 +
func() # Wrap line
You can also write multiple expression statements on one line, and various types of statements can be written on one line. This example puts two expression statements on the same line:
b = 1 c = 2 # Multiple statements
Sometimes the programmer wants to write two statements, but the interpreter may mistakenly think it is one statement. This problem is caused by the ambiguity in the process of grammatical analysis. Take this code as an example:
a = c
(b) = 1 # Be regarded as a function call
Suppose the 4th and 5th lines are intended to be two expression
sentences: a = c and (b) = 1, but the interpreter will interpret
them as a sentence: a = c(b) = 1. The cause of this problem is that
the interpreter incorrectly parses c and (b) into function
calls. To avoid ambiguity, we can add a semicolon at the end of the
statement to clearly separate the statement:
a = c; (b) = 1;
A better way is not to use parentheses on the left side of the assignment number. Obviously, there is no reason to use parentheses here. Under normal circumstances, complex expressions should not appear on the left side of the assignment operator, but only simple expressions composed of variable names, domain operation expressions, and subscript operation expressions:
a = c b = 1
Using simple expressions only on the left side of the assignment sign will not cause ambiguity in sentence segmentation. Therefore, in most cases, there is no need to use semicolons to separate expressions, and we do not recommend this way of writing.
Block¶
A Block is a collection of several sentences. A block is a scope, so
the variables defined in the block can only be accessed inside the block
and its sub-blocks. There are many places where blocks are used, such as
if statements, while statements, function declarations, etc.
These statements will contain a block through a pair of keywords. For
example, the block used in the if statement:
if isOpen
close()
print('the device was closed')
end
The statements in lines 2 to 3 constitute a block, which is sandwiched
between the pair of keywords if and end (the conditional
expression of the statement in if is not in the block). The block
does not need to contain any statements, which constitutes an empty
block, or it can be said to be a block containing an empty statement.
Broadly speaking, any number of consecutive sentences can be called a
block, but we prefer to expand the scope of the block as much as
possible, which can ensure that the area of the block is consistent with
the scope of the scope. In the above example, we tend to think that rows
2 to 3 are a whole block, which is the largest range between if
keywords and end keywords.
do Statement¶
Sometimes we just want to open up a new scope, but don’t want to use any
control statements. In this case, we can use the do statement to
encapsulate the block. do The statement has no control function.
do The sentence has the form
do block end
Among them block is the block we need. This statement uses a pair of
do and end keywords to contain blocks. do The statement has
no control function, nor does it generate any runtime instructions.
conditional statement¶
Berry provides if statements to realize the function of conditional
control execution. This kind of program structure is generally called
branch structure. if The statement will determine the branch of
execution based on the true (true) or false (false) conditional
expression. In some languages, there are other options for implementing
conditional control. For example, languages such as C and C++ provide
switch statements, but in order to simplify the design, Berry does
not support switch statements.
if Statement¶
``if`` statement is used to implement the branch structure, which
selects the branch of the program according to the true or false of a
certain judgment condition. The statement can also include else
branch or elif branch. The simple if statement form without
branches is
if condition block end
condition is a conditional expression. When the value of
condition* is ``true``,block** in the second line will be
executed, otherwise the block will be skipped and the statement
following end will be executed. In the case of block being
executed, after the last statement in the block is executed, it will
leave the if statement and start executing the statement following
end.
Here is an example to illustrate the usage of the if statement:
if 8 % 2 == 0
print('this number is even')
end
This code is used to judge whether the number 8 is even, and if it
is, it will output this number is even. Although this example is
very simple, it is enough to illustrate the basic usage of if
sentences.
If you want to have a corresponding branch for execution when the
condition is met and not met, use the if statement with the else
branch. if else The form of the sentence is
if condition blockelseendDifferent from the simple if statement, the if else statement
will execute block under the else branch when the value of
condition is false. No matter which branch is executed under
block, after the last statement in the block is executed, the
if else statement will pop out, that is, the statement after end
will be executed. In other words, no matter whether the value of
condition is true or false, one block will be executed.
Continue to use the judgment of parity as an example, this time change the demand to output corresponding information according to the parity of the input number. The code to achieve this requirement is:
if x % 2 == 0
print('this number is even')
else
print('this number is odd')
end
Before running this code, we must first assign an integer value to the
variable x, which is the number we want to check for parity. If
x is an even number, the program will output
this number is even, otherwise it will output
this number is odd.Sometimes we need to nest if statements. One
way is to nest a if statement under the else branch. This is a
very common requirement because many conditions need to be judged
consecutively. For this kind of demand, use the if else statement to
write:
if expr
block
else
if expr
block
end
end
Obviously, this way of writing will increase the indentation level of
the code, and it is more cumbersome to use multiple end at the end.
As an improvement, Berry provides the elif branch to optimize the
above writing. Using the elif branch is equivalent to the above
code, in the form
if conditionelif conditionelseendelif The branch must be used after the if branch and before the
branch, and the elif branch can be used multiple times in
succession. If the condition corresponding to the elif branch is
satisfied, the block under the branch will be executed. elif
Branching is suitable for situations that require multiple conditions to
be judged in sequence.
We use a piece of code that judges positive, negative, and 0 to
demonstrate the elif branch:
if x> 0
print('positive')
elif x == 0
print('zero')
else
print('negative')
end
Here too, the variable x must be assigned first. This code is very
simple and will not be explained.
Some languages have a problem called dangling “else”, which refers
to when a if sentence is nested inside another if sentence,
where does the else branch belong? Problem with the sentence if.
When using C/C++, we must consider the problem of dangling else. In
order to avoid ambiguity on the problem of if else, C/C++
programmers often use curly braces to make a branch into a block. In
Berry, the branch of the if statement must be a block, which also
determines that Berry does not have the problem of overhanging else.
Iteration Statement¶
Iterative statements are also called loop statements, which are used to
repeat certain operations until the termination condition is met. Berry
provides while statement and for two iteration statements. Many
languages also provide these two statements for iteration. Berry’s
while statement is similar to the while statement in C/C++, but
Berry’s for statement is only used to traverse the elements in the
container, similar to the foreach statement provided by some
languages and the one introduced by C++11 New for sentence style.
The C-style for statement is not supported.
while Statement¶
``while`` statement is a basic iterative statement. while
statement uses a judgment condition. When the condition is true, the
loop body is executed repeatedly, otherwise the loop is ended. The
pattern of the statement is
while condition block end
When the program runs to the while statement, it will check whether
the expression condition is true or false. If it is true, execute
the loop body block, otherwise end the loop. After executing the
last statement in block, the program will jump to the beginning of
the statement while and start the next round of detection. If the
condition expression is false when it is first evaluated, the loop
body block will not be executed at all (same as the condition
expression of the if statement is false).Generally speaking, the
value of condition expression should be able to change during the
loop, rather than a constant or a variable modified outside the loop,
which will cause the loop to not execute or fail to terminate. A loop
that never ends is called an endless loop. Usually we usually expect the
loop to execute a specified number of times and then terminate. For
example, when using the while loop to access all elements in the
array, we hope that the number of loop executions is the length of the
array, for example:
i = 0
l = ['a','b','c']
while i < l.size()
print(l[i])
i = i + 1
end
This loop gets the elements from the array l and prints them. We use
a variable i as the loop counter and array index. We let the value
of i reach the length of the array l to end the loop. In the
last line of the loop body, we add 1 to the value of i to ensure
that the next element of the array is accessed in the next loop, and the
while loop ends when the number of loops reaches the length of the
array.
for Statement¶
Berry’s ``for`` statement is used to traverse the elements in the container, and its form is
for variable : expressionendexpression The value of the expression must be an iterable container
or function, such as the range class. for The statement obtains
an iterator from the container, and obtains an element in the container
every time through the call to the iterator.
variable is called an iteration variable, which is always defined in
the statement for. Therefore variable must be a variable name
and not an expression. The container element obtained from the iterator
in each loop will be assigned to the iteration variable. This process
occurs before the first statement in block.
The for statement will check whether there are any unvisited
elements in the iterator for iteration. If there are, the next iteration
will start, otherwise it will end the for statement and execute the
statement following end. Currently, Berry only provides read-only
iterators, which means that the elements in the container cannot be
modified through the iteration variables in the for statement.
The scope of the iteration variable variable is limited to the loop
body block, and the variable will not have any relationship with the
variable with the same name outside the scope. To illustrate this point,
let’s use an example to illustrate. In this example, we use the for
statement to access all the elements in the rang instance and print
them out. Of course, we also use this example to demonstrate the scope
of loop variables.
i = "Hi, I'm fine." # Outer variable
for i: 0 .. 2
print(i) # Iteration variable
end
print(i)
In this example, relative to the iteration variable i defined in
line 2, the variable i defined in line 1 is an external variable.
Running this example will get the following result
0
1
2
Hi, I'm fine
It can be seen that the iteration variable i and the external
variable i are two different variables. They just have the same name
but different scopes.
for Principle of Statement¶
Unlike the traditional iterative statement while, the for
statement uses iterators to traverse the container. If you need to use
the for statement to traverse a custom class, you need to understand
its implementation mechanism. When using the for statement, the
interpreter hides a lot of implementation details. In fact, for such
code:
for i: 0 .. 2
print(i)
end
Will be translated into the following equivalent code by the interpreter:
var it = __iterator__(0 .. 2)
try
while true
var i = it()
print(i)
end
except 'stop_iteration'
# do nothing
end
To some extent, the for statement is just a syntactic sugar, it is
essentially just a simple way of writing a piece of complex code. In
this equivalent code, an intermediate variable it is used. The value
of the variable is an iterator. In this example, it is an iterator of
the range container 0..2. When processing the for statement,
the interpreter hides the intermediate variable of the iterator, so it
cannot be accessed in the code.
The parameter of function __iterator__ is a container, and the
function returns an iterator of parameters. This function gets the
iterator by calling the parameter method. Therefore, if the return value
of the iter method is an instance (instance) type, this instance
must have a next method and a hasnext method.
The parameter of function __hasnext__ is an iterator, which checks
whether the iterator has the next element by calling the hasnext
method of the iterator. hasnext The return value of the method is of
type boolean. The parameter of function __next__ is also an
iterator, which gets the next element in the iterator by calling the
next method of the iterator.
So far, the __iterator__, __hasnext__ and __next__ functions
simply call some methods of the container or iterator and then return
the return value of these methods. Therefore, the equivalent writing of
the for statement can also be simplified into this form:
do
var it = (0 .. 2).iter()
while (it.hasnext())
var i = it.next()
print(i)
end
end
This code is easier to read. It can be seen from the effective code that
the scope of the iterator variable it is the entire for
statement, but it is not visible outside the for statement, while
the scope of the iteration variable i is in the loop body, so every
time Iterations will define new iteration variables.
Jump Statement¶
The jump statement provided by Berry is used to realize the jump of the
program flow in the loop process. Jump statements are divided into
break statements and continue statements. These two statements
must be used inside iterative statements and can only be used inside
functions to jump. Some languages provide goto statements to realize
arbitrary jumps within functions, which Berry does not provide, but the
effects of goto statements can be replaced by conditional statements
and iteration statements.
break Statement¶
break Used to terminate the iteration statement and jump out. After
the execution of the break statement, the nearest level of the
iteration statement will be terminated immediately and execution will
continue from the position of the first statement after the iteration
statement. In order to illustrate the execution flow of the break
statement, we use an example to demonstrate:
while true
print('before break')
break
print('after break')
end
print('out of the loop')
In this code, the break statement is in a while loop. Before and
after the break statement and after the while statement, we have
placed a print statement to test the execution flow of the program. The
result of this code is:
before break
out of the loop
This shows that the while statement ends the loop at the break
statement position on the 3rd line and the program continues to execute
from the 6th line.
continue Statement¶
continue The statement is also used inside an iteration statement.
Its function is to end an iteration and immediately start the next
round. Therefore, after the execution of the continue statement, the
remaining code in the iteration statement of the nearest layer will no
longer be executed, but a new round of iteration will start. Here we use
a for statement to demonstrate the function of the continue
statement:
for i: 0 .. 5
if i >= 2
continue
end
print('i =', i)
end
print('out of the loop')
Here, the for statement will iterate 6 times. When the iteration
variable i is greater than or equal to 2, the continue
statement on line 3 will be executed, and the print statement on line 5
will not be executed thereafter. In other words, line 5 will only be
executed in the first two iterations (at this time i<2). The running
result of this routine is:
i = 0
i = 1
out of the loop
It can be seen that the value of the variable i is only printed
twice, which is in line with expectations. Readers can try to print the
value of the variable i before the continue statement. You will
find that the for statement does iterate 6 times, indicating that
the continue statement does not terminate the iteration.
import Statement¶
Berry has some predefined modules, such as the math module for
mathematical calculations. These modules cannot be used directly, but
must be imported with the import statement. There are two ways to
import a module:
import name
import name as variable
name For the name of the module to be imported, when using the first
writing method to import the module, the imported module can be called
directly by using the module name. The second way of writing is to
import a module named name and modify the module name when calling
it to variable. For example, a module named math, we use the
first method to import and use:
import math
math.sin(0)
Here directly use math to call the module. If the name of a module
is relatively long and it is not convenient to write, you can use the
import as statement. Here, assume a module named hardware. We
want to call the function setled of the module, we can import the
module hardware into the variable named hw and use:
import hardware as hw
hw.setled(true)
To find modules, all paths in sys.path() are explored sequentially.
If you want to add a specific path before the import (like reading from
SD card) you can use the following helper function:
def push_path(p)
import sys
var path = sys.path()
if path.find(p) == nil # append only if it's not already there
path.push(p)
end
end
Exception Handling¶
The mechanism allows the program to capture and handle exceptions that occur during runtime. Berry supports an exception capture mechanism, which allows the exception capture and handling process to be separated. That is, part of the program is used to detect and collect exceptions, and the other part of the program is used to handle exceptions.
First of all, the problematic program needs to throw an exception first. When these programs are in an exception handling block, a specific program will catch and handle the exception.
Raise an exception¶
Using the raise statement raises an exception. raise The
statement will pass a value to indicate the type of exception so that it
can be identified by a specific exception handler. Here is how to use
the raise statement:
raise exception
raise exception,message
The value of the expression exception is the thrown Outliers; the optional message expression is usually a string describing the exception information, and this expression is called Abnormal parameter. Berry allows any value to be used as an abnormal value, for example, a string can be used as an abnormal value:
raise 'my_error','an example of raise'
After the program executes to the raise statement, it will not
continue to execute the statements following it, but will jump to the
nearest exception handling block. If the most recent exception handling
block is in other functions, the functions along the call chain will
exit early. If there is no exception handling block, Abnormal exit
will occur, and the interpreter will print the exception error message
and the call stack of the error location.When the raise statement is
in the try statement block, the exception will be caught by the
latter. The caught exception will be handled by the except block
associated with the try block. If the thrown exception can be
handled by the except block, the execution of this block will
continue from the statement after the last except block. If all
except statements cannot handle the exception, the exception will be
rethrown until it can be handled or the exception exits.
Outliers¶
In Berry, you can use any value as an outlier, but we usually use short strings. Berry may also throw some exceptions internally. We call these exceptions Standard exception. All standard exception values are of string type.
Outliers |
Description |
Parameter Description |
|---|---|---|
|
Assertion failed |
Specific exception information |
|
(usually out of bounds) |
Specific exception information |
|
IO Malfunction |
Specific exception information |
|
Key error |
Specific exception information |
|
VM runtime exception |
Specific exception information |
|
End of iterator |
no |
|
Syntax error |
|
by the compiler |
||
|
Unrealized function |
Specific exception information |
|
Type error |
Specific exception information |
Standard exception list
Catch exceptions¶
Use the except statement to catch exceptions. It must be paired with
the try statement, that is, a try statement block must be
followed by one or more except statement blocks. try-except The
basic form of the sentence is
try blockexcept … block endThe except branch can have the following forms
except ..
except exceptions
except exceptionsas variable
except exceptionsas variable , message
except .. as variable
except .. as variable , message
The most basic except statement does not use parameters, this
except branch will catch all exceptions; Catch exception list
exceptions is a list of outliers that can be matched by the
corresponding except branch, used between multiple values in the
list Separate by commas; variable is Abnormal variable, if the
branch catches an exception, the outlier will be bound to the variable;
message is Abnormal parameter variable, if the branch catches an
exception, the abnormal parameter value will be bound To the variable.
When an exception is caught in the try statement block, the
interpreter will check the except branch one by one. If the
exception value exists in the capture list of a branch, the code block
under the branch will be called to handle the exception, and the entire
try-except statement will exit after the code block is executed. If
all the except branches do not match, the exception will be
re-thrown and caught and handled by the outer exception handler.
Error Handling Patterns¶
Berry provides two main approaches to error handling:
Exception Handling: Using
try/exceptblocks to catch and handle exceptions.Return Value Checking: Many functions return special values to indicate errors rather than raising exceptions.
The second approach is common in functions that parse data or perform operations that might fail. Here are some examples:
# JSON parsing - returns nil on parsing error
data = json.load('{"invalid": }')
if data == nil
print("Invalid JSON")
end
# Map access - find() returns nil if key doesn't exist
value = map.find("key")
if value == nil
print("Key not found")
end
# String operations - find() returns -1 if substring not found
index = string.find("hello", "z")
if index == -1
print("Substring not found")
end
This pattern of checking return values is particularly useful in resource-constrained environments where exception handling might be more expensive.