Chapter 6. Instructions

Table of Contents
Constant declaration
Variable declaration and assignment
Flow control
if instructions
if then else
if is null
repeat instructions (loops)
repeat for each
repeat while
repeat times
repeat from to
repeat forever
exit repeat
next repeat
case instructions
case type of
case enumerated of
case value of
case reference of
return
Resource handling instruction
use resource
Error handling instructions
assert
assert is null
check
error
Section instructions
section
exit section
Event handling instructions
generate event
on event
stop event handler
Embedded Java instructions
Unit testing instructions
test
verify
verify_error

Constant declaration

A constant is introduced with the const keyword.

A simple constant declaration looks like this:

const name string = "Bob"

Type inference is supported. If the constant's type is not explicitly declared in the source code then the compiler infers it from the expression at the right of the = symbol. Hence the above instruction is equivalent to the following one:

const name = "Bob"

After a constant has been declared, no other object can be assigned to it (i.e. writing name = "alice" after the above instruction is invalid). That doesn't mean, however, that the object's data can't change (e.g. a customer's name attribute can change after the customer object has been assigned to a constant).

The scope of a constant is limited to the block of instructions in which it is declared.

Variable declaration and assignment

A variable is introduced with the var keyword.

A simple variable declaration looks like this:

var counter zero_positive_32 = 0

As for constants, type inference is supported. Hence the above instruction is equivalent to:

var counter = 0

A variable can be declared without assigning an initial value:

var counter zero_positive_32

After a variable has been declared (and optionally initialized with a value), another object can be assigned to it:

counter = counter + 1

The scope of a variable is limited to the block of instructions in which it is declared.

Flow control

if instructions

if then else

There are three kinds of if instructions:

  1. if then

    if the_sun_shines then
       message = "Antonio is very happy."
    .
  2. if then else

    if the_sun_shines then
       message = "Antonio is very happy."
    else
       message = "Antonio is happy."
    .
  3. if then else if else

    const user_input string = \
       OS.console?.ask_string ( "Please enter a single digit: " ) \
       if_null: error
    
    const first_character = user_input.first
    
    var message string
    
    if user_input.size =v 1 and first_character.is_digit then
       message = "Very well done!"
    else
    
       if user_input.size #v 1 then
          message = "Please enter just one digit."
    
       else if first_character.is_letter
          message = "Please don't enter a letter."
    
       else if first_character.is_space
          message = "Please don't enter a space."
    
       else
          message = "Please enter a digit (0..9)."
       .
    .
    
    OS.out.write_line ( message )

if is null

Not written yet.

repeat instructions (loops)

There are five kinds of loops

repeat for each

Used to iterate over a collection.

Example:

// display continents

repeat for each continent in ["Africa", "America", "Antarctica", "Asia", "Australia", "Europe" ]
   OS.out.write_line ( continent )
.

repeat while

Used to loop while a given condition is true.

Example:

// ask user to enter at least 3 characters on the system console

var user_input = "?"
repeat while user_input.size < 3
   user_input = OS.console?.ask_string ( "Please enter at least 3 characters:" ) if_null: error
.

repeat times

Used to loop a predefined number of times.

Example:

// display "Hello" 3 times

repeat 3 times
   OS.out.write_line ( "Hello" )
.

repeat from to

Used to loop with a variable whose value is incremented or decremented at each iteration.

Example:

// display even numbers from 2 to 10

repeat from i = 2 to 10 step 2
   OS.out.write_line ( i.to_string )
.

repeat forever

Used to loop forever; can only be exited with the exit repeat instruction.

Example:

// check for a new message once a second
// exit if message is "exit"

repeat forever
   const message nullable string = get_new_message_in_inbox

   if message is not null then
      OS.out.write_line ( message )
      if message =v "exit" then
         exit repeat
      .
   .

   wait_utilities.wait_seconds ( 1L )
.

Within each loop you can exit the loop or skip to the next iteration with the following instructions:

exit repeat

Used to exit a loop immediately.

No example yet.

next repeat

Used to continue immediately with the next iteration.

No example yet.

case instructions

There are four kinds of case instructions:

case type of

The case type of instruction is used to first determine the type of an expression at runtime and then execute a block of code that is selected based on the expression's type. In other words, the instructions to be executed depend on the expression's type.

Example: Suppose that type product has three sub-types: fruit, vegetable and book. And shopping_basket is a list<product> containing any numbers of fruit, vegetable and book objects. Then the following code will count and display how many products of each type are contained in the basket:

const shopping_basket list<product> = get_shopping_basket

