CURRENT NOTES JUL/AUG 1987 L E V E L 1 I S H E R E : Open Arrays in PASCAL on the ST? By J. Andrzej Wrotniak If OSS and Prospero keep their word, two significantly upgraded Pascal compilers (or rather programming environments) are just about to be released. Neither of these companies, however, promise the major feature missing in the so-called Level 0 implementations: open (or variable-size) arrays as procedure parameters. Open Array Parameters Who Needs Them? Sooner or later -- everybody. A possibility of writing library procedures (for brevity I will use this term for both procedures and functions) for handling arrays of unknown -- at the compilation time -- size cannot be underestimated. An obvious example may be a general-purpose plotting procedure (taking X- and Y-arrays as parameters). More, we should be able to call such a routine from the same program for arrays of different sizes. This was a standard feature of the grandfather of programming languages, FORTRAN (still alive and well, thank you), this was a standard feature of Algol -- no wonder we are missing it so badly in Pascal. Oh, yes, the ISO standard allows for an extended version, Level 1, with the so-called conformant array parameters, but few implementations contain this feature -- and none of them for the Atari ST. Rumors about OSS "working on it right now" were quite persistent, but their update announcement does not even mention open arrays -- neither does the letter from Prospero (CURRENT NOTES, June 1987). Why Not Modula-2? Open array parameters are a standard feature of Modula-2 (which is basically a well-upgraded Pascal). Unfortunately, the current Modula-2 compiler from TDI (the long-expected Version 3.00a) is a classic example of crippling a great language by a lousy (this is an understatement) implementation. Yes, 90% of it works, but you must first know, which 90% it is, and you never know it for sure. Also, there are more Pascal programmers, books and program examples floating around, and this indicates, that Pascal will be around for a while. So, back to our initial question: when will we have a Pascal implementation on ST allowing for open arrays?? It's Here! Good news, everybody! Our waiting is over. More: it was over all the time, from the very moment of introduction of both (OSS and Prospero) compilers to the market more than a year ago, and here is the full story. ____________________________________________________________________ ST - 1 - ST CURRENT NOTES JUL/AUG 1987 The good founding fathers from the ISO Standard Committee not only left the open arrays out of the basic standard; they did not provide for the feature of separate compilation, either. Luckily, most decent Pascal implementations include separate compilation as an extension to the standard. You write and compile your procedures separately, and declare them in other (calling) modules, as e.g. PROCEDURE Do_It( x: REAL; VAR y: REAL ); EXTERNAL; where EXTERNAL means: "you'll find this procedure in one of the libraries of separate modules specified during the linking process". Most of the Pascal compilers (all I know for that matter) do not check, whether a procedure is declared the same way in the library module and in the calling one. This may lead to naughty program bugs (try to skip VAR in one of the declarations). On the other hand, used with care this feature gives us -- you guessed it! -- the open array feature, so useful in writing libraries of reusable modules for our programs. How To Do It? The simplest example may be a library procedure for computing a sum of N reals, where N is unknown at the time of declaration. The Pascal code for the OSS compiler may look like this: {$E+,M+,R-} { R- switches off index range checking } PROGRAM Lib_Module; TYPE Open_Array = ARRAY [1..1] OF REAL; { any size will do here } FUNCTION Arr_Sum( VAR x: Open_Array; N: INTEGER ): REAL; VAR i: INTEGER; sum: REAL; BEGIN sum := 0.0; FOR i := 1 TO N DO sum := sum+x[i]; Arr_Sum := sum; END; {... here go any other library procedures ...} BEGIN END. In Prospero Pascal, PROGRAM should be replaced with SEGMENT (which is more appropriate, anyway), and the option line should be removed from the top (the "I" compiler option should be off during the compilation instead). ____________________________________________________________________ ST - 2 - ST CURRENT NOTES JUL/AUG 1987 The second half of our trick is the calling module -- in this example the main program: PROGRAM Show_It; VAR s1, s2: REAL; a: ARRAY [1..10] OF REAL; b: ARRAY [1..1000] OF REAL; FUNCTION Arr_Sum( VAR x: REAL; n: INTEGER ): REAL; EXTERNAL; {!!! note the different declaration of x !!!} BEGIN {... here read or compute arrays a and b ...} s1 := Arr_Sum(a[1],10); s2 := Arr_Sum(b[1],1000); {... here use the s1 and s2 as you wish ...} END. The whole trick is to declare the first parameter of Arr_Sum differently in both modules: 1. In the library module, where we are accessing individual elements of array x, it has to be declared as an array. It is, however, declared by address (this is what VAR stands for), so the procedure really expects here the address of the beginning of our array, and the type declaration serves only one purpose: type checking (the Big Brother knows better, what is good for you). 2. In the calling module (here: main program), x is declared as a REAL, but -- again -- by address, so the program will pass to Arr_Sum just the address of the REAL variable being there at the procedure call. If this variable happens to be the first element of an array, then it is just what Arr_Sum expects. Variations On The Theme Keeping the above points in mind, we can use this trick in a variety of ways -- as long as we know what we are doing. For example, a call s := Arr_Sum(b[101],100)+Arr_Sum(b[901],100); will add up the second and the tenth hundred of the elements of b, while s := Arr_Sum(b[901],200); will add the sum of the tenth hundred to God knows what (i.e. whatever 400 bytes happen to occupy the memory locations following b[1000]). The compiler would not detect this error, and you would get funny results without knowing why (unless you remember this article). Using this approach with multi-dimension arrays is more tricky. It is possible, when only the first index range is unknown at the ____________________________________________________________________ ST - 3 - ST CURRENT NOTES JUL/AUG 1987 compilation time (in Pascal, as opposed to most other programming languages, the array elements are usually arranged in the memory with the last index changing fastest). The simplest way to deal with this problem is to declare a multi-dimensional array as an array of arrays (the latter of fixed length!). Thus, a library procedure declared as TYPE Row_Arr = ARRAY [1..1] OF ARRAY [1..6] OF REAL; FUNCTION Find_Row( VAR x: Row_Arr; N: INTEGER): INTEGER; ... can be redeclared in the calling module as TYPE Row_of_Six = ARRAY [1..6] OF REAL; FUNCTION Find_Row( VAR x: Row_of_Six; N: INTEGER ): INTEGER; EXTERNAL; and can be called as i := Find_Row(zz[1],100); where the array zz was declared as VAR zz: ARRAY [1..100] OF Row_Of_Six; In other words, we have reduced the multi- dimensional problem to a one-dimensional one. Final Warning Strict type checking was introduced in Pascal to keep the programmer out of trouble (well, at least in many cases). Bypassing it as in the above example allows for more programming flexibility, but carries with it a danger of (not trivial to detect) errors. Thus, do not use these tricks without need, and when you do it, pay special attention to the index ranges. And, first of all, do not blame me if things do not work as you expect them to. Maybe you've just forgotten to cast this special spell? ____________________________________________________________________ ST - 4 - ST erent editors may