– Typee Documentation –

3 – Typee Language Description

3.5 – Compound Statements

Let’s first explain what are the different categories of statements in Typee.

<typee statement> ::= <compound statement>  |
                      <empty statement>     |
                      <simple statement>    |
                      <statements block>

We first describe here the specification of compound statements, i.e. statements that are composed of many keywords.

Here is the formal EBNF specification of compound statements in Typee:

<compound statement> ::= <assign decl def func-call statement>  |
                         <embed statement>                      |
                         <exclude statement>                    |
                         <for statement>                        |
                         <forever statement>                    |
                         <if statement>                         |
                         <repeat statement>                     |
                         <switch statement>                     |
                         <try statement>                        |
                         <while statement>                      |
                         <with statement>

We explain each of them in next sub-sections, classic statements first, specific to Typee ones then and finally the assignment, declaration, definition and function call statements which are specified the most voluminous rules of Typee grammar.

3.5.1 for statement

The for statement creates an iteration over a list of typed items and processes its first block of statements for each of the items contained in the list. A second block of statements may be defined after a specific keyword, otherwise.
Python programmers: caution! Keyword otherwise in Typee does NOT behave as keyword else after for in Python – see explanations below.

Formal EBNF specification is provided below in a simplified form to be easier to understand

<for statement>      ::= 'for' '(' <typed targets list> 
                               'in' <indexed iterables list> ')'
                               <statements block>
                               ['otherwise' <statements block>]

<typed targets list> ::= <typed target> (',' <typed target>)*

<typed target>       ::= <type> <dotted name> (<subscription or slicing>)*

<indexed iterables list> ::= 'indexed' '(' <iterables list> ')'  |
                             <iterables list>
<iterables list>     ::= <iterable expression>  ( ',' <iterable expression> )*

<iterable expression>::= <container or iterable object>

The expression following keyword in has to be iterable. This means that it should be a container or an instance of an interable class – more about iterable classes later in this documentation.

A specific keyword, indexed, is used to get an integer index associated with the iterated returned value from the container or from the iterable object. Python programmers will understand this as keyword enumerated in their favorite programming language. See example for illustration below:

for( uint8 index, uint8 val in indexed [1, 2, 3, 5, 8, 13, 21, 34, 55, 89] )
    print( index, val );
// prints:
// 0 1
// 1 2
// 2 3
// 3 5
// 4 8
// 5 13
// 6 21
// 7 34
// 8 55
// 9 89
// did you recognize a part of the Fibonacci suite?

Fibonacci Spiral
(credit: reddit.com)

If no item is finally processed, the block of statements after keyword otherwise is processed.
If at least one item has been processed by the ‘for’ block of statements, the ‘otherwise’ block of statements is not processed.
Python programmers, CAUTION: this is not the behavior of controversial keyword else in Python when associated with for.

Three flow keywords can be used within the block of statements of for: break, continue and return.

break immediately stops the processing within the statements block of for and exits from it. If an otherwise statements block is present, it is not processed too.

continue skips the processing of the statements below it into the for statements block and processing gets back to the start of this block, for a next iteration within the for loop.

return may be used when the loop is present in the statements block of a function or of a method. The loop processing stops then immediately and control is given back to the caller of the function or method.

3.5.2 forever statement

The forever keyword implements infinite loops. While this may sound strange, it avoids programming things like while(true) and there are ways to exit such loops (i.e. break and return).

The formal EBNF specification is:

<forever statement> ::= 'forever' '(' ')' <statements block>

Notice: there is no otherwise alternative associated with forever loops.

Typee translators verify the presence of exiting keywords in infinite loops and raise a warning if none of them is detected within the block of statements.

Exiting keywords are break, and return when the the loop is within a function or a method body. They act exactly as for for loops.

continue may also be used in forever loops and acts exactly as with for loops.

3.5.3 if statement

Conditional branching in Typee is very classical. Its formal EBNF specification is:

<if statement>       ::= 'if' <parenthesised expr> 
                             <statements block>
                         <elseif statement>
                         [<else statement>]

<elseif statement>   ::= ( <elseif>  <parenthesised expr> 
                             <statements block> )*

<else statement>     ::= ('else' | 'otherwise') <statements block>

<elseif>             ::= 'elseif' | 'elsif'  | 'elif'

<parenthesised expr> ::= '(' <expression> ')'

The parenthesised expressions are conditional expressions. They return either true or false.

If the conditional expression after the if evaluates to true, the statements block of the if is processed.
If this conditional expression evaluates to false, the if statements block is ignored and the first elseif conditional expression is evaluated.
This process is repeated along the next elseif clauses if present.
If none of the preceeding conditional expression evaluate to true, the block of statements asociated with else is processed, if present. A valid alternative to clause else is clause otherwise. This is unusual in usual programming languages, but it makes sense and consistency in Typee with the meaning of clauses otherwise in other statements while, for, try and switch.

During this whole process, the first clause that evaluates to true stops the processing. Its block of statements is processed and processing goes then straight to the first statement after the whole if statement.

3.5.4 repeat statement

This type of loop first processes its statements block then evaluates a conditional expression and stops looping as soon as the conditional expression evaluates to true.

This is its formal EBNF specification:

<repeat statement> ::= 'repeat' <statements block>
                       'until' <parenthesised expr> ';'

The parenthesised expression is a conditional expression. It evaluates to either true or false.

As for any type of loops, three flow keywords can be used within the block of statements of repeat: break, continue and return.

break immediately stops the processing within the statements block of repeat and exits from it.

continue skips the processing of the statements below it into the repeat statements block and processing gets back to the start of this block, for a next iteration within the repeat loop.

return may be used when the loop is present in the statements block of a function or of a method. The loop processing stops then immediately and control is given back to the caller of the function or method.

3.5.5 switch statement

The switch statement in Typee does not act exactly as a traditional switch. Let’s explain this that way: a switch statement in Typee evaluates an expression, checks it against values associated with cases and runs the statements block associated with all the corresponding cases this value appears in.

The formal EBNF specification for switch is:

<switch statement> ::= 'switch' <parenthesised expr>
                           '{' <switch block> '}'
                           ['otherwise' <statements block>]

<switch block>     ::= (<case> [<switch block>])*

<case>             ::= 'case' <expr list> <statements block>
<expr list>        := <expression>  ( ',' <expression> )*

Since a same value may appear in many case clauses, many blocks of statements may be processed within the switch statement. We do understand that this may lead to code that could be a little bit more difficult to read, but it surely will shorten the code also, factorizing it when possible.

When processing a switch statement, the associated parenthesised expression is first evaluated. It gets a value which may be of any type. Each case clause is checked then, searching for the evaluated value within the list of expressed values associated with it. The expressed values have not to be literals, as it may have to be with other programming languages. Every time the evaluated value of the swicth expression matches with at least one of the expressed values of the case clause, the block of statements corresponding to this clause is processed.

Notice: Typee switch statement is not specified to finally optimize the processor generated binary code, while this is the case in C or C++ for instance. Nevertheless, Typee translators aim at optimizing the source code they generate for it to take as much as possible benefits from the final targeted programming language they are translating Typee code to.

3.5.6 try statement

Typee manages exceptions. Exceptions are faulty execution of code, such as division by 0 or out-of-bounds indexing, for instance. Typee uses a Python-like naming. C++ and Java programmers should nevertheless get no difficulty to understand and to use this concept.

The formal EBNF specification in Typee is this:

<try statement>   ::= 'try'   <statements block>
                          <try except>     <statements block>
                          (<try except>    <statements block>)*
                          [<try otherwise> <statements block>]
                          [<try finally>   <statements block>]

<try except>      ::= 'except' '(' [ (<try except expr> | 'all' ) ] ')'
<try except expr> ::= <expression> ['as' <identifier>]
                          (',' <expression> ['as' <identifier>])*

