VMS Pascal for C/Java/C# programmers

Content:

  1. Introduction
  2. Pascal
  3. Usage
  4. Source format
  5. Data types
  6. Operators and functions
  7. Control structures
  8. Procedures and functions
    1. Pascal
    2. Non-pascal
  9. IO
    1. text
    2. binary
    3. index-sequential
  10. Modules
  11. OOP and FP
  12. 32 vs 64 bit

Introduction:

Pascal is the most modern of the traditional VMS languages and relative high level (significant higher than C). And therefore a good choice for new business application code that has to be native (for whatever reason Java, Groovy, Python, PHP etc. are not an option).

VMS Pascal as other VMS compilers comes with extensive documentation:

  1. Reference Manual
  2. User Manual

And there are also extensive help:

$ help pas
$ help pas data
$ help pas stat
$ help pas pred
...

This article is not trying to redo those.

This article is targetting a very specific case:

Those that learned programming somewhere between late 70's and early 90's probably have learned Pascal as it was widely used for teaching programming back then. But later then curly bracket languages got in fashion.

The approach is practical. This article will not cover everything - it will cover a some of the common stuff that a C/Java/C# programmer will want to know about.

There will be no attempt to distinguish between what is standard and what is VMS specific.

Alle examples will be super simple.

The style is very brief. You are expected to know everything except VMS Pascal syntax.

For all the details use the documentation linked to above.

Pascal:

First a little bit about the Pascal language to set the context.

Pascal was created by Niklaus Wirth in 1970.

It was heavily inspired by ALGOL.

It did inspire later languages like Modula-2 and Ada.

Multiple flavors of the language exists:

VMS Pascal is the topic here.

Usage:

Basic usage is very straigthforward:

Example:

$ edit foobar.pas
$ pas foobar
$ link foobar
$ run foobar

Any editor can be used.

EDT and EVE comes with VMS. LSE (Language Sensitive Editor) comes with DECset suite and knows about VMS Pascal.

If editing on a PC, then VSI supply VMS IDE which is based on VS Code and knows about VMS Pascal, but any editor like notepad++ or JEdit can be used.

Source format:

A typical (and my personal preference) structure of a Pascal program is:

program header
constant declarations
type declarations
procedure/function declarations
variable declarations
program body

with a procedure/function declaration like:

procedure/function header
variable declarations
procedure/function body

Example:

program outline(input,output);

const
   ONE = 1;

type
   myinteger = integer;

procedure add_one_and_print(v : myinteger);

var
   temp : myinteger;

begin
   temp := v + ONE;
   writeln(temp:3);
end;

var
   v : myinteger;

begin
   v := 123;
   add_one_and_print(v);
end.

Output:

124

The curly brackets { ... } are replaced by begin ... end.

Pascal is not case sensitive. FOOBAR, foobar and FooBar are the same.

Note that the last end (the program/module body end) is followed by a period not by a semicolon.

Comments are neither /* ... */ block or // ... single line comments but (* ... *) block or { ... } block comments.

Data types:

Literals in VMS Pascal are different from typical curly bracket languages:

VMS Pascal C (*) Java C#
123 123 123 123
16#1B 0x1B 0x1B 0x1B
'ABC' "ABC" "ABC" "ABC"
'X''X' "X\"X" "X\"X" "X\"X"

VMS Pascal has almost all the basic data types:

VMS Pascal C (*) Java C#
integer8 int8_t byte sbyte
integer16 int16_t short short
integer (**)/integer32 int32_t int int
integer64 int64_t long long
unsigned8 uint8_t N/A byte
unsigned16 uint16_t N/A ushort
cardinal (**)/unsigned32 uint32_t N/A uint
unsigned64 uint64_t N/A ulong
single float float float
double double double double
quadruple long double (***) N/A N/A
boolean bool boolean bool
*) newer C with stdint.h, stdbool.h etc..
**) these are the ones almost alyways used in practice
***) on many platforms incl. VMS unless /L_DOUBLE=64

Besides the integer types it is also possible (and recommendable!) to use integer ranges.

var
    v : integer;

tell that v is an integer in the interval –2147483648 to 2147483647. That interval makes good sense for CPU's, but it does not make sense in the typical business application.

var
    v : 10000..99999;

tell that v is an integer in the interval 10000 to 99999 and that interval (5 digits) may make sense in the business application.

For text data there are 4 types:

char
single char, 8 bit characterset, treated as text not as integer
packed array [n1..n2] of char
fixed length string, 8 bit characterset, array index from n1 to n2
varying [n] of char
variable length string, 8 bit characterset, max length n, n <= 65535
string(n)
similar to varying [n] of char

Example:

program data(input,output);

type
   fixstr10 = packed array [1..10] of char;
   passtr = varying [255] of char;

var
   s : integer;
   u : cardinal;
   s8 : integer8;
   s16 : integer16;
   s32 : integer32;
   s64 : integer64;
   u8 : unsigned8;
   u16 : unsigned16;
   u32 : unsigned32;
   u64 : unsigned64;
   x32 : single;
   x64 : double;
   x128 : quadruple;
   b : boolean;
   fstr : fixstr10;
   vstr : passtr;

begin
   s := -123;
   writeln(s:4);
   u := 123;
   writeln(u:4);
   s8 := -123;
   writeln(s8:4);
   s16 := -123;
   writeln(s16:4);
   s32 := -123;
   writeln(s32:4);
   s64 := -123;
   writeln(s64:4);
   u8 := 123;
   writeln(u8:4);
   u16 := 123;
   writeln(u16:4);
   u32 := 123;
   writeln(u32:4);
   u64 := 123;
   writeln(u64:4);
   x32 := 123.456;
   writeln(x32:7:3);
   x64 := 123.456;
   writeln(x64:7:3);
   x128 := 123.456;
   writeln(x128:7:3);
   b := true;
   writeln(b);
   fstr := 'ABC';
   writeln('|' + fstr + '|');
   vstr := 'ABC';
   writeln('|' + vstr + '|');
end.

Output:

-123
 123
-123
-123
-123
-123
 123
 123
 123
 123
123.456
123.456
  TRUE
|ABC       |
|ABC|

It is common to use type declaration to define an alias for types used similar to C typedef.

Note that quadruple does not work on VMS x86-64 up to at least 9.2-3.

VMS Pascal C Java C#
foobar = (a,b,c,d); enum foobar { a, b, c, d }; enum Foobar { a, b, c, d }; enum Foobar { a, b, c, d };
array [n1..n2] of something something[n] something[] something[]
typnam = record
    field1 : type1;
    ...
    fieldn : typen;
end;
struct typnam {
    type1 field1;
    ...
    typen fieldn;
}
N/A
public struct typnam {
    public type1 field1;
    ...
    public typen fieldn;
}
typnam = record
    case typ of:
        val1 : ( field1 : type1; );
        ...
        valn : ( fieldn : typen; );
end;
union typnam {
    type1 : field1;
    ...
    typen : fieldn;
}
N/A
[StructLayout(LayoutKind.Explicit)]
public struct typnam {
    [FieldOffset(0)] public type1 field1;
    ...
    [FieldOffset(0)] public typen fieldn;
}

Example:

program data2(input,output);

type
   langtyp = (procedural, objectoriented, functional, generic);
   passtr = varying [255] of char;
   intarr3 = array [1..3] of integer;
   demostruct = record
                   iv : integer;
                   xv : double;
                   sv : passtr;
                end;
   demounion = record
                  case boolean of
                     true : ( v32 : integer32; );
                     false : ( v8 : packed array [1..4] of integer8; );
               end;

var
   lang : langtyp;
   ia : intarr3;
   demos : demostruct;
   demou : demounion;

begin
   lang := procedural;
   writeln(lang);
   ia[1] := 123;
   ia[2] := 456;
   ia[3] := 789;
   writeln(ia[1]:3, ' ', ia[2]:3, ' ', ia[3]:3);
   demos.iv := 123;
   demos.xv := 123.456;
   demos.sv := 'ABC';
   writeln(demos.iv:3, ' ', demos.xv:7:3, ' ', demos.sv);
   demou.v32 := 258;
   writeln(demou.v8[1]:2, demou.v8[2]:2, demou.v8[3]:2, demou.v8[4]:2);
end.

Output:

     PROCEDURAL
123 456 789
123 123.456 ABC
 2 1 0 0

Pascal has pointers, but Pascal pointers are more like Java/C# references than C pointers.

Pascal pointers can be used to allocate, to reference the allocated and to free the allocated. No fancy pointer arithmetic.

Pascal uses nil instead of NULL/null for the pointer to nothing.

The syntaxes are:

^type;
pointer to type
new(ptrvar);
allocate
ptrvar^
reference allocated
dispose(ptrvar);
free allocated

Example:

program dyn(input,output);

type
   rec = record
            val : integer;
            next : ^rec;
         end;

var
   first, curr, temp : ^rec;

begin
   new(first);
   curr := first;
   curr^.val := 123;
   new(curr^.next);
   curr := curr^.next;
   curr^.val := 456;
   new(curr^.next);
   curr := curr^.next;
   curr^.val := 789;
   curr^.next := nil;
   curr := first;
   while curr <> nil do begin
      writeln(curr^.val);
      curr := curr^.next;
   end;
   curr := first;
   while curr <> nil do begin
      temp := curr^.next;
      dispose(curr);
      curr := temp;
   end;
end.

Output:

       123
       456
       789

Operators and functions:

VMS Pascal has all the usual operators, but the symbol used are different for some of them:

VMS Pascal C Java C#
***** the ones to look out for *****
:= = = =
= == == ==
<> != != !=
DIV / (for integer args) / (for integer args) / (for integer args)
/ / (for floating point args) / (for floating point args) / (for floating point args)
MOD % % %
NOT ! ! !
AND && && &&
OR || || ||
***** as expected *****
+ + + +
- - - -
* * * *
< < < <
> > > >
<= <= <= <=
>= >= >= >=

The most important things to note are that:

VMS Pascal has a long list of builtin procedures/functions, but here is a list of some of the most relevant:

VMS Pascal C Java C#
c := chr(iv); c = (char)iv; c = (char)iv; c = (char)iv;
iv := ord(c); iv = (int)c; iv = (int)c; iv = (int)c;
iv := trunc(xv); (* towards zero *) iv = (int)floor(xv); /* down */
iv = (int)trunc(xv); /* towards zero */
iv = (int)xv; /* towards zero */
iv = (int)Math.floor(xv); // down iv = (int)Math.Floor(xv); // down
iv = (int)Math.Truncate(xv); // towards zero
iv := round(xv); iv = lround(xv);
iv = (int)floor(xv + 0.5);
iv = (int)(xv + 0.5);
iv = (int)Math.round(xv); iv = (int)Math.Round(xv);
x**n pow(x, n) Math.pow(x, n) Math.Pow(x, n)
exp(x) exp(x) Math.exp(x) Math.Exp(x)
ln(x) log(x) Math.log(x) Math.Log(x)
sqrt(x) sqrt(x) Math.sqrt(x) Math.Sqrt(x)
len := length(s); len = strlen(s); len = s.length; len = s.Length;
ix := index(s, 'something'); s2 = strstr(s, "something"); ix = s.index("something); ix = s.Index("something");
s2 := substr(s, ix, len); strncpy(s2, s + ix, len);
s2[len] = 0;
s2 = s.substring(ix1, ix2); s2 = s.Substring(ix, len);

Control structures:

VMS Pascal has the usual control structures:

VMS Pascal C Java C#
if cond then begin
    ...
end;
if(cond)
{
    ...
}
if(cond) {
    ...
}
if(cond)
{
    ...
}
case v of
    val1 : begin
               ...
           end;
    ...
    valn : begin
               ...
           end;
    otherwise begin
                  ...
              end;
end;
switch(v)
{
    case val1:
        ...
        break;
    ...
    case valn:
        ...
        break;
    default:
        ...
        break;
}
switch(v) {
    case val1:
        ...
        break;
    ...
    case valn:
        ...
        break;
    default:
        ...
        break;
}
switch(v)
{
    case val1:
        ...
        break;
    ...
    case valn:
        ...
        break;
    default:
        ...
        break;
}
for i := n1 to n2 do begin
    ...
end;
for(i = n1; i <= n2; i++)
{
    ...
}
for(i = n1; i <= n2; i++) {
    ...
}
for(i = n1; i <= n2; i++)
{
    ...
}
while cond do begin
    ...
end;
while(cond)
{
    ...
}
while(cond) {
    ...
}
while(cond)
{
    ...
}
repeat
    ...
until cond;
do
{
    ...
}
do {
    ...
}
do
{
    ...
}

Example:

program ctl(input,output);

var
   v, i : integer;

begin
   v := 1;
   if v = 0 then begin
      writeln('zero');
   end else begin
      writeln('not zero');
   end;
   case(v) of
      0 : begin
             writeln('zero');
          end;
      1 : begin
             writeln('one');
          end;
      otherwise begin
                   writeln('not zero or one');
                end;
   end;
   for i := 1 to 3 do begin
      write(i:2);
   end;
   writeln;
   i := 0;
   while i < 3 do begin
      i := i + 1;
      write(i:2);
   end;
   writeln;
   i := 0;
   repeat
      i := i + 1;
      write(i:2);
   until i >= 3;
   writeln;
end.

Output:

not zero
one
 1 2 3
 1 2 3
 1 2 3

Just like the curly brackets can be omitted for a single statement in C/Java/C#, then in Pascal begin end can be omitted for a single statement.

But both in C/Java/C# and in Pascal it is considered good practice to have them also for a single statement.

Procedures and functions

Pascal:

Pascal distinguish between functions returning a value and procedures not returning a value.

VMS Pascal C Java C#
procedure name(arg1, ..., argn);

...

begin
    ...
end;
void name(arg1, ..., argn)
{
    ...
}
void name(arg1, ..., argn) {
    ...
}
void name(arg1, ..., argn)
{
    ...
}
function name(arg1, ..., argn) : returntype;

...

begin
    ...
    name := ...;
end;
returntype name(arg1, ..., argn)
{
    ...
    return ...;
}
returntype name(arg1, ..., argn) {
    ...
    return ...;
}
returntype name(arg1, ..., argn)
{
    ...
    return ...;
}
argname : argtype argtype argname argtype argname argtype argname
var argname : argtype argtype *argname N/A ref argtype argname

Example:

program rtn(input,output);

function addf(a, b : integer) : integer;

begin
   addf := a + b;
end;

procedure addp(a, b : integer; var c : integer);

begin
   c := a + b;
end;

procedure testfac;

function fac(n : integer) : integer;

begin
   if n < 2 then begin
      fac := 1;
   end else begin
      fac := n * fac(n - 1);
   end;
end;

var
   i : integer;

begin
   for i := 1 to 5 do begin
      writeln('fac(', i:1, ')=', fac(i):1);
   end;
end;

var
   a, b , c : integer;

begin
   a := 123;
   b := 456;
   c := addf(a, b);
   writeln(c:3);
   addp(a, b, c);
   writeln(c:3);
   testfac;
end.

Output:

579
579
fac(1)=1
fac(2)=2
fac(3)=6
fac(4)=24
fac(5)=120

Note that Pascal allow procedures/functions inside procedures/functions to limit scope. In the above example fac is actually declared inside testfac.

VMS Pascal supports both default values and named arguments for calls.

Example:

program args(input,output);

procedure test(a : integer := 1; b : integer := 2; c : integer := 3; d : integer := 4);

begin
   writeln(a:1, ' ', b:1, ' ', c:1, ' ', d:1);
end;

begin
   test(11, c := 33);
end.

Output:

11 2 33 4

VMS Pascal supports variable number of arguments.

VMS Pascal C Java C#
procedure demo(v : [list] integer);

var
   n, i, v1 : integer;

begin
   n  := argument_list_length(v);
   for i := 1 to n do begin
      v1 := argument(v, i);
      ...
   end;
end;
...
   demo(1);
   demo(1, 2);
   demo(1, 2, 3);
#include <stdarg.h>
...
void demo(int n, ...)
{
    va_list ap;
    va_start(ap, n);
    for(int i = 0; i < n; i++)
    {
        int v1 = va_arg(ap, int);
        ...
    }
    va_end(ap);
}
...
    demo(1, 1);
    demo(2, 1, 2);
    demo(3, 1, 2, 3);
    public static void demo(int... v) {
        int n = v.length;
        for(int i = 0; i < n; i++) {
            int v1 = v[i];
            ...
        }
    }
    ...
        demo(1);
        demo(1, 2);
        demo(1, 2, 3);
public static void Demo(params int[] v)
{
    int n = v.Length;
    for(int i = 0; i < n; i++)
    {
        int v1 = v[i];
        ...
    }
}
...
    Demo(1);
    Demo(1, 2);
    Demo(1, 2, 3);

Non-pascal:

VMS Pascal has always supported calling code in other languages. Back in the days Macro-32, Fortran etc. code. In the last few decades a lot of C.

The syntax is:

[external]
procedure name(arg1, ..., argn); external;

[external]
function name(arg1, ..., argn) : returntype; external;

where argx is in the form:

mechanism name : type

and mechanism is one of %immed, %ref or %stddescr.

Let us see an example.

      subroutine f(iv,sv)
      integer*4 iv
      character*(*) sv
      write(*,'(1x,a,2h: ,i3,1h ,a)') 'Fortran',iv,sv
      return
      end
#include <stdio.h>

void c(int iv, char *sv)
{
    printf("C: %d %s\n", iv, sv);
}
program fc(input,output);

[external]
procedure f(%ref iv : integer; %stdescr sv : packed array [l..u:integer] of char); external;

[external]
procedure c(%immed iv : integer; %immed sv : c_str_t); external;

begin
   f(123,'ABC');
   c(123, malloc_c_str('ABC'));
end.

Build and run:

$ for f
$ cc c
$ pas fc
$ lin fc+f+c
$ r fc

Output:

Fortran: 123 ABC
C: 123 ABC

The code as shown does not free the memory allocated by malloc_c_str. That should obvious be freed in real code.

IO

text:

VMS Pascal text IO is very simple.

VMS Pascal C Java C#
readln(line); fgets(line, sizeof(line), stdin); /* note line will include \n */ line = br.readLine(); line = sr.ReadLine();
writeln(iv, ' ', xv, ' ', sv); printf("%d %f %s\n", iv, xv, sv); System.out.printf("%d %f %s\n", iv, xv, sv); Console.WriteLine("{0} {1} {2}", iv, xv, sv);
writeln(iv:3, ' ', xv:7:3, ' ', sv); printf("%3d %7.3f %s\n", iv, xv, sv); System.out.printf("%3d %7.3f %s\n", iv, xv, sv); Console.WriteLine("{0:3} {1:7F3} {2}", iv, xv, sv);
open(f, fnm, old);
reset(f);
f = fopen(fnm "r"); br = new BufferedReader(new FileReader(fnm)); sr = new StreamReader(fnm)
open(f, fnm, new);
rewrite(f);
f = fopen(fnm, "w"); pw = new PrintWriter(new FileWriter(fnm)); sw = new StreamWriter(fnm);
close(f); fclose(f); f.close(); f.Close();
while not(eof(f)) do
while not eof(f) do
while((c = fgetc(f)) != EOF)
while(fgets(line, sizeof(line), f))
while((line = br.readLine()) != null) while((line = sr.ReadLine()) != null)
readln(f, line); fgets(line, sizeof(line), f); /* note line will include \n */ line = br.readLine(); line = sr.ReadLine();
writeln(f, iv, ' ', xv, ' ', sv); fprintf(f, "%d %f %s\n", iv, xv, sv); pw.printf("%d %f %s\n", iv, xv, sv); sw.WriteLine("{0} {1} {2}", iv, xv, sv);
readv(s, v); sscanf(s, "%d", &v); v = Integer.parseInt(s); v = int.Parse(s);
writev(s, v:3); sprintf(s, "%3d", v); s = String.format("%3d", v); s = string.Format("{0:3}", v);

Example:

program copy(input,output);

var
   f1, f2 : text;
   line : varying [255] of char;

begin
   open(f1, 'z1.txt', old);
   reset(f1);
   open(f2, 'z2.txt', new);
   rewrite(f2);
   while not(eof(f1)) do begin
      readln(f1, line);
      writeln(f2, line);
   end;
   close(f2);
   close(f1);
end.

binary:

VMS Pascal binary IO is a bit more different.

Basically it is a file of records that is accessed via a buffer pointer and data is moved between the file and the buffer via get and put functions.

Easier to show with an example:

type
   pstr = varying [255] of char;
   rec = record
            iv : integer;
            xv : double;
            sv : pstr;
         end;

program binw(input,output);

%include 'bin.inc'

var
   f : file of rec;

begin
   open(f, 'z.dat', new);
   rewrite(f);
   f^.iv := 123;
   f^.xv := 123.456;
   f^.sv := 'ABC';
   put(f);
   f^.iv := 456;
   f^.xv := 456.789;
   f^.sv := 'DEF';
   put(f);
   f^.iv := 789;
   f^.xv := 789.123;
   f^.sv := 'GHI';
   put(f);
   close(f);
end.
program binr(input,output);

%include 'bin.inc'

var
   f : file of rec;

begin
   open(f, 'z.dat', old);
   reset(f);
   while not(eof(f)) do begin
      writeln(f^.iv:3, ' ', f^.xv:7:3, ' ', f^.sv);
      get(f);
   end;
   close(f);
end.

Output:

123 123.456 ABC
456 456.789 DEF
789 789.123 GHI

Note that we include the type definition to ensure consistency.

Also note that such a file of records are a RFM=FIX file.

index-sequential:

VMS Pascal has builtin support for index-sequential files. That is really builtin support for a NoSQL database (Key Value Store).

Super cool feature!

Technically it is very similar to the binary IO in previous section. Just:

type
   fixstr10 = packed array [1..10] of char;
   pstr = varying [255] of char;
   rec = record
            key1 : [key(0)] integer;
            key2 : [key(1)] fixstr10;
            ival : integer;
            sval : pstr;
         end;

program isqw(input,output);

%include 'isq.inc'

var
   f : file of rec;

begin
   open(f, 'z.isq', new, organization := indexed, access_method := keyed);
   rewrite(f);
   f^.key1 := 1;
   f^.key2 := 'A';
   f^.ival := 123;
   f^.sval := 'This is test #1';
   put(f);
   f^.key1 := 2;
   f^.key2 := 'BB';
   f^.ival := 456;
   f^.sval := 'This is test #2';
   put(f);
   f^.key1 := 3;
   f^.key2 := 'CCC';
   f^.ival := 789;
   f^.sval := 'This is test #3';
   put(f);
   close(f);
end.
program isqr(input,output);

%include 'isq.inc'

var
   f : file of rec;

begin
   open(f, 'z.isq', new, organization := indexed, access_method := keyed);
   rewrite(f);
   f^.key1 := 1;
   f^.key2 := 'A';
   f^.ival := 123;
   f^.sval := 'This is test #1';
   put(f);
   f^.key1 := 2;
   f^.key2 := 'BB';
   f^.ival := 456;
   f^.sval := 'This is test #2';
   put(f);
   f^.key1 := 3;
   f^.key2 := 'CCC';
   f^.ival := 789;
   f^.sval := 'This is test #3';
   put(f);
   close(f);
end.
$ typ isqr.pas
program isqw(input,output);

%include 'isq.inc'

var
   f : file of rec;

procedure find0(k : integer);

begin
   findk(f, 0, k);
   if not(ufb(f)) then begin
      writeln(f^.ival:3, ' ', f^.sval);
   end else begin
      writeln('Not found: ', k:1);
   end;
end;

procedure find1(k : fixstr10);

begin
   findk(f, 1, k);
   if not(ufb(f)) then begin
      writeln(f^.ival:3, ' ', f^.sval);
   end else begin
      writeln('Not found: ', k);
   end;
end;

begin
   open(f, 'z.isq', old, organization := indexed, access_method := keyed);
   reset(f);
   find0(2);
   find0(4);
   find1('BB');
   find1('DDDD');
   close(f);
end.

Output:

456 This is test #2
Not found: 4
456 This is test #2
Not found: DDDD

It is very similar to the annotation/attribute base ORM frameworks that are common in non-native programming languages (mostly outside of VMS).

Modules:

All the above examples show one Pascal source file being compiled to one EXE.

But it is possible to create libraries in VMS Pascal and reuse those from multiple programs.

This is done using modules.

VMS Pascal C Java C#
module mylib(input, output);

type
    myinteger = integer;

procedure myprint(v : myinteger);

begin
    writeln(v);
end;

end.
#ifndef MYLIB_H
#define MYLIB_H

typedef int myint;

void myprint(myint v);

#endif
#include <stdio.h>

#include "mylib.h";

void myprint(myint v)
{
    printf("%d\n", v);
}
public class MyLib {
    public static void myPrint(int v) {
        System.out.println(v):
    }
}
public class MyLib {
    public static void MyPrint(int v) {
        Console.WriteLine(v):
    }
}
[inherit('mylib')]
program myprog(input, output);

var
    v : myinteger;
    
begin
    v := 123;
    myprint(v);
end.
#include "mylib.h";

int main()
{
    myint v = 123;
    myprint(v);
    return 0;
}
public class MyProg {
    public static void main(String[] args) {
        int v = 123;
        MyLib.myPrint(v);
    }
}
public class MyProg {
    public static void Main(string[] args) {
        int v = 123;
        MyLib.MyPrint(v);
    }
}
$ pas/env mylib
$ pas myprog
$ link myprog + mylib
$ run myprog
cc -c mylib.c -o mylib.obj
cc myprog.c mylib.obj -o mylib.exe
myprog
javac MyLib.java
javac -cp . MyProg.java
java -cp . MyProg
csc /t:library MyLib.cs
csc /t:exe /r:MyLib.dll MyProg.cs
MyProg

The inherit attribute makes the program inherit all types, procedures and functions from the module.

VMS Pascal ships with some modules that provide interface to system services and various RTL's, including:

sys$library:starlet
system services
sys$libray:pascal$lib_routines
LIB$ RTL functions

Example:

[inherit('sys$library:pascal$lib_routines')]
program sys(input,output);

type
   pstr = varying [255] of char;

var
   cmdlin, sym, log : pstr;

begin
   lib$get_foreign(cmdlin.body,,cmdlin.length);
   writeln(cmdlin);
   lib$get_symbol('HOME', sym.body, sym.length);
   writeln(sym);
   lib$get_logical('JAVALIB', log.body, log.length);
   writeln(log);
end.

These 3 LIB$ calls all expect a string and a return length in the arguments.

That is easy to provide by utilizing the fact that:

type
    pstr = varying [255] of char;
    

is equivalent of:

type
    pstr = record
              length : unsigned16;
              body : packed array [1..255] of char;
           end;
           

which is why .body and .length in the above LIB$ call examples works!

OOP and FP:

VMS Pascal is a procedural only language. No classes, no lambdas etc..

And in general it is a bad thing to try and use a language for something else than it is intended for.

But VMS Pascal has certain features that can be used to implement OO-like encapsulation.

Example:

module oo(input,output);

type
   clz = [hidden] record
            counter : integer;
         end;
   clzptr = [hidden] ^clz;

function clz_new : pointer;

var
   p : clzptr;

begin
   new(p);
   p^.counter := 0;
   clz_new := p;
end;

procedure clz_increment(obj : pointer);

var
   p : clzptr;

begin
   p := obj;
   p^.counter := p^.counter + 1;
end;

function clz_get(obj : pointer) : integer;

var
   p : clzptr;

begin
   p := obj;
   clz_get := p^.counter;
end;

procedure clz_delete(obj : pointer);

var
   p : clzptr;

begin
   p := obj;
   dispose(p);
end;

end.
[inherit('oo')]
program ootest(input,output);

var
   o : pointer;

begin
   o := clz_new;
   clz_increment(o);
   clz_increment(o);
   clz_increment(o);
   writeln(clz_get(o));
   clz_delete(o);
end.

The content of the record clz is inaccessible for code outside the module.

The hidden attribute can also be put on procedures/functions to make them inaccessible for code outside the module.

Pascal also has the ability to pass functions as arguments in procedure/function calls.

Example:

program fp(input,output);

const
   EPS = 0.000001;

function diff(x : double; function f(x : double) : double) : double;

begin
   diff := (f(x + EPS) - f(x)) / EPS;
end;

function diff2(x : double; function f(x : double) : double) : double;

begin
   diff2 := (diff(x + EPS, f) - diff(x, f)) / EPS;
end;

function newton(function f(x : double) : double) : double;

var
   x, xprev : double;

begin
   x := 1;
   repeat
      xprev := x;
      x := xprev - diff(x, f) / diff2(x, f);
   until abs(x - xprev) < EPS;
   newton := x;
end;

function testf(x : double) : double;

begin
   testf := (x - 2) * (x - 2);
end;

begin
   writeln(newton(testf));
end.

32 vs 64 bit:

VMS Pascal as of V6.3/6.4 on VMS 8.4/9.2 is mostly using 32 bit addresses/pointers.

...
type
   charptr = ^char;
   qcharptr = [quad] ^char;

var
   loc : char;
   sta : [static] char;
   p : charptr;
   qp : qcharptr;

begin
   ...
   new(p);
   ...
   new(qp);
   ...

here:

For more about this topic see VMS - a 64 bit OS with both 32 and 64 bit pointers.

Article history:

Version Date Description
0.9 January 1st 2025 Pre-release
1.0 January 3rd 2025 Updates (thanks to Chris, Dennis, Dan and Martin for comments)
1.1 January 7th 2025 Updates (thanks to John for comments)

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj