The Programming Language Paradigm/SP

Atanas Radenski
Computer Science Department, P.O. Box 19479
Winston-Salem State University, Winston-Salem, NC 27103
e-mail: radenski@computer.org

 
 


This report describes Paradigm/SP, an object-oriented parallel programming language that supports the development and use of generic parallel algorithms. In Paradigm/SP, generic parallel algorithms are represented as extensible modules that encapsulate module-bound classes and methods. Such modules can be extended with sequential domain-specific code in order to derive particular parallel applications. Module extension is achieved through module embedding, class overriding and method overriding. The Paradigm/SP language also incorporates standard means for secure parallel message-passing programming, such as send, receive, for-all, and parallel statements, and channel types.

< ^>    Contents

1. Introduction
2. Credit to Others
3. Extensible Modules
3.1. Modules Extension
3.2. Comparison of Module Import and Module Extension
3.3. Scope of Declared Identifiers
3.4. Multiple and Single Inheritance
3.5. Module Compilation and Execution
4. Classes and Class Overriding
5. Methods and Method Overriding
6. Processes and Variables
7. Types
7.1. Channel Types
8. Variables
8.1. Entire Variables
8.2. Block Parameters
8.3. Target Variables
8.4. Expression Variables
8.5. Channel Variables
9. Expressions
9. 1. Channel Expressions
9.2. Relational Operators
10. Message Communication
10.1. The Procedure Open
10.2. The Procedures Send and Receive
11. Statements
11.1. Assignment Statements
11.2. Procedure Statements
11.3. Parallel Statements
11.4. Forall Statements
11.5. Unrestricted Statements
11.6. Assume Statements
12. Standard Identifiers
13. Syntax Summary
14. References
15. Related Links


< ^>    1. Introduction

This report describes Paradigm/SP, an object-oriented parallel programming language that supports the development and use of generic parallel algorithms. The principal new feature of Paradigm/SP is a code reuse mechanism called module extension. The Paradigm/SP language also inherits standard parallel features, such as send, receive, for-all, and parallel statements, and channel types from SuperPascal, a high-level parallel programming language developed by P. B. Hansen [Brinch Hansen 94]. These standard parallel features are systematically enhanced to conform with module extension.

Module extension is a code reuse mechanism that enables building of new modules from existing ones. An extensible module encapsulates public and private (1) classes and other types, (2) procedure and function methods, (3) global variables, and (4) statements for module initialization. Module extension consists of module embedding, class overriding, and method overriding.  Existing modules can be embedded in a newly declared module. The embedding module can declare new components in addition to those inherited from its embedded modules. An inherited class definition can be overridden (i.e., re-declared) with an extended class definition. Similarly, an inherited method implementation can be overridden with a new method implementation.

Parallel programming is supported with deterministic statements for parallel processes and synchronous message communication. A parallel statement denotes parallel execution of a fixed number of statements. A forall statement denotes parallel execution of the same statement by a dynamic number of processes. Recursive procedures may be combined with parallel and forall statements to define recursive parallel processes. Parallel processes communicate by sending typed message through channels created dynamically. Restrictions on the use of variables enable a single-pass compiler to check that parallel processes are disjoint.

The syntax of Paradigm/SP is specified in Section 13. Individual sections of this report do not contain syntax definitions but incorporate appropriate links to Section 13 instead.

A prototype implementation of the Paradigm/SP language has been developed. The implementation consists of a compiler that generates abstract code and a loader and an interpreter for this abstract code. All source files of  the prototype implementation (currently in Turbo Pascal) are freely available.

< ^>    2. Credit to Others

This manual adopts parts of The Programming Language SuperPascal report [Brinch Hansen 94] in order to reflect language features adopted in Paradigm/SP from SuperPascal. Additional explanations are presented in [Radenski 98], Section Credit to Others.

<^>    3. Extensible Modules

<^>    3.1. Modules Extension

An extensible module is a collection of declarations of constants, types, variables, procedure and function methods. The module can also include a sequence of statements used for initialization of the module. Some of the declared entities are exported by the module and can be used by client modules, while the non-exported entities remain private in the module. Technically, identifiers of exported constants, types, variables, and restricted methods are marked with a "*", while the so called unrestricted methods (3.2) are marked with "-". Exported variables are write-protected in client modules (i.e., importing modules) but can be updated in module extensions.