<try otherwise>   ::= 'otherwise'

<try finally>     ::= 'finally'

The keyword try tags a block of statements in which any raised exception will be caught. As soon as an exception is raised by some statement in this block, the execution of the statements in this block stops and the chain of except clauses is checked against the type of the raised exception.

Each except clause specify one or many types of exceptions for which their associated block of statements will be processed. The keyword as names the raised exception which can then be addressed in the statements block after it.

If the type of the raised exception matches the type expressed in an except clause, the statements block associated with this except clause is immediately processed.

If none of the exceptions types expressed in the chain of except clauses matches the raised exception, then the otherwise clause is used and its associated block of statements is processed, if this clause is present.

If a finally clause is present, the raised exception having been caught by a previous except clause or not, the statements block associated with clause finally is processed. This means that when a finally clause is present, it is always processed.

The Exceptions concept will be fully explained later in this documentation. For the sole purpose of describing the related syntax, the EBNF specification should be enough to provide.

3.5.7 while statement

The while first evaluates a conditional expression. If it evaluates to true, the block of statements associated whith the while keyword is processed and processing gets back to the evaluation again of the conditional expression.

Here is a formal EBNF specification of it:

<while statement> ::= 'while' <parenthesised expr> <statements block>
                          ['otherwise' <statements block>]

The parenthesised expression above is a conditional expression which evaluates either to true or to false.

While the conditional expression evaluates to true, the block of statements is processed. As soon as it evaluates then to false, processing is kept on at the first statement after the block of statements.

If the conditional expression evaluates to false the very first time it is evaluated, the block of statements associated with the keyword otherwise is processed, as long as this clause appears after the while-related block of statements.
Python programmers, CAUTION: this is not the Python behavior of controversial keyword else put after a while.

3.5.8 with statement

The with concept of Typee is directly inherited from Python syntax. This Python goodie is so useful that we have decided to specify it in Typee also.

This concept will be fully explained later in this documentation. Let’s just see its syntax for now. Its formal EBNF specification is:

<with statement>  ::= 'with' <with items list> <statements block>

<with items list> ::= <with item> (',' <with item>)*
<with item>       ::= <expression> ['as' <target>]

After keyword with, at least one entity is declared as an expression. Each of these declared entities may be associated with an alias, or wrapper: an identifier that will be locally known in the block of statements which is processed just after the with clause.

3.5.9 embed statement

This is a key concept in Typee associated with keyword exclude. When translating Typee code into some targeted programming language, and since Typee code aims at being generic enough, it might be that some specificities of the targeted programming language are not available with Typee. The embed statement lets programmers embed native code in Typee code.

A semi-formal EBNF specification of its syntax is:

<embed statement>      ::= 'embed' <languages>
                               ( <dotted name> ';' | <embedded native code> )
                               ['exit']

<language>             ::= 'android' | 'java' | 'cpp' | 
                            'cs' | 'csharp' | 'py' | 'python'

<languages>            ::= <language> ( ',' <language> )*

<embedded native code> ::= '{{' (<any code chars but '}}'>)* '}}'

This is not a truly formal LL(1) EBNF specification, but trust us, it can very easily be transformed in a legal one. The advantage of its formulation this way is that it is far easier to understand.

Of course, native code will be put as is in the finally generated source code files. Then, it gets also access to every declared entity locally known at the embed clause level. Python programmers will have to take care of indentation within embeded code. They should be multiple of 4 spaces each (currently not configurable).

Keyword exit can be added at the end of the statements block of an embed clause. The use of this is that, when translating a Typee portion of code that contains embedded native code, once this native code has been put as-is in the created translation file nothing after this exit will be inserted in the created file until the end of the currently translated portion of Typee code.
See example of use in Typee built-in library Thread, for instance. You will find this extensively used in module base_thread.ty for instance.