var book_count, fruit_count, vegetable_count = 0

repeat for each product in shopping_basket

   case type of product
      when fruit
         fruit_count = fruit_count + 1
      when vegetable
         vegetable_count = vegetable_count + 1
      when book
         book_count = book_count + 1
   .
.

%write_line ( """Content of shopping basket:
fruit items    : {{fruit_count}}
vegetable items: {{vegetable_count}}
book items     : {{book_count}}""" )

Note that the compiler checks that each subtype of product appears exactly once in the case type of instruction (unless an otherwise clause is added at the end). Hence, if another sub-type is added later (e.g. game as sub-type of product) then a compilation error occurs if we forget to test for the new sub-type in the case type of instruction.

case enumerated of

The case enumerated of instruction is used to execute a block of code that is selected based on the value of an expression whose type is enumerated. In other words, the instructions to be executed depend on the enumerated value at runtime.

Example: Suppose the following enumerated type exists:

enum type product_size

   small, medium, large, "very large"
.

Then a coefficient used in calculating transportation costs could be computed as demonstrated in the following code:

const product_size = product_size.large

var coefficient float_64

case enumerated of product_size
   when small
      coefficient = 0.95
      %write_line ( "small product" )

   when medium
      coefficient = 1.0
      %write_line ( "medium-size product" )

   when large, "very large"
      coefficient = 1.2
      %write_line ( "large or very large product" )
.

assert coefficient is not null
assert coefficient =v 1.2

The compiler checks that each enumerated value of product_size appears exactly once in the case enumerated of instruction (unless an otherwise clause is added at the end). Hence, if another value is added later (e.g. "very small") then a compilation error occurs if we forget to test for "very small" in the case enumerated of instruction.

case value of

The case value of instruction is used to execute a block of code that is selected based on the value of an expression at runtime.

Example:

const console = OS.console
if console is null then
   return
.

console.ask_positive_32 () (
   const number = result
   const error = error )
if number is null then
   return
.

var message = "none"

case value of number
   when 1
      message = "1"
   when 2, 3
      message = "2 or 3"
   when <= 10
      message = "between 4 and 10"
   otherwise
      message = "greater than 10"
.

%write_line ( """The number you entered is {{message}}""" )

The case value of instruction can by used for any type that inherits from type equalable. All scalar types (e.g. string, number, character), as well as many other types in PPL's standard library inherit from equalable - so they can be used. You can also use your own types, as long as they inherit from type equalable (e.g. case value of vector_3d ...).

case reference of

The case reference of instruction is used to execute a block of code that is selected based on the object reference an expression points to at runtime.

Example:

const name_1 = "foo"
const name_2 = "bar"
const name_3 = name_1

var i = 0

case reference of name_3
   when name_2
      i = 1
   when name_1
      i = 2
   otherwise
      i = 3
.

assert i =v 2

The case reference of instruction is used rarely in practice, but, if used properly, it can lead to highly efficient code, because only pointers (instead of values) are compared. For example, comparing the values of two long strings (i.e. all the characters contained in the strings) can take a lot of time. Just comparing their pointers is much faster.

return

Not written yet.

Resource handling instruction

use resource

The rationale for the use resource instruction is to eliminate resource leaks. Resource leaks occur whenever the program does not release system resources it has acquired, such as open files, network or database connections, etc. They can lead to very nasty bugs that are difficult to detect and can crash an application or a whole system.

Look at the following code:

const writer = get_text_file_writer
// ...
writer.write_line ( "foo" )
// ...
writer.close

If we forget to close the writer then a resource leak occurs. But even if we correctly close the writer (as in the code above) a resource leak still occurs if a program error (exception) arises between opening and closing the file writer.

These two inconveniences disappear with the use resource instruction. The above code must be written like this:

use resource const writer = get_text_file_writer
   // ...
   writer.write_line ( "foo" )
   // ...
.

The compiler ensures that the writer is closed at the end (i.e. writer.close is executed implicitly), even if a program error occurs within the use resource block.

Error handling instructions

assert

Not written yet.

assert is null

Not written yet.

check

Not written yet.

error

Not written yet.

Section instructions

section

Not written yet.

exit section

Not written yet.

Event handling instructions

There are three instructions for handling events:

generate event

Used to generate a new event.

No example yet.

on event

Used to register an event handler.

No example yet.

stop event handler

Used to unregister a previously registered event handler.

No example yet.

Embedded Java instructions

Not written yet.

Unit testing instructions

test

Not written yet.

verify

Not written yet.

verify_error

Not written yet.