Example: Principal parts of an extensible module.

module M0;
const
    { ... constant declarations... }
type
    { ... type declarations... }
var
    exported*: typeIdent...;
    private: typeIdent...;
procedure dynamic*;
begin  {exported method} end;
procedure static;
begin {private method} end;
begin
   {  ... module initialization ... }
end.
Modules are extensible, i.e., a module can be declared as an extension of one or more base modules.

Example: Module M1 is a (direct) extension of its (direct) base module M0.

module M1(M0);
    { ... new declarations... }
var
    private : typeIdent;
procedure dynamic*;
begin  { overrides M0.dynamic } end;
procedure static;
begin { new private method} end;
begin
    { ... module initialization ... }
end.
An extended module M1 inherits all components of its base module M0. Only identifiers that are exported by M0 are visible in the extension M1; such identifiers are re-exported by M1. Besides, an extended module may declare new identifiers in addition to those inherited from its base modules. Newly declared identifiers must be different from identifiers that are exported by base modules but can be the same as their private identifiers.

<^>    3.2. Comparison of Module Import and Module Extension

The principal difference between module import and module extension is that module import implements shares-a relationship between modules while module extension implements contains-a relationship.

Different modules M1 and M2 that belong to an application can import the same module M0. The imported module M0 is shared between modules M1 and M2. Any change of M0 by, say, M1 is visible for M2 as well.

Alternatively, if module M0 is extended by modules M1 and M2, each of these modules will have M0 as its proper part. Now, M1 and M2 incorporate separate instancesof M0. Therefore, M1 may change components inherited from M0, but these changes do not affect the same components inherited by M2. Besides, M1 and M2 can make different extensions of the same class inherited from M0. Likewise, M1 and M2 can have different methods override the same method inherited from M0.

Module import and extension can be mixed, if necessary in order to implement shares-a and contains-a relationships. In particular, a module M1 can import module M0 and, at the same, extend module M0. In such a case, M1 incorporates a separate instance of its base module M0 and also refers to the shared instance of the imported module M0. Note that the qualified identifier M0.x stands for an imported entity x, while the identifier x always stands for an inherited one.

<^>    3.3. Scope of Declared Identifiers

The scope of a declared identifier extends from the declaration to the end of the block containing the declaration. An identifier is accessible in its scope unless it is hidden by a locally declared identifier. The scope of an identifier x that is exported by a module M0 extends to any module M1 that imports or extends M0. If M1 imports M0, x is indirectly accessible as a qualified identifier M0.x. If M1 extends M0, x is directly accessible in M1.

<^>    3.4. Multiple and Single Inheritance

An extended module can have one, two, three, or more base modules, provided the name spaces of the base modules are disjoint. In contrast, class extension always applies to exactly one class. Thus, module extension allows multiple inheritance, while class extension allows single inheritance.

<^>    3.5. Module Compilation and Execution

Extensible modules are separate compilation units and separate execution units. The implementation converts a correct module 1) into a symbol file that represents the module's interface and 2) into an object file. At run time, the implementation executes a module by loading its object file and then executing the module body.

A module can be directly executed for either testing the module, or because that module is intended to be used as a main module, or main program in traditional terms. This approach eliminates the need for a special linguistic construct for main programs. A benefit is that a main module (i.e., a main program) is as extensible and adaptable as any other module.

A client module C0 specifies a list of imported modules: module C0; import M0, M1; ... end. The client, C0, can refer to an entity x that is exported by M0 only through a qualified identifier,  M0.x. At run time, the implementation executes imported modules before the execution of the client. Since imported modules are shared between client modules, the implementation loads only one instance of each imported module and this copy is shared by all of its clients.

The execution of a module body is preceded by the execution of the bodies of its base modules, if any. This rule ensures proper module initialization.

<^>4. Classes and Class Overriding

A class type, or simply a class, is an aggregate type that consists of fields of possibly different types. If a class is exported by a module, some of its fields can also be marked for export; all unmarked fields are private for the declaring module. An empty class (i.e., class without fields) can be briefly designated with a double dot, "..".

A class that is exported by a base module M0 can be re-declared in an extension M1 of M0. A class definition in M1 extends the definition inherited from M0; the extended definition comprises all fields originally specified in M0 and, in addition, the fields specified in M1. The extended type definition overrides the type definition inherited from M0. The class originally declared in the base module M0 is dynamically bound to the definition extended by M1.