You will have noticed that android is an allowed language. Well, Android is not a programming language but it is still defined as being one just to distinguish Java and Java for Android. You will find examples of its use in Typee built-in libraries – see module time.ty in library Time, for instance.

Let’s provide a few examples.

const uint32 NB = 16;
array<int32>[NB] my_data;

embed cpp {{
for ( auto it = my_data.begin(); it != my_data.end(); ++it )
    *it = 0;
}}

embed java, android {{
for( int i=0; i < NB; i++ )
    my_data[i] = 0;
}}

embed python {{
my_data = [0]*NB
}}

my_data[1] = 1;
print( my_data );  // prints 0 1 0 ... 0
const int32 NB = 16;
array<int32>[NB] my_data;

embed cpp     zero_set.cpp;        // (all of these  native  code
embed java    zero_set.java;       //  source files being present
embed python  zero_set.py;         //  in the 'current' directory)

my_data [1] = 1;
print( my_data );  // prints 0 1 0 ... 0

3.5.10 exclude statement

This concept is the counterpart of embed. It excludes some Typee code from being translated into code of specified translated language. It can be used in two ways: either directly in Typee code or right after the signature of a function in its definition.

3.5.10.1 exclude in Typee code

A formal EBNF specification of its syntax is:

<exclude statement> ::= 'exclude' <languages> '{{' <statements list> '}}'

<language>          ::= 'android' | 'cpp' | 'java' |
                        'cs' | 'csharp' |
                        'py' | 'python'

<languages>         ::= <language> (',' <language>)*

<statements list>   ::= <statement>  (<statement>)*

The main use of this is when some native code is embedded for a targeted programming language while the equivalent code is to be programmed in Typee, to be translated into other targeted programming language for which no native code can be directly equivalent.

Example:

none zero( list[] l, const uint32 n ) {
  // sets list l with n items with value zero
  // for sole Python
  embed python {{
    l = [0]*n
  }}

  // for any targeted programming language but Python
  exclude python {{
    l = [];
    while( n != 0 ){
        l.append( 0 );
        --n; // could have been put in while( n-- != 0 )
    }
  }}
}

In the above example, either function zero() is translated in Python, in which case the sole embedded Python code will be put in the generated module code, or function zero() is translated in any other programming language than Python, in which case the Typee code contained in the Python exclude-d block will be translated.

Notice: in the case of translating this code into Python, it may be that Typee optimizer will automatically “inline” the code ot function zero() directly in place in modules where this function is called – performance and memory space optimizations here.

3.5.10.2 exclude in function definition

We know only one use case for this usage of exclude. May be you wilil find other ones… We have introduced this use in function definition to exclude its translation into a targeted language when coding an interface layer in a Typee library above some already existing libraries in different targeted languages.

The formal EBNF description of the Typpe syntax is this one

<function definition> ::= <function decl> ['exclude' <languages>] 
                               <statements block>

<function decl>       ::= [<access protection qualifier>] ['final']
                              <type> <identifier>
                              [<template def>] <function args declaration>

<language>            ::= 'android' | 'cpp' | 'java' |
                          'cs' | 'csharp' |
                          'py' | 'python'

<languages>           ::= <language> ( ',' <language> )*

It aims at exclude from code translation any method or function that would already exist with exact same signature in a targeted language. Difficult to explain, easy to understand with an example.

This is part of the code of Typee built-in library Thread. Java library Thread already defines method is_daemon() while Python names it another way.

const bool is_daemon() exclude java
/**
Returns true if this thread is daemonic, and false otherwise.
*/
{
  embed py {{
    return self.daemon
  }}
}

Here, the Typee signature is available for any Typee code, but at translation time into Java, the already existing method in Java will be named in the translated code and no other translation from Typee to Java will take place. For any other targeted language, the related translators will attempt to translate the block of Typee statements of the method.

 
Next section formerly explains the Typee simple statements.

< previous (3.4 identifiers & expressions) | (3.6 simple statements) next >