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 enum
s:
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 union
s.
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.