Now that we have learned how data can be stored and evaluated in YCP, we will take a look at the surrounding code structure that can be realized. Code structure is created by means of blocks and statements.
Despite not being “really” statements, comments do (and should) belong to the overall structure of a YCP program. There are two kinds of comments:
Single-line comments
Single-line comments may start at any position on the line and reach up to the end of this line. They are introduced with “//”.
Multi-line comments
Multi-line comments may also start at any position on the line but they may end on another line below the starting line. Consequently there must be a start tag (“/*”) and an end tag (“*/”) as is shown below.
Synopsis: data_type variable_name = initial_value;
Variable declarations in YCP are simliar to C. Before you can use a variable, you must declare it. With the declaration you appoint the new variable to be of a certain data_type which means you can assign only values of that specific type. To avoid any errors caused by unintialized variables, a declaration must imply a suitable value assignment.
Note: A variable declaration may occur at several points in the code which determines its validity (accessability) in certain program regions (see Section 3.8.15, “Variable Scopes and blocks”).
Synopsis: variable_name = value;
An assignment statement is almost the same as a declaration statement. Just leave out the declaration. It is an error to assign a value to a variable that has not already been declared or to a variable of different data type.
Synopsis: if (condition) then_part [ else else_part ]
Depending on condition only one of the code branches then_part and else_part is executed. The else_part is optional and may be omitted. Both then_part and else_part may either be single statements or a sequence of statements enclosed in curly brackets, i.e. a block. The then_part is executed if and only if condition evaluates to true, the else_part otherwise. It is an error if condition evaluates to something other than true or false.
Synopsis: while (condition) loop_body
The while() loop executes the attached loop_body again and again as long as condition evaluates to true. The loop_body may be either a single statement or a block of statements.
Because condition is checked at the top of loop_body, it may not be executed at all if condition is false right from start.
Synopsis: do loop_body while (condition);
The do...while() loop executes the attached loop_body again and again as long as condition evaluates to true. The loop_body may be either a single statement or a block of statements.
Because condition is checked at the bottom of loop_body, it is executed at least once, even if condition is false right from start.
Synopsis: repeat loop_body until (condition);
The repeat...until() loop executes the attached loop_body again and again as long as condition evaluates to false. The loop_body may be either a single statement or a block of statements.
Because condition is checked at the bottom of loop_body, it is executed at least once, even if condition is true right from start.
repeat...until() is similar to do...while() except that condition is logically inverted. The example below has been converted from the do...while() example above.
Synopsis: break;
The break statement is used within loops to exit immediately. The execution is continued at the first statement after the loop.
Synopsis: continue;
The continue statement is used within loops to abandon the current loop cycle immediately. In contrast to break it doesnt exit the loop but jumps to the conditional clause that controls the loop. So for a while() loop, it jumps to the beginning of the loop and checks the condition. For a do...while() loop or repeat...until() loop, it jumps to the end of the loop end checks the condition.
Synopsis: return [ return_value ];
The return statement immediately leaves the current function or a current toplevel block (that contains it) and optionally assigns a return_value to this block. If blocks are nested, i.e. if the current block is contained in another block, the return statement leaves all nested blocks and defines the value of the outermost block.
However, if a block is used in an expression other than a block, and that expression is contained in an outer block, the return statement of the inner block won't leave the outer block but define the value of the inner block. This behaviour is a as one would expect. For example in the iteration builtins in Section 3.8.16, “Applying Expressions To Lists And Maps”,
Synopsis: data_type function_name ( [ typed_parameters ] ) function_body
A function definition creates a new function in the current namespace named function_name with a parameter list typed_parameters that has function_body attached for evaluation. The function_body must return a value of type data_type and the arguments passed upon function call must match the type definitions in typed_parameters.
Synopsis: data_type function_name ( [ typed_parameters ] );
A function declaration allows you to declare only a header of a function without its body. It's main purpose is for indirect recursion etc. You have to provide a function definition with exactly the same arguments later in the same file. A new function will be declared in the current namespace named function_name with a parameter list typed_parameters.
Synopsis: include " included file";
The include statement allows you to insert contents of a file at the given place in the current file. If the current file is a module, the contents of the included file will become a part of the module.
This is useful for dividing a large file into number of pieces. However, if a file is included more than once in a single block, the 2nd, 3rd etc. include statements are ignored.
The included file can be a relative or an absolute file name. Relative names are looked up with /usr/share/YaST2/include as a base.
In contrast to many other programming languages, YCP variables can be defined at (almost) any point in the code, namely between other statements. Given that, there must be some rules regarding the creation, destruction and validity of variables. Generally variables are valid (accessible) within the block they are declared in. This also covers nested blocks that may exist in this current block. The valid program region for a variable is called a scope.
Example 3.27. Variable scopes and blocks
{ // Declared in the outer block integer outer = 42; { // Declared in the inner block integer inner = 84; // This is OK. // Log: ...IN: inner: 84 - outer: 42 // y2milestone("IN: inner: %1 - outer: %2", inner, outer); } // This yields an error because "inner" is not defined any more. // y2milestone("OUT: inner: %1 - outer: %2", inner, outer); } |
Additionally to the structural language elements described so far, there are special commands that apply to lists and maps. What is special about these commands is that they apply an expression to the single elements of a list or map. This is done in a functional manner, i.e. the expression to be applied is passed as a parameter. Generally this executes faster than a procedural loop because the internal functionality is realized in a very effective way.
Furthermore some of these commands create lists from maps, maps from maps, maps from lists etc., so that they can be used to avoid the cumbersome assembling of these compound data types in a procedural loop.
Synopsis (list): any foreach( type variable, list<type>, (expression) );
Synopsis (map): any foreach( type_key variable_key, type_value variable_value, map<type_key, type_value>, (expression) );
This statement is a means to process the content of list or map in a sequential manner. It establishes an implicit loop over all entries of the list or map thereby executing the given expression with the respective entries. With lists the variable is a placeholder for the current entry. With maps, variable_key and variable_value are substituted for the respective key-value-pair.
Note 1: FIXME: Typing
Note 2: The return value of the last execution of expression determines the value of the whole foreach() statement.
Synopsis: map<type1, type2> listmap( type3 variable, list<type3>, (expression returning map<type1, type2>) );
This statement is a means to process the content of list in a sequential manner. It establishes an implicit loop over all entries in list thereby executing the given expression with the respective entry. During execution variable is a placeholder for the current entry. For each element in list the expression is evaluated in a new context. The result of each evaluation MUST be a map with a single pair of key-value. All the returned key-value-pairs are assembled to form the new map that is returned.
Note: FIXME: Typing, break, continue
Example 3.29. listmap() statement
{ // This results in $[1:"xy", 2:"xy", 3:"xy"] // map<integer, string> m1 = listmap(integer s, [1,2,3], ( $[s: "xy"])); // This results in $[11:2, 12:4, 13:6] // map<integer, integer> m2 = listmap(integer s, [1,2,3], { integer a = s+10; integer b = s*2; list ret = [a,b]; return(ret); }); y2milestone("map 1: %1 - map 2: %2", m1, m2); } |
Synopsis (map): list<type1> maplist( type2 key, type3 value, map<type2, type3>, (expression returning type1) );
Synopsis (list): list<type1> maplist( type2 variable, list<type2>, (expression returning type1) );
This statement is a means to process the content of map or list in a sequential manner. It establishes an implicit loop over all entries in map or list thereby executing the given expression with the respective entries. With lists the variable is a placeholder for the current entry. With maps, key and value are substituted for the respective key-value-pair. For each element the expression is evaluated in a new context. All return values are assembled to form the new list that is returned.
Note: FIXME: Typing, break, continue
Synopsis: map<type1, type2> mapmap( type3 key, type4 value, map<type3, type4>, (expression returning map<type1, type2>) );
This statement is a means to process the content of map in a sequential manner. It establishes an implicit loop over all entries in map thereby executing the given expression with key and value substituted for the respective key-value-pair. For each map element the expression is evaluated in a new context. The result of each evaluation MUST be a map with a single pair of key-value. All the returned key-value-pairs are assembled to form the new map that is returned.