Example: Consider class C declared in M0 and extended in M1, and an object exported by M0. Although the object is originally declared in M0, when inherited by M1 it contains all fields that belong to the extended definition of class C.

module M0;
type C* = class a*: integer;end;
var object*: C;
begin  object.a := 0; end.

module M1(M0);
type C* = class b*: integer;end;
begin object.b := 0; end.

<^>    5. Methods and Method Overriding

Methods can be declared as either functions or procedures. A method declaration consists of a heading followed by a block.

A method itself can contain declarations of other entities (such as constants, types, variables, and other methods) that are private for the enclosing method and cannot be exported.

A method identifier that is declared in a module can be marked for export with either a restricted mark "*" or an unrestricted mark "-".

A restricted method is a method exported with a restricted mark, "*".

An unrestricted method is one of the following:

1) a method exported with an unrestricted mark, "-", or
2) a private method that invokes an unrestricted method, or both.
A restricted method cannot have implicit parameters (see definition of implicit parameter in Section 8.2) and cannot invoke unrestricted methods.

An unrestricted method can have implicit parameters and invoke unrestricted methods. An unrestricted method can be invoked only in an unrestricted statements (see definition of unrestricted statements in Section 11.5).

A method identifier exported by a base module M0 can be re-declared in its extension M1, provided that its heading in M1 is the same as in M0 (in particular the export mark, "*" or "-", must be the same). The newly declared method body overrides the method body inherited from the base module. The body of a method P that is inherited from M0 and overridden in M1 is still available in M1 and can be invoked through the designators ^P.

A method does not belong to a class but to a module (or, alternatively, to another method).  Objects that belong to the class can be passed as parameters to the method.

Examples:

module M0;
type account* = class
          balance*: real;
       end;
var  total*: real;
procedure open*( var a: account);
begin a.balance := 0; end;

proceduretransact* (var a: account; amt: real);
begin a.balance := a.balance + amt;
    total := total + amt;
end;

procedure close* (var a: account);
begin transact (a, - a.balance) end;

begin {initialization of M0} total := 0; end.

---

module M1(M0);
type account* = class
          number*: integer;
       end;
var numAccnts*: integer;

procedure open* (var a: account);
begin  ^open(a);
    numAccnts := numAccnts + 1;
    a.number := numAccnts;
end;

procedure close* (var a: account);
begin ^close(a);
    numAccnts := numAccnts – 1;
end;

begin {initialization of M1}
    numAccnts := 0;
end.

---

module client;
import M1;
var  account: M1.account;
begin {client}
    M1.open(a);  M1.transact(a, 100);
    M1.transact(a, -50);  M1.close(a);
end.

<^>    6. Processes and Variables

By definition, a command is either variable access, expression, statement, statement sequence.

The evaluation or execution of a command is called a process. A structured process is a sequential or parallel composition of processes. The components of a parallel composition are called parallel processes. They proceed independently at unpredictable speeds until all of them have terminated.

In a program text an entire variable is a syntactic entity that has an identifier, a type, and a scope.

During program execution a block is activated when a process evaluates a function designator or executes a procedure statement or module. Every activation of a block B creates a new instance of every variable that is local to B. When an activation terminates, the corresponding variable instances cease to exist.

During recursive and parallel activations of a block, multiple instances of the local variables exist. Each variable instance is a dynamic entity that has a location, a current value, and a finite lifetime in memory.

The distinction between a variable as a syntactic entity in the program text and a class of dynamic entities in memory is usually clear from the context. Where it is necessary, this report distinguishes between syntactic variables and variable instances.

Parallel processes are said to be disjoint if they satisfy the following condition: Any variable instance that is assigned a value by one of the processes is not accessed by any of the other processes. In other words, any variable instance that is accessed by more than one process is not assigned a value by any of the processes.

< ^>    7. Types

Every type has an identifier. Two types are the same if they have the same identifiers and the same scope. Besides, a type with an identifier T2 can be declared as being the same as a type with identifier T1 by means of a type declaration, T2 = T1.

Standard scalar types are integer, real, char, boolean. User-defined types include enumerated types, array types, channel types, class types. Class types are discussed in Section 4. A standard string type is persented in Section 12.

Examples: Type declarations.

