PLUS - Language Features

Let’s look in more detail at some of the features of the PLUS language.

Format

PLUS is free format and case-insensitive, so the variable count is the same as COUNT. Comments are the same as PL/I and C, ie /* ... /*. Statements are separated by semicolons. Input files to the compiler contain procedures and global definitions so they can be compiled separately.

Variables and types

Variables must be defined before use and given a type. They can then be assigned to with :=. Numeric types must be given a range, for example:

variable Count is (1 to 5);
variable Temperature is (-100 to 100);
Count := 1;
Temperature := -100;

Type definitions can be names with type and multiple definitions can be done on the same line:

type Temperature_Type is (-100 to 100);
variable Current_Temp is Temperature_Type;
variables High_Temp, Low_Temp are Temperature_Type;

The third line above is an example of multiple definitions on the same line; the variables and are keywords are just syntactical sugar for the singular forms.

Fixed string types include the length of the string, variable strings contain a min/max length. So in the below, My_Name is varying. Strings can be concatenated with ||.

variable My_Name is character(50);
variables First_Name, Last_Name are character(1 to 25);
First_Name := "Jane";
Last_Name := "Doe";
My_Name := First_Name || Last_Name;

Bit types allow precise control over the amount of memory used:

variable Full_Word is bit(32);
variable Byte is bit(8);

There are type attributes such as aligned or packed for further fine-grained control.

Identifiers are like C enums:

variable Device is (Reader, Punch, Printer);

There are also real and set types defined in the language but not fully implemented - so you can create a real and assign to it but not do arithmetic.

Arrays have an index (which can be non-numeric) and a value

variable Lookup is array character(1) of character(8);
variable Matrix is array (1 to 10) of array (1 to 10) of Temperature_Type;

The size of the array must be determined at compile time. Elements can be accessed via round brackets.

Pointers work similarly to C pointers but use the suffix @ for de-referencing and the built-in procured Address for assignment.

variable Ptr is pointer to Count_Type;
type Count_Type is (1 to 10);
variable x is Count_Type;

x := 5;
Ptr := Address(x);
Ptr@ +:= 1;

Note that the type of what the pointer is accessing (here Count_Type) can be defined after it is introduced. Note also the +:= form to increment a value; here the underlying variable x pointed to by Ptr is updated to be 6.

Record types define structures.

type My_Record is
   record
      a is (1 to 1),
      b is character(50)
   end;

There are also variant records which allow something similar to C’s unions.

equate allows aliasing of variables or variable expressions.

Procedures

Procedures need to be declared before they are defined. They can also be treated as types.

/* Pull in definition of Integer */
%Include(Numeric_Types);

type Arithmetic_Procedure is
   procedure
   parameters
      x is Integer,
      y is Integer
   result
      answer is Integer
   end;

/* Declaration */
procedure Plus is Arithmetic_Procedure;

/* Definition */
definition Plus;
  answer := x + y;
end Plus;

/* ... */
variable x is Integer;
x := Plus(2, 3);

Parameters are passed by value by default; you can define them as reference parameter to pass a reference in instead, and the compiler will behind the scenes pass a pointer and de-reference it in the procedure.

Expressions and control structures

The usual range of arithmetic and logical expressions are supported. There is no separate Boolean type but anything non zero is counted as true.

For conditionals, there is if ... then ... elseif ... else... end if and select ... case ... end select.

The cycle statement starts a infinite loop that can be broken out of using exit or restarted with repeat, which can be used to write loops. An example from the tutorial:

variables Count, Return_Code are Integer,
String is Varying_String;
Count := 0;
/* Loop reading and writing records, counting number copied. */
cycle
   Scards_Varying(String, Return_Code);
   if Return_Code ¬= 0
   then
      /* Terminate loop when no more input */
      exit
   end if;
   Spunch_Varying(String);
   Count := Count + 1
end cycle;

A more restrictive do statement allows single variable incrementing or decrementing, like we saw in the Hello World program in part 1.

begin ... end can be used to start a new block with its own scoped variables, and supports exit and break like cycle.

Constants, macros and compiler directives

Compile time constants can be defined as follows

constant Zero_Temp is 0;

and can be used in type definitions as well as variable initialisation.

Macros provide a procedure like interface and copy the text of the macro into the current scope.

Compiler directives start with %. We’ve seen %Title in part 1 and $Include above; other directives control things like compiler listing output and extra code checks.

Input/output and the standard library

There are only a few built in procedures in PLUS; most functionality is provided by the standard source library *PLUS.SOURCELIB. This include I/O routines like Sprint_String or Scards_Varying which respectively send a fixed string to the output device and read a varying string from the input device.

A more complex, printf style procedure called Message can be used for string printing.

Message(Msg, "<hi> items found at <v></>", count, location);

This will print the halfword integer and varying strings included in the parameter list. The </> prints a new line. (I think the resemblance to HTML-like tags is coincidental, as PLUS predates even SGML of which HTML was a subset).

The standard source library also includes many routines to call MTS system procedures so you can control devices, file attributes directly from PLUS.

Further information

As mentioned in part 1, the main language tutorial and reference is at Bitsavers along with the source library reference.

Comments

comments powered by Disqus