– Typee Documentation –
3 – Typee Language Description
3.6 – Simple Statements
We describe here the specification of simple statements, i.e. statements that can be or are written on a single line of code and that are all ended by a semi-colon, which is a regular separator between statements in Typee.
Here is the formal EBNF specification of simple statements in Typee:
<simple statement> ::= ( <access protection statement> | <assert statement> | <delete statement> | <ensure statement> | <file endianness> | <file flushing> | <flow statement> | <import statement> | <nop statement> | <raise statement> | <require statement> ) <simple statement end> <simple statement end> ::= ';'
We explain each of those statements in next sub-sections.
3.6.1 Access Protection statement
Access protection is traditionnally associated with object oriented programming and classes implementation, for which methods and attributes may get a protected or even a forbidden access either externally to the class or to inheriting classes also. This concept is specified in C++ as well as in Java which extends it to packages. It is not totally ignored in Python, where everything is public except entites for which identifiers are prefixed with two successive underscores (said to be protected, but with a forbidden-external access meaning).
Typee extends the access protection concept to modules. Every declaration in a file may get access protection. The declared entities will then be accessible to external modules. There are two ways for the declaration of access protection in Typee. It is either specified within the declaration itself or declared at statement level and set for all the next declarations appearing after this statement.
We present here the access protection statements only. Individual access protection in declaration statements will be seen later in this documentation. The formal EBNF specification for access protection statements is:
<access protection statement> ::= ':' <public> | <protected> | <hidden> ':' <public> ::= 'public' <protected> ::= 'protected' <hidden> ::= 'hidden' | 'local' | 'private'
Keyword hidden
might sound strange to experienced programmers who are more used to use private
for this. This is why Typee accepts also keyword private
for highly protected entities. Meanwhile, programmers are strongly encouraged to use keyword hidden
in Typee. This keyword really means that no entity external to the class is supposed to see these declarations and that, as such, they are not allowed to get access to them. Since this level of protection is also available within modules, Typee accepts also keyword local
, which is sometimes used in other programming languages but for other while quite similar purposes. These three keywords all get the same effects. Every declaration appearing after them will be not accessible to any external entities out of the module.
3.6.2 Assert statement
The assert
statement evaluates a conditional expression (i.e. an assertion) and raises an exception if the expression evaluates to false
.
EBNF formal specification of this statement is:
<assert statement> ::= 'assert' <expression> (',' <expression>)*
This is useful when some value (of a function or method argument, for instance) has to be checked at run-time. The first expression appearing after keyword assert
is the conditional expression to be evaluated. The next expressions, separated by commas, are the arguments to be passed to the raised exception.
When exceptions will be fully described, later in this document, we will explain what they are, how they are instantiated when they are raised and what are the arguments passed to their constructors. With the assert
statement, an AssertException
is instantiated with, as its sole argument, a string (either str
or str16
) which defaults to the empty string if no second expression is set in the assert
statement. Typee nevertheless accepts a list of argument expressions right now because this may be useful in the future. Up today, passing more than one argument expression to an assert
statement will lead to a warning set by Typee translators.
3.6.3 Delete statement
In Typee, the delete
statement is used to delete objects and items from memory. Two keywords are accepted for this statement. They get the exact same results. They are delete
and its shorter wrapper del
which will, for sure, be preferred by Python programmers.
The formal EBNF specification is:
<delete statement> ::= ('del' | 'delete') <targets list> <targets list> ::= <target> (',' <target>)* <target> ::= <dotted name> (<subscription or slicing>)* <subscription or slicing> ::= '[' (<subscription> | <slicing>) ']'
When applied to an instance of a class (i.e. an object), this statement calls the descructor of this class and applies it to this instance. When applied to an indexed item or to a sliced group of items of a container, this statement suppresses this item or these items from within the container. Finally, when applied to the identifier of a container, the whole content of this container is suppressed from memory.
Typee translators will keep track of references to items, objects and containers at translation time. They will do their best to “nullify” any reference to finally deleted objects, items or containers. Programmers are nevertheless warned that there might be situations when this tracking will not be possible to ensure, and that creating references to objects has to be done cautiously in case of later deletes of the original element. Do not be too much confident in others’ softwares. Typee translators are others’ softwares.
3.6.4 Ensure statement
The ensure
statement evaluates a conditional expression and raises an exception if the expression evaluates to false
. It is used in contract programming and it has a slightly different behavior when compared with assert
statement.
EBNF formal specification of this statement is:
<ensure statement> ::= 'ensure' <expression> (',' <expression>)*
Contract programming defines two statements, require
and ensure
. Statement require
verifies that a condition is true
when entering some part of code – for instance, a function or a loop entrance. Statement ensure
verifies that a condition is true
when exiting some part of code – for instance, after the processing of a loop.
These contract programming statements may be considered similar to the assert
statement. According to their functionalities, they truly are BUT, while asserts are always processed, statements require
and ensure
are only processed when in debug mode. Once a software has been validated, there is no more needs to check again for contract programming.
About statement ensure
: this statement should appear at the end of the code to be checked as ensuring a condition – for instance, verifying some constant condition within a loop. It raises an exception of type EnsureException
. Associated with the instantiated exception are a message string – the first expression next to keyword ensure ensure, and a list of other arguments which up today are not used for the base class EnsureExpression
but which could be used in some future. Up today then, passing more than one argument expression to an ensure
statement will lead to a warning set by Typee translators.
3.6.5 File Endianness
File endianness has to do with the order bytes are put in or read from file when storing or loading groups of bytes. When storing and reading multi-bytes values on permanent storage, the default mode in Typee is Big Endian. It can be specified with unary operators <
and >
for resp. little- and big- endian.
Little Endianness: Next read and write operations will get and put first highest significant bytes and end with lowest significant bytes (i.e. little-at-end).
Big Endianness: Next read and write operations will get and put first lowest significant bytes and end with highest significant bytes (i.e. big-at-end).
The EBNF formal specification of the related syntax is:
<file endianness> ::= ( <little endian> | <big endian> ) <expression> ( ( '<<' | '>> | '>>>' ) <expression> )* <little endian> ::= '<' <big endian> ::= '>'
Example:
with file( 'filepath.data', 'rw' ) as my_file { uint32 val; < my_file.write( 0x12_34_56_78 ); // little endian my_file.rewind(); > my_file.read( val ); // big endian // val contains now 0x78_56_34_12 }
See dedicated sub-section at related section on files in this documentation.
3.6.6 File Flushing
Operating Systems (OS) optimize disk accesses which are very slow. OS manage intermediate buffer where written data is first buffered, waiting for its final writing on disk. Forcing the writing of data in file is an operation names flushing. It ensures that all recently written data on file has finally been stored on the physical media. In Typee, it is specified by unary operator !
.
The EBNF formal specification if Typee grammar related rules is:
<file flushing> ::= '!' <dotted name> [<file flushing'>] <file flushing'> ::= '(' <expression> [<file flushing''>] ')' | '[' <expression> ']' '=' <expression> | '>>' <expression> | '>>>' <expression> <file flushing''> ::= ',' <expression> <file flushing'>
Example:
with file( 'some_path', 'wo' ) as my_file { ! my_file.write( 'some text' ); // writes first the string, then flushes the file
3.6.7 Flow statements
Flow statements are statements which interrupt the flow of the processing, redirecting it elsewhere in the code. These statements are not branching. Branching keeps the flow of processing continuous, it does not stop it.
In Typee there are four flow statements. Their EBNF formal specifications are:
<flow statement> ::= 'break' | 'continue' | <raise statement> | <return statement> <raise statement> ::= 'raise' <expression> ['from' <expression>] <return statement> ::= ('ret' | 'return') <expr list>
We’ve already described keywords break
and continue
when describing the syntax of loops (for
, forever
, while
and repeat
).
break
exits from the loop it is in. Processing jumps to the first statement after the block of statements of the loop.
continue
restarts the processing at the first statement contained in the loop after having iterated. Any statement placed after continue
is ignored.
The return
statement can only be placed in functions and methods bodies (i.e. statements block). This immediately interrupts the processing of the function or of the method and returns the processing back to the caller statement. Two keywords are accepted in Typee for this: return
and its shorter form ret
. This keyword may be associated with a list of expressions or with a single expression. This is not mandatory. If absent, then the embedding function or method has to be declared as returning no value (this is sepcified by keyword none
). If present, the expression or the succession of expressions have to conform the types that have been specified at the declaration statement of the function or method. Type checking takes place within every Typee translator. Errors will be set if type checking fails between the declared returned types and the types used with the return
statement.
Statement raise
is associated with exceptions. This statement raises exceptions at the discretion of programmers. If this statement is processed in an environment where exceptions are caught, the associated raised exception will be caught and processed accordingly. The expression next to keyword raise
should be the constructor of an Exception or an instance of an exception class. It may be followed by keyword from
and another exception expression. This secondary information may help producing more precise errors messages when raising exceptions. In Python, this is used to chain exceptions. This is its use also in Typee.
3.6.8 Import statement
Import is… important. We will fully discuss this topic later in this documentation. It allows for the importation of declared classes, variables, functions, etc. from a module (i.e. a source file) into another module. We just explain here the syntax of imports in Typee. Python and Java programmers will get no surprise with it. To the attention of C++ programmers: think include
and forget double quotes!
The formal EBNF specification of statement import
is:
<import statement> ::= <import name> | <import from> <import name> ::= 'import' <dotted as names> <import from> ::= 'from' ('.')* <dotted name> 'import' ( 'all' | '(' <import as names> ')' | <import as names> ) <import as names> ::= <import as name> (',' <import as name>)* <import as name> ::= <identifier> ['as' <identifier>] <dotted as names> ::= ('.')* <dotted as name> (',' ('.')* <dotted as name>)* <dotted as name> ::= <dotted name> ['as' <identifier>] <dotted name> ::= <identifier> ('.' <identifier>)*
A first version of import
statement imports the whole content of a module. This is the <import name>
grammar rule. The <dotted as names>
rule that is associated with it declares paths to the modules for which content is to be imported. Each dot in <dotted name>:
indicates a level in the directories hierarchy of the program. The prefixing dots each figure a level up in this hierarchy of directories: one prefixing dot, one level up. Each intermediate identifier within the chain of identifiers seperated by dots is the name of a directory. The final identifier in the dotted chain is the name of the imported module without its suffix (either ‘.ty
‘ or ‘.typee
‘. Typee allows the aliasing of every imported module with keyword as
. Such aliases may be useful to shorten some modules when or to rename modules which get same name while not belonging to the same directory (i.e. package): when importing the whole content of a module, its content identifiers of variables, functions, classes and the like have to be prefixed with the name of this module.
A second version of import
statement imports parts only of an external module. This is the <import from>
grammar rule. This statements starts with keyword from
which precedes the path name to the imported module. This path is of the same form than paths to modules described in the above paragraph: they are dotted names with dots representing a level up or down in the directories hierarchy. The imported entities (classes names, variables identifiers, cnostant values identifiers, etc.) from this module are then listed, separated with commas, maybe enclosed between parenthesis, after keyword import
. Those names may each be aliased, either to be shorten or to be differnciated with other same names imported from other modules.
Well, that’s it. Let’s delay imports discussion for now. This was just a presentation of the syntax of imports in Typee. We will fully discuss modules importing in section 6. of this documentation – Typee Programs.
3.6.9 No-Operation statement
There may be times when no operation is to take place. This may be used, for instance, when further debugging will need the setting of breakpoints or during development progression when some parts of the code are not yet available while the global skeleton of its architecture has been defined.
Typee specifies the no-operation statement – as does C++ and Java with the empty instruction (a single semi-colon), Python with statement pass
or most of assemblers with instruction nop
. The two last styles are accepted in typee as explained here below.
<nop statement> ::= 'nop' | 'pass'
The first style (i.e. single semi-colon) is also accepted in Typee, as is explained in next section of this documentation on blocks of statements.
3.6.10 Require statement
The require
statement evaluates a conditional expression and raises an exception if the expression evaluates to false
. It is used in contract programming and it has a slightly different behavior when compared with assert
statement.
EBNF formal specification of this statement is:
<require statement> ::= 'require' <expression> (',' <expression>)*
Contract programming defines two statements, require
and ensure
. Statement require
verifies that a condition is true
when entering some part of code – for instance, a function or a loop entrance. Statement ensure
verifies that a condition is true
when exiting some part of code – for instance, after the processing of a loop.
These contract programming statements may be considered similar to the assert
statement. According to their functionalities, they truly are BUT, while asserts are always processed, statements require
and ensure
are only processed when in debug mode. Once a software has been validated, there is no more needs to check again for contract programming.
About statement require
: this statement should appear at the entrance of the code to be checked as requiring a condition before being processed – for instance, verifying some entering condition within a loop. It raises an exception of type RequireException
. Associated with the instantiated exception are a message string – the first expression next to keyword ensure ensure, and a list of other arguments which up today are not used for the base class RequireExpression
but which could be used in some future. Up today then, passing more than one argument expression to an require
statement will lead to a warning set by Typee translators.
Next section formerly explains blocks of statements.