type
    vector = class x, y: real end ;
    v = vector;
    body = class m: real; r, v, f: vector end;
    system = array [1..n] of body;
    channel = *(body);
    net = array [0..p] of channel;
    mixed = *(body, integer);
    two = array [0..1] of mixed;
    four = array [0..1] of two;

<^>    7.1. Channel Types

Processes communicate by means of values called messages transmitted through entities called channels. A communication takes place when one process is ready to output a message of some type through a channel and another process is ready to input a message of the same type through the same channel. A channel type is designated as *(T1, T2, ... , Tn) and consists of an unordered set of distinct message types, T1, T2, ... , Tn. A channel type declaration cannot be recursive: T = *(..., T, ...). Processes create channels dynamically and access them by means of values known as channel references.

Examples: Channel types.

*(body)
*(body, integer)

< ^>    8. Variables

<^>    8.1. Entire Variables

An entire variable is a variable denoted by one of the following kinds of identifiers:
1. A variable identifier introduced by a variable declaration or a forall statement.
2. A qualified identifier of an imported variable.
3. A function identifier that occurs as the left part of an assignment statement in the statement part of the corresponding function block.
Examples: Entire variable declarations.
var
    inp, out: channel;
    c: net;
    a: system;
    ai, aj: body;
    left: mixed;
    top: four;
    i, j, k: integer;
A variable context is associated with each command C. This context consists of two sets of entire variables called the target and expression variables of C. If the process denoted by C may assign a value to an entire variable v (or one of its components), then v is a target variable of C. If the process may use the value of v (or one of its components) as an operand, then v is an expression variable of C.

<^>    8.2. Block Parameters

Consider a procedure or function block B with a statement part S. An implicit parameter of B is an entire variable v that is global to B and is part of the variable context of S. If v is a target variable of S, then v is an implicit variable parameter of B. If v is an expression variable of S, then v is an implicit value parameter of B.

A function block cannot use formal variable parameters or implicit variable parameters.

A recursive procedure or function block cannot use implicit parameters.

<^>    8.3. Target Variables

An entire variable v is a target variable of a command C in the following cases:
1. The variable identifier v occurs in an assignment statement C that denotes assignment to v (or one of its components).
2. The variable identifier v occurs in a for statement C that uses v as the control variable.
3. The variable identifier v occurs in a procedure statement  C that uses v (or one of its components) as an actual variable parameter.
4. The variable v is an implicit variable parameter of a procedure block B, and C is a procedure statement that denotes activation of B.
5. The variable v is a target variable of a command D, and C is a command that contains D.

<^>    8.4. Expression Variables

An entire variable v is an expression variable of a command C in the following cases:
1. The variable identifier v occurs in an expression C that uses v (or one of its components) as an operand.
2. The variable identifier v occurs in the element statement C of a forall statement that introduces v as the index variable.
3. The variable v is an implicit value parameter of a function block B, and C is a function designator that denotes activation of B.
4. The variable v is an implicit value parameter of a procedure block B, and C is a procedure statement that denotes activation of B.
5. The variable v is an expression variable of a command D, and C is a structured command that contains D.

<^>    8.5. Channel Variables

A channel variable is a variable of a channel type. The value of a channel variable is undefined unless a channel reference has been assigned to the variable.

Examples:

inp
c[0]
top[i,j]

< ^>   9. Expressions

<^>    9. 1. Channel Expressions

A channel expression is an expression of a channel type. The expression is said to be well-defined if it denotes a channel; otherwise, it is undefined.

Examples:

out
c[k-1]

<^>    9.2. Relational Operators

If x and y are well-defined channel expressions of the same type, the expressions x = y and x <> y denote boolean values. The value of x = y is true if x and y denote the same channel, and is false otherwise. The value of x <> y is the same as the value of not (x = y).

Example:

left = top[i,j]

<^>   10. Message Communication

The standard procedures for message communication are open, send, and receive.

<^>    10.1. The Procedure Open

If v is a channel variable, the open statement open(v) denotes creation of a new channel.

The open statement is executed by creating a new channel and assigning the corresponding reference to the channel variable v. The channel reference is of the same type as the channel variable.

The abbreviation open(v1, v2, ... , vn) is equivalent to open(v1); open(v2, ... , vn).

Examples:

open(c[k]);
open(inp, out);

<^>    10.2. The Procedures Send and Receive

