Protel

Protel stands for "Procedure Oriented Type Enforcing Language". It is a programming language created by Nortel Networks and used on telecommunications switching systems such as the DMS-100. Protel-2 is the object-oriented version of Protel.

The PROTEL language was designed to meet the needs of digital telephony and is the basis of the DMS-100 line of switching systems PROTEL is a strongly typed, block-structured language which is based heavily on PASCAL and ALGOL 68 with left-to-right style of variable assignment, variable-sized arrays, and extensible structures. The designers of PROTEL significantly extended PASCAL of the day by adding external compilation and extending the data structures available in the language.

The PROTEL compiler is tightly integrated with the operating system (SOS), application (CALLP), the development environment (PLS) and originally the processor (NT40). PLS, SOS, CALLP and the compiler itself are all written in PROTEL. Any description of the PROTEL language can't help but include some aspects of the other components.

PROTEL has very strict type enforcement but the tight coupling of the components creates opportunities to bypass some type checking for skilled coders by using internal compiler features directly.

PROTEL is considered 'wordy', containing a large number of reserved words with some statements reading like English.

PROTEL source code is case insensitive but by convention upper case is used for reserved words.

Variables and Assignment
Most global and all local variables are declared using the DECL reserved word.


 * DECL myvar INT;

Variables can be initialized using INIT keyword.


 * DECL myvar INT INIT 42;

Constants use IS keyword, hexadecimal uses #.


 * DECL myconst INT IS #F00D;

Global variables can also use PROTECTED, or PRIVATE declarations to define write protected data or thread local data respectively. Writes to protected data requires the use of builtin primitives that alter protected data in a safe way. Protected data survives all restarts short of a system image reload.

Note WRITE_PROTECTED_STORE arguments are type agnostic as long as the types of both arguments match.
 * PROTECTED myprotdata INT;
 * write_protected_store( myprotdata, value );

PRIVATE provides a private copy of the data for each process that uses it. There is no COW functionality, each process is allocated its own copy and optionally initializes it at creation.

Note Local variables defined using DECL are naturally private.
 * PRIVATE myprivatedata INT;

GAZINTA
Gazinta is the colloquial name used for the assignment operator '->'. Its name comes from 'goes into' meaning expression gazinta (goes into) variable. Expressions are evaluated in strict left to right order with no operator precedence. Lack of operator precedence is a legacy of the NT40 processor which used a stack based ALU using RPN logic. Parentheses are used to prioritize subexpressions.


 * expression -> myvar;

Pointers
The pointer operator is @ and is placed after the ptr. A NULL pointer is a predefined value that is guaranteed not to point to a mapped address. It is not 0 nor -1 nor another the predefined uninitialized memory pattern (#FDFD). In other words, a pointer is not NULL by default, it must be set in the code. Pointer arithmetic is not supported.

Note Dereferencing a NULL pointer will cause a data access trap.
 * DECL myptr PTR TO INT INIT NULL;
 * DECL mydata INT;
 * myptr@ -> mydata;

Descriptors
The descriptor 'DESC' is a compiler defined structure containing a pointer to an array and the array upperbound. A descriptorized array is indexed using simple array syntax 'MY_DESC[I]' and the UPB operator can be used to access the upperbound 'UPB MYDESC'. A TDSIZE operator also exists as a legacy of the descriptor implementation on NT40: TDSIZE = UPB+1. Descriptors have automatic run time bounds checking on dynamically allocated arrays. Indexing a descriptor out of bounds or indexing a NULL descriptor will cause a descriptor range check trap. UPB is generally used by application software to more gracefully handle index out of range.

Note NULL can be used for pointers and descriptors.
 * DECL my_int_array DESC OF INT INIT NULL;

Strings use descriptors extensively. Strings are of a defined length with no NULL character terminator.


 * DECL foo DESC OF CHAR IS "my string named foo";

Blocks
Block scope is defined by BLOCK and ENDBLOCK statements, which are analogous to BEGIN END in Pascal or { } in C. Blocks can optionally be labeled for enhanced compiler checking and functionality.


 * [label:] BLOCK
 * some code
 * EXIT [label];
 * more code
 * ENDBLOCK [label]

Procedures
Procedures and functions are only differentiated by the presence of the RETURNS clause and the requirement to include a RETURN statement. A RETURN statement may be inserted anywhere in a function or procedure.

Declaration
 * PROC myprocname( argument list ) IS FORWARD;
 * PROC myfuncname( argument list ) RETURNS type IS FORWARD;

Implementation
 * PROC myfuncname( argument list ) RETURNS type IS
 * BLOCK
 * ...code...
 * RETURN some_value;
 * ENDBLOCK

Structures
The TABLE is the basic array structure. TABLE is only used for arrays whose size is known at compile time. Descriptors are the preferred way to reference arrays. Indexing a table out of range will cause a table range check trap. UPB and TDSIZE operators also apply to tables.

Unions
The OVERLAY is the basic union structure. It is declared and used in a manner similar to Pascal-descended languages.

AREAs
Areas are memory blocks that could be cast to TABLES and OVERLAYS. They are declared in bytes and typically are declared large enough to allow for future expansion. This is due to the desire to upgrade DMS software 'live' without requiring a restart. The modular nature of PROTEL allows relatively small chunks of code to be swapped into a load; if AREAs were planned smartly, this would not affect the placement of modules in memory, thereby avoiding a restart.

SUBSYSTEMs, MODULEs, and SECTIONs
A SECTION is a text file containing source code and directives to manage the different section types and their position in the module hierarchy. A SECTION is the smallest unit of compilation. A MODULE is the smallest unit of linkage and loading and contains one or more sections. A SUBSYSTEM is the smallest unit of packaging and contains one or more modules.

Control Flow
There are 2 forms of the case (switch) statement. Using the CASE keyword uses a jump table and SELECT uses sequential if-then-else logic. Cases are exclusive, they don't fall through as in C.