THE PARVA PROGRAMMING LANGUAGE

P.D. Terry, Computer Science Department, Rhodes University

Fourth release (for CSC 301 practical course) - March 24, 2002


1 Introduction

Parva is a small toy programming language that has evolved from Clang (Terry, 1996), Java, JavaScript and C++ (Stroustrup, 1990). (Parva is the feminine form of the Latin adjective meaning "small".)

This report is not intended as a programmer's tutorial. It is intentionally kept concise. Its function is to serve as a reference for programmers, implementors, and manual writers. What remains unsaid is mostly left so intentionally, either because it is derivable from stated rules of the language, or because it would require a general commitment in the definition when a general commitment appears as unwise. This specification of Parva is modelled on the Modula-2 and Oberon specifications (Wirth, 1980, 1990).

Parva was developed as a possible vehicle for teaching elementary algorithmic concepts to beginners in 2001. Since it was expected that these students would move on ("up"?) to Java or C++ a compromise was attempted in several areas between the various syntactic feature of C, C++, Java and Javascript which are annoyingly different.


2 Syntax

A language is an infinite set of sentences, namely the sentences well formed according to its syntax. Each sentence is a finite sequence of symbols from a finite vocabulary. The vocabulary of Parva consists of identifiers, numbers, strings, operators, delimiters, and comments. These are called lexical symbols and are composed of sequences of characters. (Note the distinction between symbols and characters.)

To describe the syntax the variant of extended Backus-Naur formalism called Cocol/R is used. This is described in full detail elsewhere (Terry, 1996). Brackets [ and ] denote optionality of the enclosed sentential form, and braces { and } denote its repetition (possibly 0 times). Syntactic entities (non-terminal symbols) are denoted by English words expressing their intuitive meaning. Symbols of the language vocabulary (terminal symbols) are denoted by strings enclosed in quote marks (these include words written in lower case letters, so-called reserved key words).


3 Vocabulary and representation

The representation of symbols in terms of characters is defined using the ASCII set. Symbols are identifiers, numbers, string literals, character literals, operators, delimiters, and comments.

The following lexical rules must be observed: Blanks and line breaks must not occur within symbols (except that line breaks are allowed in comments, and blanks are allowed within string and character literals). They are ignored unless they are essential for separating two consecutive symbols. Capital and lower-case letters are considered as being distinct.

  CHARACTERS
    cr         = CHR(13) .
    lf         = CHR(10) .
    back       = CHR(92) .
    letter     = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" .
    digit      = "0123456789" .
    cntl       = CHR(0) .. CHR(31).
    noQuote1   = ANY - '"' - cntl - back .
    noQuote2   = ANY - "'" - cntl - back .
    graphic    = ANY - cntl .

  IGNORE CHR(9) .. CHR(13)
  COMMENTS FROM "//" TO lf
  COMMENTS FROM "/*" TO "*/"

Comments may be inserted between any two symbols in a program. They are arbitrary character sequences either opened by the bracket /* and closed by */, or opened by the bracket // and closed at the end of that line. Comments do not affect the meaning of a program.

  TOKENS
    identifier = letter { letter | digit | "_" } .
    number     = digit { digit } .
    string     = '"' { noQuote1 | back graphic } '"' .
    character  = "'" { noQuote2 | back graphic } "'" .

Identifiers are sequences of letters, underscores and digits. The first character must be a letter.

Examples:

     x   scan   Parva   Get_Symbol   firstLetter

Restriction: An implementation is free to restrict the number of significant characters in an identifier. Typically this will be at least 10.

Numbers are (unsigned) integers. Integers are sequences of digits, and have their usual decimal interpretation.

Restriction: An implementation is free to restrict the range of numbers that it can handle. The current implementation uses 32 bit arithmetic: the range is -2147483648 ... 2147483647.