The send statement send(b, e) denotes output of the value of an expression e through the channel denoted by an expression b. The expression b must be of a channel type T, and the type of the expression e must be a message type of T.

The receive statement receive(c, v) denotes input of the value of a variable v through the channel denoted by an expression c. The expression c must be of a channel type T, and the type of the variable v must be a message type of T.

The send and receive operations defined by the above statements are said to match if they satisfy the following conditions:

1. The channel expressions b and c are of the same type T and denote the same channel.
2. The output expression e and the input variable v are of the same type, which is a message type of T.
The execution of a send operation delays a process until another process is ready to execute a matching receive operation (and vice versa). If and when this happens, a communication takes place as follows:
1. The sending process obtains a value by evaluating the output expression e.
2. The receiving process assigns the value to the input variable v.
After the communication, the sending and receiving processes proceed independently.

The following communication errors can occur:

1. Undefined channel reference: A channel expression does not denote a channel.
2. Channel contention: Two parallel processes both attempt to send (or receive) through the same channel at the same time.
3. Message type error: Two parallel processes attempt to communicate through the same channel, but the output expression and the input variable are of different message types.
The abbreviation send(b, e1, e2, ... , en ) is equivalent to send(b,e1); send(e2, ..., en).

The abbreviation receive(c, v1, v2, ... , vn) is equivalent to receive(c,v1); receive(c, v2, ... , vn).

Examples:

send(out, ai);
receive(inp, aj);
send(top[i,j], 2, ai);

<^>   11. Statements

A statement is one of the following: empty statement, assignment statement, procedure statement, external procedure statement, if statement, while statement, repeat statement, for statement, case statement, compound statement, parallel statement, forall statement, unrestricted statement, assume statement. The syntax of all statements is specified in Section 13.

<^>    11.1. Assignment Statements

If x is a channel variable access and y is a well-defined channel expression of the same type, the effect of the assignment statement x := y is to make the values of x and y denote the same channel.

Example:

left := top[i,j];

<^>    11.2. Procedure Statements

The restricted actual parameters of a procedure statement are the explicit variable parameters that occur in the actual parameter list and the implicit parameters of the corresponding procedure block.

Restriction: The restricted actual parameters of a procedure statement must be distinct entire variables (or components of such variables). Similarly, the actual parameters of an external procedure statement must be distinct entire variables.

A procedure statement cannot occur in the statement part of a function block. This rule also applies to a standard or external procedure statement .

<^>    11.3. Parallel Statements

A parallel statement denotes parallel processes. Each process is denoted by a separate process statement.

The effect of a parallel statement is to execute the process statements as parallel processes until all of them have terminated.

Example:

parallel
    source(a, c[0]); sink(a, c[p]) |
    forall k := 1 to p do
        node(k, c[k-1], c[k])
end
Restrictions: In a parallel statement:
1. A target variable of one process statement cannot be a target or expression variable of another process statement.
2. A process statement cannot invoke an unrestricted method (see Section 5).

< ^ >    11.4. Forall Statements

The forall statement forall i := e1 to e2 do S denotes a (possibly empty) array of parallel processes, called element processes, and a corresponding range of values, called process indices. The lower and upper bounds of the process index range are denoted by two expressions, e1 and e2, of the same simple type (the index type). Every index value corresponds to a separate element process defined by an index variable i and an element statement S.

The index variable declaration i := e1 to e2 introduces the index variable i which is local to the element statement S.

A forall statement is executed as follows:

1. The expressions e1 and e2 are evaluated. If e1 > e2, the execution of the forall statement terminates; otherwise, step 2 takes place.
2. e2-e1+1 element processes run in parallel until all of them have terminated. Each element process creates a local instance of the index variable i, assigns the corresponding process index to the variable, and executes the element statement S. When an element process terminates, its local instance of the index variable ceases to exist.
Restrictions: In a forall statement, the element statement cannot:
1. Use target variables.
2. Invoke unrestricted methods (see Section 5).
Examples:
forall k := 1 to p do
    node(k, c[k-1], c[k])

forall i := 0 to 1 do
    forall j := 0 to 1 do
        quadtree(i, j, top[i,j])

<^>    11.5. Unrestricted Statements

A statement S is said to be unrestricted in the following cases:
1. The statement S is prefixed by a sic clause.
2. The statement S is a component of an unrestricted statement.
All other statements are said to be restricted.

