In butter a program is a list of statements basically. Each statement belongs to a certain function definition. Its not possible to put a statement outside of a function in butter. The most simple statement is the so-called void-expression statement. Other statements offer loop control and conditional execution. In general each statement in butter needs to be separated from the next with a ";" character, unless the statement ends with a block of code. Blocks of code are enclosed within the curly-braces "" and "{"
Optionally statements can be prefixed with a label. Such a label is referenced by other statements like the goto-statement. In case you want to do that you need to state the labelname and a following ':'.
printsomething ("hello"); // plain statement label1: printsomething ("hello"); // statement with label |
Here is a list of the currently available statements in butter:
Void expression statements like:
10; // just do a void 10, makes little sense 10, 20, 30; // all expressions in the list are evaluated a = 10 + 10; // the value 20 is assigned to the variable a b = 100, c = 200; // b is set to 100 and c to 200 b &= 128 + 64; // the result of (b binaryand 192) is assigned to b |
If and If-Else statements that look like this:
if (a < 0) a *= -1; // make a positive if (a >= 0) { /* if-code here */ } else { /* else code here */ } if ((a >= 0) && (b >= 0)) a *= -1; else b*= -1; |
While loops: A while loop is the most basic loop construct. The body of a while-loop is executed as long as the while-condition evaluates to a nonzero value.
/* snippet that computes 100 in variable b */ int a, b; a = b = 0; while (a < 10) { // check loop condition b += 10; // increment b by 10 a += 1; // advance loop counter } |
Do/While loops, similiar like while-loops, but the loop condition is checked after the loop body. That means loop body is executed one initial time no matter what. Subsequent runs will be done as long as the Do/While condition evaluates to a non-zero value.
int a; a = 0; do { a += 2; } while (a < 0); |
Until loops: Until loops are exactly like while-loops, except that their condition is inverted. The loops body is executed until the condition becomes true. Until loops can be rewritten as simple while loops.
int a; a = 0; until (a == 10) { a += 1; } // a is equal to 10 here |
int a; a = 0; do (!(a == 10)) { a += 1; } // ! inverts the condition // a is equal to 10 here |
Do/Until loops: Do/until loops correspond to do/while loops the same way that the until-loops do to the while-loops. The only difference between Do/until and its counterpart do/while is the negated condition.
For loops:
/* snippet that computes 100 in variable b */ int a, b; /* for command includes initialization, loop condition and increment here */ for (a = b = 0; a < 10; a += 1) { b += 10; // increment b by 10 } |
Return statements: In a function returning void, you can only have return statements without an expression. In a function not returning void, you must specify the returnvalue in a expression that is part of the return statement.
/* some simple functions include return statements */ int add (int a, int b) { return a + b; } // adds a and b and returns the result void donothing () { return; } // this function does nothing |
Declaration statements: Any variable needs to be declared before it can be used. The formal-arguments passed to a function are automatically declared in the function-signature, all other variables must be declared before they are used. This is done by declaration-statements.
/* some simple declarations */ listnode a; // declares a list node object reference int a, b; // declares two integer variables named 'a' and 'b' float temperature; // declares a single floating point variable |
Goto statements: Goto allows jumping to any place in within the current function. Its main purpose is to break out of deep nested loops and if constructs or to enter a loop in the middle. The place goto shall jump to must be a labeled statement.
int i, j; i = j = 0; while ((i += 1) < 10) { j = 0; while ((j += 1) < 100) { if (i == 5 && j == 55) goto out; // bail out of a deeply nested loop here } } out: prs ("behind the loop\n"); // target label for the above goto |
Almost program needs to react to certain dynamic conditions. Thats why you will find conditional-statements in any straight-forward programming-language. This one supports a if or if-else statement that behaves 100% like the ones you find in c, c++ or java.
Each if statement is followed immediately by an expression which must be enclosed in parentheses. The next statement or block of statements after this expression are executed only if the expression evaluates to a true value. In butter everything but the numerical value 0 or the null-pointer is considered to be true. After this statement (or statement block) there may be an optional else keyword. This keyword signals there is code to be executed if the condition did not evalute to a true value. The else-branch consists of a single statement or a block of statements, which is similiar to the code following the if statement's expression. Here are a few examples of if and if-else statements:
int result; if (1) { result = 100; } // result is always set to 100 if (1) result = 100; // equivalent to the above if (1) result = 100 else result = 200; // result is still always = 100 if (!1) result = 100 else result = 200; // result is set to // the following will test whether result is in the range between 20-80 if ((result >= 20) && (result <= 80)) { do_something(); /* result is inside the interval */ } else { react_to_error(); /* result is not within the interval */ } |
The if-expression can be any valid expression of the language and thus allows testing for several conditions, which can be concatenated with the logical operators like || (or), && (and) and ! (not).
Please note that any else statement belongs to the closest if statement appearing before it in the source. Note the following example:
Even though it is not possible to have statements other than declaration-statments outside of blocks, you can still put declaration- statements outside of any function (including the special init-block function, details will follow). These declarations declare global variables, which are only existing once and changes made to any of these will persist over the lifespan of functions invocations.
A typical example usage of globals could look like this:
int i, j; // // an init-block can be used to initialize globals. // note that an init-block is similiar to a function. // it is only run through once upon program invocation // though. // init { i = j = 10; j += i; prs ("globals initialized .. \n"); } // // a simple main function, printing out the values computed // above // void main () { prs ("i + j = "); pri (i + j); prs (" (is it 30?)"); nl (); } |
In case you want to initialize such global variables, you can make use of a init-block construct. This is also shown in the above example. Its best to think of the init-block like any other user-defined function, with the exception that its automatically invoked once at startup by the runtime implicitly. Thus it is a suitable place for initializing global variables.
The complement of the init-block is the exit-block. Its code is executed after all threads have terminated right before program termination.