String literals are sequences of zero or more characters or escaped character sequences enclosed in quote marks ("). The number of characters in a string is called the length of the string. Strings can only be used within I/O statements (see 10.8 and 10.9). A string literal may not extend over a line break in the source text.

Character literals are denoted by a single graphic character or a single escape character sequence between single quote marks ('). Character literals denote the integer value of the corresponding ASCII character.

Within a string or character literal the following escaped character sequences denote non-graphical characters

      \a         Alert            (CHR(7))
      \b         Backspace        (CHR(8))
      \t         Horizontal tab   (CHR(9))
      \n         Line feed        (CHR(10))
      \f         Form feed        (CHR(12))
      \r         Carriage return  (CHR(13))
      \"         Quotation mark   (CHR(34))
      \'         Apostrophe       (CHR(39))
      \x         (where x is not a, b, t, n, r or f) denotes x itself

(Within a string \n denotes the sequence Carriage return, Line feed, on MS-DOS systems.)

   Examples: "Parva will become big"  "He said\"hello\" and rang the bell\a"

Operators and delimiters are the special characters, character pairs, or reserved words listed below. The reserved words cannot be used in the role of identifiers.

    !       <       ,       bool            for              read
    |       <=      :       boolean         get              return
    ||      ==      ;       break           if               stackdump
    &       !=      <<      char            inline           true
    &&      >       >>      cin             int              val
    +       >=      [       const           mod              void
    -       %=      ]       cout            new              while
    *       &=      {       do              print            write
    /       *=      }       else            println          toUpperCase
    %       +=      (       enum            random           toLowerCase
    ++      -=      )       eof             randomseed
    --      /=              eoln
            |=              false
            =

Some of the keywords are synonyms - for example bool and boolean, print and write, get and read. Some will not be discussed further here and remain "undocumented features".


4 Programs and Methods

A program is defined by a set of method declarations and method definitions.

  ParvaProgram      = { MethodDeclaration | MethodDefinition } .
  MethodDeclaration = MethodHeader ";" .
  MethodDefinition  = MethodHeader Block .
  MethodHeader      = MethodType MethodIdentifier
                      "(" FormalParameters ")" .
  MethodType        = "int" | "char" | "boolean" | "void" .
  MethodIdentifier  = identifier .
  Block             = "{" { Declaration | Statement } "}" .

The distinction between a MethodDefinition and a MethodDeclaration is imposed by the necessity for the implementation to have each identifier in the program "declared" before it is "used". In many cases this is easily achieved by constituting a program as a set of MethodDefinitions, textually declared in an order that meets this requirement. In some situations - typically those in which mutually recursive functions need to invoke one another - this is impossible. The concept of the MethodDeclaration is akin to the "function prototype" of C++, and allows the compiler to resolve forward references to methods in a simple way. If a MethodDeclaration is used in this way it follows that a matching unique MethodDefinition must appear later in the text of the program.

A void method is activated by a method call that is a form of Statement (see section 10.3). A method of type int, char or boolean is activated by a method call that forms a constituent part of an Expression (see section 9), and yields a result that is an operand of that expression.

One method is uniquely designated as the main method. This is the first to be executed when the program as a whole is executed. It must be of type void and has no formal parameters; that is it must be defined with the header void main ().

The Block uniquely associated with each method incorporates a collection of Declarations of constants and variables (whose values are said to constitute the program state), and a sequence of Statements for the purpose of altering the program state by manipulating the variables and formal parameters of the set of methods.

The FormalParameters associated with a method provide the mechanism by which data may be transmitted from one method to another.

There are no "global" variable or constants. This is deliberate, both for simplicity in implementation, and also as a means of enforcing the important concepts of parameter passing. As in Java, parameters are passed by value. Since the value passed for an array is a tuple of {address, size}, arrays may appear to be passed by reference, as the contents of the array may be modified by the callee.


5 Declarations, scope rules and type

Every identifier occurring in a method must be introduced by a declaration. Declarations also serve to specify certain permanent properties of the entity denoted by that identifier, such as its type, and whether it is a constant, a variable, or an array.

The identifier is then used to refer to the associated entity. This is possible only in those parts of a program that are within the scope of the declaration. No identifier may denote more than one entity within a given scope. The scope extends textually from the point of the declaration to the end of the Block in which the declaration has been made and to which the entity is said to be local.

  Declaration =  ConstDeclaration | VarDeclarations .

A data type determines the set of values which variables and parameters of that type may assume, and the operators that are applicable to such variables and parameters.

  Type = ( "int" | "char" | "boolean" | "void" )  .

Parva supports only three fundamental types, representing the set of Boolean values {false, true}, the set of ASCII characters, and the set of integer values {min .. max}, where min and max are implementation defined (the values {-2147483648 ... 2147483647}). The void type cannot be used in the declaration of variables or constants, and has a special meaning in the context of method declarations.


6 Constant declarations

A constant declaration permanently associates an identifier with a constant value.

  ConstDeclaration = "const" ConstIdentifier "="
                     ( number | character | "true" | "false" ) ";" .
  ConstIdentifier  = identifier .

The type of the identifier is determined from the context of the declaration. true and false are the Boolean constants. Numbers and character literals are deemed to define constants of integer and character type respectively. The syntax of ConstDeclarations is one of the few places where the syntax does not match that of Java.

Examples:

    const MAX = 100;
    const YES = true;
    const CapitalA = 'A';


7 Variable declarations

Variables are those data items whose values may be changed by execution of the program. Variable declarations serve to introduce variables and to associate them with identifiers that must be unique within the given scope. They also serve to associate a fixed data type with each variable so introduced.

  VarDeclarations  = VarType OneVar { "," OneVar } ";" .
  VarType          = "int" | "char" | "boolean" .
  OneVar           =   VarIdentifier [ "=" Expression ]
                     | "[" "]" VarIdentifier "=" "new" VarType ArraySize .
  VarIdentifier    = identifier .
  ArraySize        = "[" number | ConstIdentifier "]" .

Each variable identifier denotes either a simple scalar variable of a specified type, or an array structure of scalar elements that all have that specified type. Each array structure has a fixed size, denoted by the number (or the value of the constant identifier) specified in its declaration.

Scalar variables and arrays whose identifiers appear in the same list are all of the same type. This type is either integer (denoted by the key word int), character (denoted by the key word char), or Boolean (denoted by the key word boolean).

When the statement sequence that is defined by a Block is activated, all variables are deemed to have initially undefined values, except for those scalar variables that have been assigned the values of expressions within the variable declaration sequence where they were declared.

Examples:

    const SIZE = 100;
    int i, j = 8;
    boolean Started, Suitable;
    char Letter, reply;
    boolean Sieve [] = new boolean [SIZE];

As an extension, the language allows either C++ style or a simulated Java style declarations of arrays.

    const SIZE = 100;
    int list1[Size];
    int [] list2 = new int [SIZE];

The Java style still requires that arrays are declared of a fixed length; there is no dynamic allocation of memory or garbage collection in the implementation.


8 Formal Parameters

Formal parameters are specified in a list of identifiers that denote actual parameters that are only specified when a method is called. The correspondence between formal and actual parameters is established only when the call takes place.

  FormalParameters    = [ OneFormal { "," OneFormal } ] .
  OneFormal           = FormalType ( ScalarFormal | ArrayFormal ) .
  FormalType          = "int" | "char" | "boolean" .
  ScalarFormal        = FormalIdentifier .
  ArrayFormal         = "[" "]" FormalIdentifier | FormalIdentifier "[" "]" .
  FormalIdentifier    = identifier .

There are effectively two kinds of formal parameters - scalar parameters and array parameters. The distinction is drawn in the syntax of the formal parameter list. Each scalar formal parameter stands for a local variable to which the result of the evaluation of the corresponding actual parameter is assigned as initial value when the method is called. Array formal parameters correspond to actual parameters that are specified by array names, and effectively act as alias names for these arrays during the execution of the method body; these arrays may be passed to other methods in the same way. The scope of formal parameters is the text of the Block of the method.

The type of a formal parameter is specified in its declaration; this type must correspond to the type of the actual parameter used in the method call.

A method declared without parameters has an empty parameter list, and must be called by a method designator whose actual parameter list is empty.

Examples:

    int larger (int a, int b) {
    //  return larger of the two arguments
      if (a > b) { return a; } else { return b; }
    }

    int sum (int [] list, int n) {
    // return sum of n elements - list[0] ... list[n-1]
      int total = 0;
      for (int i = 0; i < n; i++) { total += list[i]; }
      return total;
    }

The declaration syntax allows either C++ style or Java style declarations of array parameters. This is a compromise (it seems to exist in Java too) and it would probably be preferable to settle for one form only.

A method may detect the "length" property of an array parameter. The length is passed as a hidden parameter to allow the called method to perform array bound checking.


9 Expressions

Expressions are constructs denoting rules of computation whereby the current values of variables, constants and values returned by method calls may be combined to derive other values by the application of operators. Expressions consist of operands and operators. Parentheses may be used to express specific associations of operators and operands where the normal rules of precedence are unsuitable on their own.

  Expression       = AndExp { "||" AndExp } .
  AndExp           = BitOrExp { "&&" BitOrExp } .
  BitOrExp         = BitAndExp { "|" BitAndExp } .
  BitAndExp        = EqualExp { "&" EqualExp } .
  EqualExp         = RelationExp { EqualOp RelationExp } .
  RelationExp      = AddExp { RelOp AddExp } .
  AddExp           = MultExp { AddOp MultExp } .
  MultExp          = UnaryExp { MulOp UnaryExp } .
  UnaryExp         = Factor | "+" UnaryExp | "-" UnaryExp | "!" UnaryExp .
  Factor           =   Designator | ConstIdentifier | MethodCall
                     | number | character | "true" | "false"
                     | VarType "(" Expression ")" | "(" Expression ")" .
  Designator       = VarIdentifier [ "[" Expression "]" | "." Property ] .
  Property         = identifier .
  VarIdentifier    = identifier .
  ConstIdentifier  = identifier .
  MethodCall       = MethodIdentifier "(" ActualParameters ")" .
  MethodIdentifier = identifier .
  ActualParameters = [ Expression { "," Expression } ] .
  VarType          = "int" | "char" | "boolean" .
  AddOp            = "+"  | "-" .
  MulOp            = "*"  | "/" | "%" .
  EqualOp          = "==" | "!="  .
  RelOp            = "<"  | "<=" | ">" | ">=" .

The expression syntax is a simplified form of the C++/Java one, employing rather too many precedence levels to remember. The only Property at present is length, used to extract the length of an array, as in Java.

9.1 Operands

With the exception of numbers and Boolean and character literals, simple operands are denoted by designators. A Designator consists of an identifier referring to the constant or variable to be designated. This identifier must be followed by a selector if the identifier denotes an array.

  Designator    = VarIdentifier [ "[" Expression "]" ] .
  VarIdentifier = identifier .

If A designates an array, then A[E] denotes that element of A whose index is the current value of the expression E. E must be of integer or character type, and the value of E must lie within the range of possible values for the index of A. This range is from 0 ... ArraySize - 1, as specified when A was declared.

If the designated entity is a variable or an array element, then, when used as an operand, the designator refers to the variable's current value, and the type of the operand is the type of that variable or array element. An operand that is a literal constant denotes a value that is the value of that constant, and the type of the operand is the type of that literal constant.

Operands may also be designated by method calls (see section 9.3).

9.2 Operators

The syntax of expressions distinguishes between several classes of operators with different precedences (binding strengths). The syntax and operator precedence are modelled on the baroque C++/Java model. The unary operators have the highest precedence, followed by multiplication operators, addition operators, relational operators, bitwise operators and logical operators. Operators of the same precedence associate from left to right. Thus, for example, x - y - z * w stands for (x - y) - (z * w).

The available operators are listed in the following tables.

9.2.1 Logical operators

symbol   result

||       logical disjunction (Boolean OR)
&&       logical conjunction (Boolean AND)
!        negation

These operators apply only when their operands are of the Boolean type, and yield a Boolean result.

p || q   stands for "if p then true, else q"
p && q   stands for "if p then q, else false"
! p      stands for "not p"

9.2.2 Bitwise operators

symbol   result

|      bitwise OR
&      bitwise AND

These operators yield the same type as the operands.

9.2.3 Arithmetic operators

symbol   result

+        sum
-        difference
*        product
/        quotient
%        modulus

These operators apply only to operands of integer or character type, and yield an integer type. When used as operators with a single operand, - denotes sign inversion and + denotes the identity operation.

The operators / and % are related by the following formulae defined for any dividend x and positive divisor y:

    x  =  (x / y) * y  +  (x % y)
    0 <=  (x % y) < y.

9.2.4 Relational operators

symbol   relation

==      equal
!=      unequal
<       less
<=      less or equal
>       greater
>=      greater or equal

These operators yield a result of Boolean type. The ordering operators <, <=, > and >= apply to operands of the integer and character types. The relational operators == and != also apply when both operands are of the Boolean type. The operands of a relational operator may be evaluated in any convenient order.

Examples of expressions:

    1996                    (Integer)
    i / 3                   (Integer)
    !Started || Suitable    (Boolean)
    (i+j) * (i-j)           (Integer)
    (0 <= i) && (i < max)   (Boolean)

9.3 Method calls

A MethodCall serves to activate a non-void method, and may appear as an operand in an Expression, where it represents the value computed by that particular activation of the designated method.

  MethodCall       = MethodIdentifier "(" ActualParameters ")" .
  MethodIdentifier = identifier .
  ActualParameters = [ Expression { "," Expression } ] .

The call may contain a list of ActualParameters which are substituted in place of the corresponding FormalParameters defined in the MethodDeclaration. The correspondence is established by the positions of the parameters in the lists of actual and formal parameters respectively.

There are two kinds of actual parameters: array parameters and scalar parameters. In the case of array parameters, the actual parameter, while syntactically an Expression, must be an identifier denoting an entire array (or formal array parameter of the method in which the MethodCall is an operand). In the case of a scalar parameter, the actual parameter may be a more general Expression. This expression is evaluated prior to the method activation, and the resulting value is assigned to the formal parameter which then constitutes a local variable of the called method. The types of corresponding actual and formal parameters must be compatible.

The use of a method identifier in an Expression within the Body associated with the definition of that method implies a recursive activation of that method.

9.4 Type-casting methods

Parva is a more strictly typed language than either Java or C++. To enable expressions of mixed types to be evaluated, use of type-casting pseudo methods may be helpful. Operands of the form Type(Expression) have the value denoted by Expression and the type denoted by Type.

Examples of type-casting:

    boolean(3) && boolean('a')
    char(int('A') + 32)

9.5 Random number generation

There is a predefined int method random(Max) that returns a random number in the range 0 ... Max-1, where Max is the value of the actual parameter specified in the call.

9.6 Predefined character methods

Predefined char methods toLowerCase(Ch) and toUpperCase(Ch) returns the lower case/upper case equivalents of their arguments if these are letters.

9.7 Predefined Boolean methods

Predefined boolean methods eof() and eoln() return true if the last input operation reached the end of file or end of line, respectively.


10 Statements

Statements denote actions. There are elementary and structured statements.

Elementary statements are not composed of any parts that are themselves statements. They are the assignment, the input statement, the void method call, the output statement, the return statement, the break statement, the exit statement, the assert statement and the empty statement.

Structured statements are composed of parts that incorporate further statements. They are used to express sequencing, as well as conditional, selective, and repetitive execution.

A Statement may assume the form of a Block, and may thus incorporate declarations of identifiers, whose scope is local to that block.

  Statement =   Block           | AssignmentStatement | IfStatement
              | WhileStatement  | ForStatement        | DoStatement
              | InputStatement  | OutputStatement     | VoidMethodCall
              | ReturnStatement | BreakStatement      | EmptyStatement
              | ExitStatement   | AssertStatement .
  VoidMethodCall = MethodCall .

10.1 Assignments

An AssignmentStatement denotes the replacement of the value of a variable with a new value, and results in a change to the state of a program.

  AssignmentStatement = Variable ( AssignOp Expression | "++" | "--" ) ";" .
  AssignOp            = "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" .
  Variable            = Designator .
  Designator          = VarIdentifier [ "[" Expression "]" ] .
  VarIdentifier       = identifier .

If the Expression is present, the assignment serves to replace the current value of the designated Variable by the value specified by the Expression. The simple assignment operator is written as "=" and pronounced as "becomes". The types of the Expression and of the Variable must be assignment compatible, and the Variable must designate a scalar variable, or an array element.

Assignment compatibility is defined by the following

Type of Variable  Type of Expression

int           int or char
char          char or int
boolean       boolean

Statements of the form Variable++; and Variable--; are semantically equivalent to the statements Variable = Variable + 1; and Variable = Variable - 1; respectively. In this case the Variable must be of the integer or character type.

The compound assignment operators +=, -=, *=, /=, %=, &= and |= have the same semantics as in C++ or Java. That is

a += b;           is equivalent to     a = a + b;
a -= b;           is equivalent to     a = a - b;
a *= b;           is equivalent to     a = a * b;
a /= b;           is equivalent to     a = a / b;
a %= b;           is equivalent to     a = a % b;
a &= b;           is equivalent to     a = a & b;
a |= b;           is equivalent to     a = a | b;

Examples:

    i = 0;
    Suitable = i == j;
    j += i + k % 3;
    List[i]++;

In Parva, Expressions and Statements are clearly distinguishable, which is not the case in C++ where "assignment expressions" (with side effects) are usually taught to the neophyte to be "assignment statements". This means that statements (expressions) of the form a = b = c are not permitted. Furthermore the use of the ++ and -- operators has been restricted in a way that renders what in C++ would be regarded as expressions of the form i++ syntactically to become "assignment statements".

10.2 Blocks - statement sequences

A Block statement may be used to group several statements and declarations into one indivisible unit. Block statements are demanded syntactically as components of structured statements.

  Block = "{" { Declaration | Statement } "}" .

Statement sequences within a Block statement denote the sequence of actions specified by the component statements, executed in the order specified by the sequence.

Note that a Block may incorporate the declarations of further constants and variables whose scope is restricted to that block, and to blocks introduced within that block, subject to the rule that the redeclaration of an identifier in an inner block is, as in Java, not permitted (as it would be in some other languages).

Parva demands that Blocks are used within structured statements. Although this is not true in C++ or Java it will make for fewer of those situations where the braces are omitted in error.

Note that although blocks can be nested, methods cannot be nested - again as in C++.

10.3 Void method calls

A VoidMethodCall serves to activate a void method.

  MethodCall       = MethodIdentifier "(" ActualParameters ")" .
  MethodIdentifier = identifier .
  ActualParameters = [ Expression { "," Expression } ] .

The call may contain a list of ActualParameters which are substituted in place of the corresponding FormalParameters defined in the MethodDeclaration. The correspondence is established by the positions of the parameters in the lists of actual and formal parameters respectively.

There are two kinds of actual parameters: array parameters and scalar parameters. In the case of array parameters, the actual parameter, while syntactically an Expression, must be an identifier denoting an entire array (or formal array parameter of the method in which the MethodCall is a Statement). In the case of a scalar parameter, the actual parameter may be a more general Expression. This expression is evaluated prior to the method activation, and the resulting value is assigned to the formal parameter which then constitutes a local variable of the called method. The types of corresponding actual and formal parameters must be compatible.

The use of a method identifier in a MethodCall within the Body associated with the definition of that void method implies a recursive activation of that method.

There is a predefined void method randomseed(x) that sets the seed for random number generation to be x, where x is the value of the actual parameter specified in the call.

10.4 If statements

An IfStatement specifies the conditional execution of a guarded Block.

  IfStatement  = "if" "(" Condition ")" Block [ "else" ( Block | IfStatement ) ] .
  Block        = "{" { Declaration | Statement } "}" .
  Condition    = Expression .

The Expression (guard) forming the Condition must yield a Boolean result. If the guard evaluates to true the guarded Block is executed. If the guard evaluates to false, the Block following the symbol else is executed, if there is one.

Note the requirement that the components of an IfStatement are Blocks rather than single statements. However, an else clause may contain a simple IfStatement. This has been done to cut down on the numerous braces that might otherwise appear in a cascaded if ... else if ... else if ... else if construction.

Examples:

   if (i > max) {
     println("limit exceeded)"; return;
   }
   if (j % 3 == 0) {
     print("divisible by 3");
   } else {
     print("not divisible by 3");
   }

10.5 While statements

A WhileStatement is one form of specifying the repetition of an associated statement sequence.

  WhileStatement = "while" "(" Condition ")" Block .
  Block          = "{" { Declaration | Statement } "}" .
  Condition      = Expression .

The Expression that is the Condition must yield a Boolean result. If this expression yields true, the associated Block is executed. The condition evaluation is performed once at the start of each complete iteration, and the test and block execution are repeated as long as the Condition yields true.

Example:

   while (j > 0) {
     j = j / 2; i = i + k;
   }

10.6 Do Statements

A DoStatement is one form of specifying the repetition of an associated statement sequence.

  DoStatement =  "do"  Block  "while" "(" Condition ")" ";" .
  Block       = "{" { Declaration | Statement } "}" .
  Condition   =  Expression .

The Expression must yield a Boolean result. A DoStatement specifies the repeated execution of the associated Block as long as the Condition is satisfied. The condition evaluation is performed once at the end of each complete iterationand so the Block is executed at least once.

Example:

  do {
    j = j / 2; i = i + k;
  } while (j > 0);

10.7 For statements

A ForStatement is a effectively a notational shorthand for expressing a form of WhileStatement that occurs frequently in practice.

  ForStatement = "for" "(" [ ControlType ] ControlVar "=" Expression ";"
                 Condition ";" [ Epilogue ] ")" Block .
  Epilogue     = ControlVar ( AssignOp Expression | "++" | "--" ) .
  ControlVar   = identifier .
  ControlType  = "int" | "char" .
  Block        = "{" { Declaration | Statement } "}" .
  Condition    = Expression .
  AssignOp     = "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" .

A ForStatement is semantically equivalent to

           ControlVar = Expression;
           while (Condition) { Block; Epilogue }

Frequently epilogues are of the forms identifier++ and identifier--, which are semantically equivalent to the assignments identifier = identifier + 1; and identifier = identifier - 1; respectively. The Epilogue may be omitted.

The identifier that denotes the ControlVar in a ForStatement and in its Epilogue must denote the same scalar variable, which must be of the int or char type. This variable may be declared within the ForStatement itself. Statements within the Block must not alter the value of this variable in any way.

Examples:

  for (int i = 1; i <= 99; i++) { println(i); }
  for (i = 99; i >0; i -= 3) { println(i); }

10.8 Input statements

An InputStatement specifies a list of variables that are to be assigned new values from an input source external to the program.

  InputStatement  = "read" "(" ReadElement { "," ReadElement } ")" ";" .
  ReadElement     = PromptString | Variable .
  Variable        = VarIdentifier [ "[" Expression "]" ] .
  PromptString    = string .

Each Variable must designate a scalar variable, or an element of an array. Strings may be included in the list of arguments, and serve to act as prompts to the user as to what is to be input.

Example:

  read("Please supply your age and mass ", age, mass);

The system allows for a C++ simulation using cin, as well as this more traditional Pascal-like notation. get is also allowed as a synonym for read.

10.9 Output statements

A OutputStatement specifies a list of strings and expressions whose values are to be computed and then transferred to an output sink external to the program.

  OutputStatement = ( "print" | "println" )  "(" [ ResultList ] ")" .
  ResultList      = Result { "," Result } .
  Result          =   string
                    | IntegerExpression [ ":" MinimumWidth ]
                    | BooleanExpression [ ":" MinimumWidth ]
                    | CharacterExpression [ ":" MinimumWidth ] .
  MinimumWidth    = IntegerExpression .

println differs from print in that it specifies that a line break is to appended to the end of the ResultList automatically.

The optional second Expression in a Result specifies the minimum width of field to be used for the output, save that the special value of 0 means "precede with exactly one space". By default a width of 0 is used for integer and Boolean results, and a width of 1 for character results.

Example:

  println("The result is " , i + j);
  print(" The answer is ", i % k : 4, "\n");

The system allows for a C++ simulation using cout, as well as this more traditional Pascal-like notation.

10.10 Return statements

A ReturnStatement causes immediate termination of execution of the method in which it appears, returning control to the invoking environment.

  ReturnStatement = "return" [ Expression ] ";" .

In the case of void methods the Expression must be absent. In all other methods it must be present, and defines the value to be returned as the result of that method. The type of this Expression must be compatible with that of the method as specified in the MethodDefinition. A method may incorporate several ReturnStatements, although of course only one can be executed in any particular activation of the method. In void methods an implicit ReturnStatement occurs at the end of the method Block.

10.11 Break Statements

A BreakStatement may only be used within the associated Block of a loop construction (either a WhileStatement, DoStatement or ForStatement), and causes immediate termination of execution of the loop and continuation with the statement following the loop.

  BreakStatement = "break" ";" .

Example:

  int total = 0, i;
  while (true) { // indefinite loop
    read(i);
    if (i == terminator) break;
    total += i;
  }
  write("total = ", total);

10.12 Assert statements

An AssertStatement evaluates a Boolean expression; if this has the value false the program is aborted immediately with a warning message.

  AssertStatement = "assert" "(" Condition ")" ;" .
  Condition       = Expression.

10.13 Exit statements

An ExitStatement is a call to the predefined void method exit and causes the immediate termination of the program.

  ExitStatement =  "exit" "(" ")" ";" .

10.14 Empty statements

An EmptyStatement causes no change to the program state.

  EmptyStatement =  ";" .

The EmptyStatement is included in order to relax punctuation rules in statement sequences.

10.15 Missing statements

There is no continue statement in this release of the language.

There is no switch statement in this release of the language.


11 Complete example

This is not an outstanding example!

int readList (int[] list, int MAX) {
// read a list and return the number of elements processed
  println("Type a list of numbers to sort, terminated by a multiple of 8\a");
  int n = 0;
  do {
    read(list[n]);
    n++;
  } while (list[n - 1] % 8 != 0    // a number divisible by 8 stops the list
           && n != MAX);           // the list is about to overflow
  return n;
}

void sortList (int[] list, int n) {
// Bubble sort list of n elements list[0] .. list[n-1]
  boolean sorted = false;      // local Boolean variable
  while ((n > 1) && !sorted) {
    sorted = true;             // be optimistic
    for (int i = 0; i <= n - 2; i++) {
      if (list[i] > list[i + 1]) {
        sorted = false;        // bad luck!
        int temp = list[i];    // swap two values using local temp
        list[i] = list[i + 1];
        list[i + 1] = temp;
      }
    }
    n--;
  }
}

void main () {
// A simple Parva program for demonstrating a Bubble Sort
  const MAX = 20;
  println("Hello there - welcome to \"Pat\'s Fiendish Sorter\" - it\'s great!");
  int [] list = new int[MAX];
  int n = readList(list, MAX);

  if (n > 1) {
    n--;                            // throw away stopping value
    sortList(list, n);
    print("Sorted list of " , n, " numbers\n");
    int j = 0;
    while (j < n) {                 // list in forward order
      print(list[j]);
      j++;
    }
    println();
    for (int i = n; i >= 1; i--) {  // list in reverse order
      print(list[i - 1], "\t");
    }
    println();
  } else {
    println("No data given");
  }
}


Home  © P.D. Terry