Restricted statements must satisfy the rules labeled as restrictions in this report. These rules restrict the use of entire variables in procedure statements, parallel statements, and forall statements to make it possible to check the disjointness of parallel processes during single-pass compilation (see Sections 11.2, 11.3, and 11.4).

The same rules do not apply to unrestricted statements. Consequently, the programmer must prove that each unrestricted statement preserves the disjointness of parallel processes; otherwise, the semantics of unrestricted statements are beyond the scope of this report.

Examples: Unrestricted statements.

[sic] { i <> j }swap(a[i], a[j]);

[sic] { i <> j }a[i] := ai|a[j] := aj;

[sic] { disjoint elements a[i] }
    forall i := 1 to n do a[i] := ai;

<^>    11.6. Assume Statements

The effect of an assume statement is to test an assumption denoted by a boolean expression. If the assumption is true, the test terminates; otherwise, program execution stops.

Example:

assume i <> j;

<^>   12. Standard Identifiers

The standard type string is the only string type: string = [1..maxstring] char. A character string with n string elements denotes a string of n characters followed by maxstring-n null characters, where 2 <= n <= maxstring,  maxstring = 80, null = chr(0).
The default length n of a write parameter of type string is the number of characters (if any) which precede the first null character (if any), where 0 <= n <=  maxstring.

The standard text file input is the only input file. The file identifier is omitted from eof and eoln function designators and read and readln statements. The input file is an implicit value parameter of the eof and eoln functions and is an implicit variable parameter of the read and readln procedures (see Section 8.2).

The standard text file output is the only output file. The file identifier is omitted from write and writeln statements. The output file is an implicit variable parameter of the write and writeln procedures (see Section 8.2).

The following is an alphabetical list of all standard identifiers:
 

abs, arctan, boolean, char, chr, cos, eof, eoln, exp, false, integer, ln, maxint, maxstring, null, odd, open, ord, pred, read, random, readln, real, receive, round, send, sin, sqr, sqrt, string, succ, true, trunc, write, writeln.

<^>   13. Syntax Summary

The following grammar defines the complete syntax of Paradigm/SP. Note that in the current implementation uses two additional keywords: context as a synonim of module, and record as a synonim of class.

Module =
    ModuleHeading
    [ "import" ImportedModuleList ";" ]
    Block "." .
ModuleHeading =
    "module" ModuleIdentifier [ "(" BaseModuleList ")" ] ";" .
ModuleList =
    [ Identifier ":=" ] Identifier
    [ "," [ Identifier ":=" ] Identifier ] .
Block =
    DeclarationPart StatementPart .
DeclarationPart =
    [ ConstantDeclarations ]
    [ TypeDeclarations ]
    [ VariableDeclarations ]
    MethodDeclarations .
ConstantDeclarations =
    "const" ConstantDeclaration ";"
    [ ConstantDeclaration ";" ]* .
ConstantDeclaration =
    ConstantIdentifier [ ExportMark ] "=" Constant .
ExportMark = "*" .
Constant =
    [ Sign ] UnsignedConstant .
Sign =
    "+" | "-" .
UnsignedConstant =
    ConstantQualIdent |
    UnsignedOrdinal |
    UnsignedReal |
    CharacterString .
QualIdent =
    [ ModuleIdentifier "." ] Identifier .
UnsignedOrdinal =
    CharacterConstant |
    UnsignedInteger .
CharacterConstant =
    "'" CharElement "'" .
CharElement =
    GraphicCharacter |
    ApostropheImmage .
ApostropheImmage =
    "''" .
UnsignedInteger =
    DigitSequence .
DigitSequence =
    Digit [ Digit ]* .
UnsignedReal =
    IntegerPart RealOption .
IntegerPart =
    DigitSequence .
RealOption =
    "." FractionalPart [ ScalingPart ] |
    ScalingPart .
FractionalPart =
    DigitSequence .
ScalingPart =
    Radix ScaleFactor .
Radix =
    "e" | "E" .
ScaleFactor =
    [ Sign ] DigitSequence .
CharacterString =
    "'" StringElements "'" .
StringElements =
    CharElement [ CharElement ]* .
TypeDeclarations =
    "type" TypeDeclaration ";" [ TypeDeclaration ";" ]* .
TypeDeclaration =
    TypeIdentifier [ ExportMark ] "=" NewType | ExistingType .
ExistingType =
    TypeQualIdent .
NewType =
    EnumeratedType |
    ArrayType |
    ClassType |
    ChannelType .
EnumeratedType =
    "(" NewConstantList ")" .
NewConstantList =
    ConstantIdentifier [ "," NewConstantList ] .
ArrayType =
    "array" IndexRange "of" TypeQualIdent .
IndexRange =
    "[" OrdinalConstant ".." OrdinalConstant "]" .
ClassType =
    ".." | "class" FieldList "end" .
FieldList =
    ClassSection [ ";" ClassSection]* [ ";" ] .
ClassSection =
    FieldIdentifier [ ExportMark ] SectionTail .
SectionTail =
    "," ClassSection | ":" TypeQualIdent .
ChannelType =
    "*" "(" MessageTypeList ")" .
MessageTypeList =
    MessageTypeQualIdent [ "," MessageTypeQualIdent ]* .
VariableDeclarations =
    "var" VariableDeclaration ";" [ VariableDeclaration ";" ]* .
VariableDeclaration =
    VariableIdentifier [ ExportMark ] VariableTail .
VariableTail =
    "," VariableDeclaration |
    ":" TypeQualIdent .
MethodDeclarations =
    [ FunctionDeclaration ";" | ProcedureDeclaration ";" ]* .
FunctionDeclaration =
    FunctionHeading ";" MethodBlock .
FunctionHeading =
    "function" FunctionIdentifier [ MethodExportMark ]
        FormalParameterPart ":" TypeQualIdent .
MethodExportMark =
    "*" | "-" .
FormalParameterPart =
    [ FormalParameterList ] .
FormalParameterList =
    "(" FormalParameters ")" .
FormalParameters =
    FormalParameterSection [ ";" FormalParameterSection ]* .
FormalParameterSection =
    [ "var" ] VariableDeclaration .
VariableDeclaration =
    VariableIdentifier [ ExportMark ] VariableTail .
VariableTail =
    "," VariableDeclaration |
    ":" TypeQualIdent .
ProcedureDeclaration =
    ProcedureHeading ";" MethodBlock .
ProcedureHeading =
    "procedure" ProcedureIdentifier [ MethodExportMark ] FormalParameterPart .
StatementPart =
    CompoundStatement .
Expression =
    SimpleExpression [ RelationalOperator SimpleExpression ] .
RelationalOperator =
    "<" | "=" | ">" | "<=" | "<>" | ">=" .
SimpleExpression =
    [ Sign ] Term [ AddingOperator Term ]* .
AddingOperator =
    "+" | "-" | "or" .
Term =
    Factor [ MultiplyingOperatorFactor ]* .
MultiplyingOperator =
    "*" | "/" | "div" | "mod" | "and" .
Factor =
    UnsignedConstant |
    QualifiedVariableAccess |
    FunctionDesignator |
    "(" Expression ")" |
    "not" Factor .
QualifiedVariableAccess =
    QualIdent  [ ComponentSelector ] * .
ComponentSelector =
    IndexedSelector |
    FieldSelector .
IndexedSelector =
    "[" IndexExpressions "]" .
IndexExpressions =
    OrdinalExpression [ "," OrdinalEpression ]* .
FieldSelector =
    "." FieldIdentifier .
FunctionDesignator =
    FunctionIdent ActualParameterPart |
    StandardFunctionDesignator .
FunctionIdent =
    OverloadedIdent | QualIdent .
OverloadedIdent =
    " ^ " Identifier .
ActualParameterPart =
    [ ActualParameterList ] .
ActualParameterList =
    "(" ActualParameters ")" .
ActualParameters =
    [ ActualParameters "," ] ActualParameter .
ActualParameter =
    Expression |
    VariableAccess .
VariableAccess =
    EntireVariableAccess    [ ComponentSelector ]* .
EntireVariableAccess =
    VariableIdentifier .
StandardFunctionDesignator =
    FileFunctionDesignator |
    MathFunctionDesignator .
FileFunctionDesignator =
    "eof" | "eoln" .
MathFunctionDesignator =
    MathFunctionIdentifier "(" Expression ")" .
MathFunctionIdentifier =
    "abs" | "arctan" | "chr" | "cos" | "exp" | "ln" |
    "odd" | "ord" | "pred" | "round" | "sin" | "sqr" |
    "sqrt" | "succ" | "trunc" | "random".
Statement =
    EmptyStatement |
    AssignmentStatement |
    ProcedureStatement|
    ExternalProcedureStatement |
    IfStatement |
    WhileStatement |
    RepeatStatement |
    ForStatement |
    CaseStatement |
    CompoundStatement |
    ParallelStatement |
    ForallStatement |
    UnrestrictedStatement |
    AssumeStatement .
EmptyStatement = .
AssignmentStatement =
    LeftPart ":=" Expression .
LeftPart =
    FunctionVariable |
    VariableAccess .
FunctionVariable =
    FunctionIdentifier .
ProcedureStatement =
    ProcedureIdentifier ActualParameterPart |
    StandardProcedureStatement .
StandardProcedureStatement =
    ReadStatement |
    WriteStatement |
    OpenStatement |
    ReceiveStatement |
    SendStatement .
ReadStatement =
    "read" ReadParameterList |
    "readln" [ ReadParameterList ] .
ReadParameterList =
    "(" ReadParameters ")" .
ReadParameters =
    ReadParameter [ "," ReadParameter ]* .
ReadParameter =
    VariableAccess .
WriteStatement =
    "write" WriteParameterList |
    "writeln" [ WriteParameterList ] .
WriteParameterList =
    "(" WriteParameters ")" .
WriteParameters =
    WriteParameter [ "," WriteParameter ]* .
WriteParameter =
    Expression WriteOptionWriteOption .
WriteOption =
    [ ":" Expression ] .
OpenStatement =
    "open" "(" OpenParameters ")" .
OpenParameters =
    OpenParameter [ "," OpenParameter ]* .
OpenParameter =
    ChannelVariableAccess .
ReceiveStatement =
    "receive" "(" ReceiveParameters ")" .
ReceiveParameters =
    ChannelExpression "," InputVariableList .
InputVariableList =
    InputVariableAccess [ "," InputVariableAccess ]* .
SendStatement =
    "send" "(" SendParameters ")" .
SendParameters =
    ChannelExpression "," OutputExpressionList .
OutputExpressionList =
    OutputExpression [ "," OutputExpression ]* .
ExternalProcedureStatement =
    ExternalIdent ActualParameterPart .
ExternalIdent =
    OverloadedIdent | QualIdent .
IfStatement =
    "if" Expression "then" Statement [ "else" Statement ] .
WhileStatement =
    "while" Expression "do" Statement .
RepeatStatement =
    "repeat" StatementSequence "until" Expression .
StatementSequence =
    Statement
    [ ";" Statement ]* .
ForStatement =
    ForClause ForOption .
ForClause =
    "for" EntireVariableAccess ":=" Expression .
ForOption =
    UpClause | DownClause .
UpClause =
    "to" Expression "do" Statement .
DownClause =
    "downto" Expression "do" Statement .
CaseStatement =
    "case" OrdinalExpression "of" CaseList "end" .
CaseList =
    CaseListElement [ ";" CaseListElement ]* . [ ";" ] .
CaseListElement =
    CaseConstantList ":" Statement .
CaseConstantList =
    CaseConstant [ "," CaseConstant ]* .
CaseConstant =
    OrdinalConstant .
CompoundStatement =
    "begin" StatementSequence"end" .
ParallelStatement =
    "parallel" ProcessStatementList "end" .
ProcessStatementList =
    ProcessStatement [ "|" ProcessStatement ]* .
ProcessStatement =
    StatementSequence.
ForallStatement =
    "forall" IndexVariableDeclaration "do" Statement .
IndexVariableDeclaration =
    VariableIdentifier ":=" ProcessIndexRange .
ProcessIndexRange =
    Expression "to" Expression .
UnrestrictedStatement =
    SicClause Statement .
SicClause =
    "[" "sic" "]" .
AssumeStatement =
    "assume" Expression .

< ^>    14. References

Brinch Hansen, P. (1994) The Programming Language SuperPascal. Software-Practice and Experience, 24 (5), May 1994, 467-483.

Radenski, A. (1998) The Paradigm/SP Implementation Notes. Computer Science Department, Winston-Salem State University, Winston-Salem, NC.

< ^ >    15. Related Links

*Index of Manuals
*The Paradigm Language and Its Implementation
*Research
*Contact

 

 
 
 

-------------
Last updated: February 2000.