Demo IP Pascal Version

Copyright © 2005 S. A. Moore


Demo "on one page"

This page contains all you need to download, install and run the current demo. This file also appears in the demo itself.


The windows XP demo self-executable:

ipdemo.exe

 


What's new

A new release was done, 1.12.00. This fixes an inadvertent problem introduced with version 1.11.00, in gralib. Please update your demo.

A major bug scrub was completed on 2005/07/12. This addresses many outstanding issues from different sources, including customer feedback, our internal lists, internal tests, and other material.

Several features were enabled or added in the new version.

1. Header file associations do the correct thing. Formerly, if the order of reset or rewrite on header files was wrong, the filenames associated would also be incorrect:

program copy(infile, outfile);

 

var infile, outfile: text;

    c:               char;

 

begin

 

   rewrite(outfile);

   reset(infile);

   while not eof(infile) do begin

 

      while not eoln(infile) do begin

 

         read(infile, c);

         write(outfile, c)

 

      end;

      readln(infile);

      writeln(outfile);

 

   end

 

end.

This program would not work because the rewrite on the second file was performed first, so:

> copy myfile yourfile

Would assign the name "myfile" to outfile, and "yourfile" to infile. Now, the order of names on the command line always match the order in the header.

2. The "update" procedure was added. This is used instead of rewrite to allow updating a non-text file. See details in the "language" section below.

3. Case statements now have an else clause.

program copy(input, output);

 

var n: integer;

 

begin

 

   repeat

 

      write('Enter number: ');

      readln(n);

      case n of

 

         1: writeln('one');

         2: writeln('two');

         3: writeln('three');

         else writeln('What was that ?')

 

      end

 

   until n < 0

 

end.

4. There now exists a shorthand for standard base 1 integer index arrays:

program test(output);

 

var s: packed array 10 of char;

 

begin

 

   s := 'hi there  ';

   write(s)

 

end.

 

This is part of an ongoing effort to align Pascal extensions with Oberon, which uses 1 based integer index arrays. The notation is the same as writing:

 

program test(output);

 

var s: packed array [1..10] of char;

 

begin

 

   s := 'hi there  ';

   write(s)

 

end.

 

The number used in the declaration is the number of elements N, which are then numbered 1..N, and the index type is integer, without subranges.

 

As with standard array index notation, any number of dimensions can be specified:

 

program test(output);

 

var s: packed array 20, 10 of char;

 

begin

 

   s[1] := 'hi there  ';

   write(s[1])

 

end.

 

5. The standard function "refer".

 

program test;

 

procedure stub(a, b: integer);

 

begin

 

   refer(a, b)

 

end;

 

begin

end.

 

Refer takes any number of parameters and marks them as referenced, so that they will not we announced as unreferenced symbols. Its is useful in various cases where you don't wish to use a symbol in the code, such as a "dummy" procedure used to placehold for later use.

 


License


This software is provided "as is," without warranty of any kind, express or implied.  In no event shall Moore/CAD be held liable for any direct, indirect, incidental, special or consequential damages arising out of the use of or inability to use this software.

No guarantee is made that we will not change the documentation, input language for IP Pascal, or methods used to run the program. Nothing in this or other documents should be construed to mean that we will update this demonstration, or insure that it is correct, or even continue to provide it in the future. Our provision of the demonstration is completely at will.

Permission is granted to anyone to use this software for any purpose, including commercial applications.

This software is not for use in life-critical applications, nor for use in building life-critical applications.

Permission is given to individuals to download and use the software. The software can be redistributed without charge, provided the software is not altered, subsetted or modified in any manner.

The user of this software agrees not to reverse engineer the product, nor modify it.

This license applies to the demonstration version of IP Pascal only, and no permission is granted for any use or distribution of the full, unlimited IP Pascal product.


Support


Please use the email address demo@moorecad.com. We get a lot of mail, using this address will allow your email to be handled faster.

All support for the demo versions is on a time permitting basis. Please note that we handle bugs with IP Pascal, problems using IP Pascal, and questions about extended features and libraries in IP Pascal. If you need help constructing a program, we recommend the Pascal newsgroups:

comp.lang.pascal.ansi-iso

comp.lang.pascal.misc

The first is for standard Pascal related questions (of which IP Pascal is certainly a standard Pascal). The second is for all Pascals, including non-standard ones. When posting there, we recommend you clearly state which Pascal you have a question about (as in "question on IP Pascal...").

We encourage student project use of IP Pascal, and the demo should be sufficient for most student projects. Please note, however, that we don't help with student projects or help with program construction. The newsgroups above are best used for this purpose.


Please Read These Notes for the Current Demo.


1.    This is a command line compiler demo. The system will generate graphical programs, but there is no IDE available at this time. You need to compile programs using a command line window, listed in Windows XP under the start menu in:

Start->All programs->Accessories->Command prompt

2.    The demo version is limited to:

200 lines of program text

10,000 total characters per file

This should be sufficient to compile small test programs, and class assignments. There is no restriction on the output from the compiler. Your code will be fully compiled to machine code for the I80386 processor.

3.    The demo version cannot compile more than one file at a time.

4.    The demo version will only compile program files. There is no ability to compile modules to be used by other programs.

5.    Several IP Pascal standard features were not ready in time for this demo. The following items are not implemented, or out of service:

Note that all of these are IP Pascal extensions, and none affect the 7185 standard status of the system, it is fully ISO 7185 compliant.

These items will be added to the demo as they become available.


Issues


The following issues exist with the current version.


Fixes


If you have a demo version that is before the date of a fix below, it is recommended that you update.

Date: 2005/07/28

New version: 1.12.00

Fault classification: Serious

Description:

The procedure and function parameter calling method was changed in version 1.11, but the overload shim for syslib wasn't changed, leading to errors when running standard file operations under trmlib and gralib.

Please note: IP Pascal won't be released component by component any more. Its assumed that you will update everything. There will be several minor changes that won't be listed in the fixes column.

Date: 2005/07/12

Component: all

New version: 1.11.00

Fault classification: Medium

Description:

 

Across the board code scrub. Header file associativity was corrected. The "update" procedure was added. Other new features. UPDATE YOUR ENTIRE IPDEMO DIRECTORY PLEASE.

 

Date: 2005/05/22

Component: parse (parser front end)

New version: 1.02

Fault classification: Medium

Description:

Due to library change, side effect was induced in parser that caused it to handle upper case incorrectly.

Date: 2005/05/01

Component: pc (compiler shell)

New version: 1.10

Fault classification: Serious

Description:

When including multiple libraries, pc can place the standard I/O library somewhere besides the front of the link list. Because the startup code is packaged into the standard I/O, this will cause a crash on running such a binary.

A word about versions

The version numbering system was changed. With all of the changes occuring here, the minor version numbers were rolling over too fast. We have implemented a new version format that includes a "build number", as follows:

1.00.00

^  ^  ^

|  |  |

|  |  +--- Build number.

|  +------ Minor version number.

+--------- Major version number.

The major version number indicates entirely new issues of software and documentation. The minor version number indicates fixes, or collections of fixes, released externally. The build number is the internal number that is incremented for each fix.

You should not see anything but zeros in the build number. When we release code, we "roll" the version numbers by incrementing the minor version number if the build numbers are non-zero.

The new format gives 100 minor version numbers before a major release is done, and gives us 100 fix numbers internally.

In addition, the version numbers have been "synced" between all components of IP. You will now see the same version on all components of the IP Pascal system. This should make it easier to report problems.


Installation Instructions


The following installation steps are needed for the demo. The release product will use an installer. Thank you for your patience.

This demo is for Windows XP only. The demo may do anything from totally fail to partially work on other versions of windows.

1.    Place ipdemo.exe  in the directory you wish to install it in. This can be the root ("\"), or any other directory. It will create a directory called "ipdemo", which contains the entire program tree. The following installation instructions will be simpler if you use root as the location.

2.    Execute the demo file. If you have virus worries, you may scan the file for viruses using your virus scanning software. This is typically done by opening the explorer view of the ipdemo.exe file, right clicking the file, and selecting "Scan for Viruses" from the menu. The demo file is (currently) a .zip format file, and so will be usable with most current .zip format programs.

2.    Add the IP Demo binary directory to your path. There are two ways to do this.

Method #1 (preferred) used to permanently put IP Pascal on your execution path:

Add the executable directory to your path. From the start button, perform:

Start->Control Panel

This will display the control panel window. At this time, you will see either a list of icons (classic folder display), or a list of common tasks (common task display). If you see icons,  double click the "system" icon at this time. If you see common tasks, double click "Performance and Maintenance" on the right side (under "pick a category"). After "Performance and Maintenance" is clicked, you should see the "system" icon at the bottom right (under "pick a task").

Now you should be in the "system Properties" menu. Click the "advanced" tab at the top. You should now see a button marked "Environment Variables" near the bottom of the window. Click that button.

Now you should see "User variables for John Doe" (with your user name), with "System variables" at the bottom. Use the scroll bar on the "System variables", and find the variable "path". Double click that, and add the following text at the END of the "Variable value" line:

;c:\ipdemo\windows\i80386\bin

Note the ";" separating the new directory from the other entries.

Click "ok" and leave the control panel windows.

You can also place the entry at the front of the path, or anywhere in the middle, if you need to have the IP programs override other programs of the same name.

Method #2 (simpler, but not permanent) used to temporarily execute IP Pascal services:

Open a command window:

Start->All programs->Accessories->Command prompt

Execute:

> cd c:\ipdemo

(Using whatever directory you installed IP into). Execute:

> setpath

This will execute setpath.bat, which will add the IP binary to your path. This will allow you to execute IP Pascal services while the command window is open.

If you have installed the IP demo in another directory besides c:\ipdemo, you will need to edit the "setpath.bat" file to reflect that.

3.    If you have installed the IP Demo in any location besides "c:\ipdemo", you will need to edit the pc control file. Edit:

c:\ipdemo\windows\i80386\bin\pc.ins

Using whatever path you installed the IP Demo to. Edit the lines for "usespath" and "exclude", and change the "c:\ipdemo" section of the paths to reflect your actual installation location.


Using the IP Pascal Compiler


Open a command window (using instructions from the installation instructions above if required).

IP Pascal uses a typical collection of compiler and linker programs. However, you can control all of the aspects of creating an executable with the program "pc", executed as follows:

> pc myprogram

To compile "myprogram.pas".

This will create the program "myprogram.exe", which can then be executed. That's all there is to it.

Compiling a program will create several files in the current directory. These files are as follows:

myprogram.int    The intermediate code file.

myprogram.obj    The object code.

myprogram.sym    The symbols.

myprogram.exe    The executable.

Any of these files can be deleted if you require.

pc is a compiler "shell", and actually executes other programs to perform the compilation, link and executable generation. You will see these other programs execute as the compilation proceeds. pc also understands the structure of the program, similar to the Unix program "make" (but without the need for a make file), and knows if the program needs to be recompiled, and what parts do, and what parts don't. Therefore, don't be surprised if you don't see it execute all phases of the compilation/link process.

If you require that pc completely recompile the program without question, execute:

pc myprogram/r

(for "/rebuild").


Using IP Pascal as a Strict ISO 7185 Level 0 Compiler


Execute the command:

> pc myprogram/s

(for "/standard"). This passthrough option will cause the compilation to accept only the original ISO 7185 language at Level 0.

If you choose to compile ISO 7185 programs WITHOUT the standard flag, this can be done with fairly low impact. You will need to observe the following extra restrictions on program source:

IP Pascal defines additional reserved words. You will need to avoid use of the following reserved words:

module     process     monitor    share      uses

private    external    forward    xor        view

fixed      class       atom       construct  destruct

is         overload    override

Note that "forward", and "external" are promoted in the general IP Pascal language from the status of ISO 7185 "directive" to "word-symbol" or reserved word. Unlike ISO 7185 Pascal, they cannot be used for other identifiers.

There exist reserved words in the IP Pascal language that are not currently used. These have no particular meaning. They may simply indicate reserved words that we wanted to make sure did not appear in user programs. IP Pascal may have additional reserved words defined in the future, or have current reserved words removed. There is no guarantee that your programs will always work unmodified, with IP Pascal.


Lets Create "hello, world"


Ok, lets take a moment and run through a sample program, using ISO 7185 standard Pascal, using some typical I/O modes in Windows. In other words, let's get you started building standard Pascal programs.

The program will be "hello.pas", and looks, of course, like:

program hello(output);

 

begin

 

   writeln('hello, world')

 

end.

Remember, being ISO 7185, "output" in the header is required for standard output.

Ok, lets edit the program:

And compile for normal command line (serial) mode:

As detailed above, several program files were created, including the final executable, hello.exe.

And it runs.

Now that was serial mode. But there are three total modes you can target in windows:

Serial mode is the old fashioned Pascal mode. You write lines of text, they appear one after the other. Terminal mode gives you the ability to control character placement on the screen in x and x, like an old fashioned glass terminal. Graphical mode is full, windowed graphical mode, with line drawing, the works.

In windows, serial programs write to the console file, which is opened by name ("con:"). Terminal mode files are done via the console interface, a set of advanced calls that manage a console buffer and its display to a console window. Graphical mode is done via the Windows GDI, which is a VERY complex windowed graphical environment.

That three very different modes, and three VERY different APIs that windows supplies. But IP Pascal has paved over these differences for you. You can send an ISO 7185 standard Pascal program to any mode Windows has with a single option. Lets try the other two modes, starting with terminal mode.

The option "/dt" means to "default to terminal mode". Because we already built the program for serial mode, and we want to rebuild it for terminal mode, we specify the "/r" or "rebuild" switch.

We compiled and ran it, but it appears no different than the other run. Well, its actually not supposed to be different, even though it uses a very different API to output to the console. Terminal mode is completely compatible with the Windows console mode programming model, so it behaves just like any other console program. To see the difference, we have to do something that only a terminal can do, such as move the cursor, place different colored text, etc.

Finally, lets do the same thing for graphical mode.

Ok, this time, we built and executed "hello", but it came up in another window. That's a graphical window, there is no little "c:\" icon up in the top left, and no "console" in the title. The font is also different. IP Pascal created a full graphical window for you, and painted the text "hello, world" in it for you.

Also note the "finished - hello" in the title bar. What is this ? Well, hello does not know its running in a Window. If we terminated the window when hello finished, the window would be put up, written to, then closed almost immediately. You would only see a flash on the screen as the window came and went.

To prevent this, IP Pascal automatically "holds" the window until you have had a chance to read the output. IP Pascal knows to hold the window, because the program hello did nothing to demonstrate that it was aware that it was running as an advanced, windowed program. It didn't execute a call to check for any of the advanced input that IP Pascal provides. So IP Pascal assumes that it is an older program, and holds the Window until you can read the result. It is closed by you in the standard way, by clicking on the "X" in the upper right hand corner. It can also be terminated by hitting Ctrl-C on the keyboard while the window is selected. Ctrl-C is a valid termination character.

That's it. To truly take advantage of each of the new modes, terminal and graphical, you would need to use some of the calls we will introduce later in this document. But until then, realize that any ISO 7185 standard Pascal program can be brought forward into the advanced modes that IP Pascal provides, and do so without changing the program source.

Oh, by the way. If you happen to notice that we used some unusual commands like "list" and "ed", these are all programs built with IP Pascal. "ed" is a small editor that runs in terminal mode.


The IP Pascal Language


 IP Pascal complies with the complete ISO 7185 Pascal language at level 0. In order to understand the basic ISO 7185 language, see the file:

Rules of ANSI-ISO 7185 Pascal, text document

and/or the ISO 7185 standard itself:

The ISO Pascal standards, documents in various formats

Also we highly recommend Doug Cooper's book:

Standard Pascal User Reference Manual

And the original Pascal User Manual and Report, now in it's 4th edition:

Pascal User Manual and Report

We won't repeat the standard language rules in this demo document. However, note that the full IP Pascal release contains all the rules of Pascal.

The following pages of this demo document contain a short explanation of IP Pascal extended language features.


Implementation Defined Behavior


ASCII  control characters (characters with values between 0 and 32 inclusive) are treated as spaces by the compiler and its utilities.

The type "file of char" is NOT equivalent to type "text", and none of the rules for end of line or end of file interpretation apply to "file of char" (as is allowed by the ISO 7185 standard). Specifically, this allows a character file to be read with all of the control, line ending and other special characters intact.


IP Pascal Language Extensions


Identifiers

The '_' character is allowed anywhere in identifiers. For example

my_identifier

Newwave_

Are valid identifiers. Avoid use of identifiers with '_' at the front, for example:

_other

The reason for this is that they can be system names.

Goto Labels

Standard Pascal identifiers are allowed as goto labels (including '_'):

label terminate;

 

...

 

goto terminate;

 

Numeric Format

Numbers can have a radix specifier:

$feeb  Hexadecimal

&76    Octal

%0110  Binary

String Format

Strings can have "force escapes":

writeln('hello\sub\ff\0\$a5');

Unlike typical C implementations, however, the original ISO 646 (ASCII) mnemonics for the control codes are used:

\NUL \DLE \SOH \DC1 \STX \DC2 \ETX \DC3 \EOT \DC4 \ENQ \NAK
\ACK \SYN \BEL \ETB \BS  \CAN \HT  \EM  \LF  \SUB \VT  \ESC
\FF  \FS  \CR  \GS  \SO  \RS  \SI  \US

So instead of C's convention of:

'string\r'

IP Pascal uses:

'string\cr'

Numeric equivalents for characters can also be specified, using any base. The force character can also be used to terminate "run on" sequences:

'this is\$a\fun'

Keeps the string from being interpreted as:

'this is\$afun'

Which would make the inserted force an $af valued character.

Constants

Sets are allowed to appear in const declarations:

const hexset = ['a'..'z', '0'..'9'];

Constant expressions can appear anywhere a constant can. This includes const declarations, case selectors, variant selectors, and ordinal declarations.

Constant expressions have their own syntax and rules, apart from normal expressions. Only constants can be operands in constant expressions. The following operators can appear, in priority order:

Priority One

not x

For boolean operands, finds the logical "not" of the boolean value. For integer operands, finds the inversion of all bits. It's an error for the operand to be negative.

Priority Two

x * y

Multiplication.

x div y

Division.

x / y

x mod y

Integer modulo.

x and y

For boolean operands, finds the boolean "and". For integer operands, finds the boolean "and" of all bits. It's and error for either operand to be negative.

Priority Three

-x

Negation.

+x

Positivition.

Priority Four

a + b

Addition.

a - b

Subtraction.

a or b

For boolean operands, finds the boolean "or". For integer operands, finds the boolean "or" of all bits. It's and error for either operand to be negative.

a xor b

For boolean operands, finds the boolean "xor". For integer operands, finds the boolean "xor" of all bits. It's and error for either operand to be negative.

Boolean Operations

A new operator for booleans, "xor", is available:

type a, b: boolean;

a := a xor b;

The boolean operators "and", "or", "xor" and "not" function bitwise on unsigned integers:

type a: integer;

writeln(a or $a5);

Use of boolean operators on signed integers is an error.

Procedures and functions

The parameter list for procedures and functions can be duplicated when they are forwarded: 

procedure wrtout(a: integer; c: char); forward;

 

...

 

procedure wrtout(a: integer; c: char);

 

begin

 

...

 

end;

This extension makes it easier to create a forwarded procedure or function by cut and paste, and also makes it easier to read the program, because the parameters for a procedure or function are right there at the actual body of the procedures or function.

When a routine is declared this way, the two parameter lists are checked for congruence, or equality of parameter numbers and types. The identifiers used to declare the parameters can be different. If they are, then the identifier used in the routine is that of the first identifier (the one that appears in the forward declaration).

Procedures and functions can be declared external. This has several uses. First, if the procedure or function is written in assembly language, this directive passes the reference to that. Additionally, external can be used in "abbreviated modules" or modules that simply exist as descriptions of a module interface, in order not to give errors such as references, and lack of returns for functions. See the section below on modules for more information.

procedure x; external;

function y(i: integer): boolean; external;

Procedures and functions can be "overloaded" for both parameter lists, and function vs. procedure status. Overloading means to use the same name for several procedures or functions that differ in the types and numbers of their parameter lists:

procedure writeout(var f: text; i: integer);

 

begin

 

   write(f, i)

 

end;

 

overload procedure writeout(var f: text; c: char);

 

begin

 

   write(f, c)

 

end;

Overloads allow flexibility in defining procedures and functions that is very useful in a strict typing language such as Pascal. They allow the construction of library routines to be simplified, and allow "default" parameter values to be set up.

In IP Pascal, there are no ambiguous overloads, that is, there are never two or more procedures and functions that might be acceptable for a given call. To accomplish this, IP Pascal has fairly strict rules about the specification of overloads.

Overloads bind a group of procedures or functions together, referred to as an overload group. The first member of the group is the prime procedure or function. The prime function or procedure of an overload group appears as a normal procedure or function. Each succeeding procedure or function in the group must be specified as an overload by the overload keyword. In addition, the overload group must be completed in the same block as it starts. Overloads cannot cross from one procedure or function to another, or one module to another.

All of the members of an overload group must be unique vs. the others. This means that one of the number of parameters, the types of the parameters, or the procedure or function status must be different from the others. The return type of a function is not part of its uniqueness. IP will not attempt to determine, by context, which two otherwise identical functions is meant by its return type.

When parameters are considered for whether their types match another procedure or function's parameters, the var, view or value status of the parameter is not considered, only the actual type of the parameter. Parameters which are assignment compatible with each other are considered identical, under the ISO 7185 rules for assignment compatibility. Note that this means that reals and integers are not unique compared to each other, and subranges of integer are also not unique to each other, or to the type integer itself. Sets are only unique if they have different base types. Finally, general array string types are not unique vs. char types, because a general array can be a single character in length, and appear alike in context (see the section on general arrays for more information).

The most difficult rule in IP Pascal is the rule of "convergence". Convergence is defined as two members of an overload group with non-unique left hand side parameter lists. Thus:

procedure x(i: integer; c: char; d: bolean); ...

 

overload procedure x(i: integer; c: char; z: myset); ...

These members of group x are convergent in the first two parameters.

If two or more members converge, they must not specify different modes in the converging sections. A mode is the var, value or view status of the parameter. For example:

procedure x(i: integer; c: char; d: bolean); ...

 

overload procedure x(i: integer; view c: char; z: myset); ...

is an illegal example, because the second parameter has two different modes in the convergent parameter lists.

Convergence is a difficult rule. The reason it exists is that the compiler must generate code to handle the mode. A var parameter requires different handling than a value parameter. When evaluating a parameter list from left to right, the compiler must either know exactly what procedure or function it is working on, or the two "candidates" of the overload group must agree in mode.

Case statements

An "else" clause is supported for case statements:

program numbers(input, output);

 

var n: integer;

 

begin

 

   repeat

 

      writeln('Enter number: ');

      readln(n);

      case n of

 

         1: writeln('One');

         2: writeln('Two');

         3: writeln('Three');

         else writeln('Don''t know that number')

 

      end

 

   until n < 0

 

 

end.

The else clause will take the place of the error that occurs when the case selector does not match any of the case constants.

Halt Procedure

The system defined procedure halt causes the program to terminate immediately. Most operating systems also define an error value to be returned by a program to indicate the success or failure of the program. See how to set that in the description of extlib below.

procedure error;

 

begin

 

   writeln('bad input');

   halt

 

end;

halt is actually one of the first extensions to Pascal, and was in the original CDC 6000 compiler. halt allows the creation of very simple error handling procedures, without the use of a long goto back to the main program block.

Program Header Files

In IP Pascal, the header files are both required, and have extensive functions. This is essentially as original Wirth Pascal. The identifiers "input" and "output" must appear to use standard input and standard output, respectively. In addition, several other standard header files are declared. This is a complete list:

input

The standard input file.

output

The standard output file.

error

As in the C language, the error file is connected to the console, but can be used to insure that error messages are routed directly to the console. For example, if the standard output is redirected to a file, writes to error will still go to the console. Also, if the program is compiled in graphical mode, errors still go back to the original console window used to start the program, provided it is not detached. See gralib below for more details.

list

An output file, is routed to the printer. In many graphical operating systems, it simply goes to the console.

command

Reads from the command line used to execute the program. Typically, this will be a file, containing a single line, which has all of the text following the command word used to execute the program.

program echo(command, output);

 

var c: char;

 

begin

 

   while not eoln(command) do begin

 

      read(command, c);

      write(command, c)

 

   end;

   writeln

 

end.

Would appear as executed:

> echo hi there !

hi there !

The advantage of having the command line as a file instead of a string passed to the program is that all of the standard Pascal parsing procedures work on it, such as reading and converting a number, etc.

Header files can also be used to automatically pass any number of files into the program.

 

program copy(infile, outfile, output);

 

type byte = 0..255;

 

var infile, outfile: file of byte;

    b: byte;

 

begin

 

   reset(infile);

   rewrite(outfile);

   while not eof(infile) do begin

 

      read(infile, b);

      write(outfile, b)

 

   end

 

end.

Each header file that does not match a predefined name ("input", "output", "list", etc. from the list above) is assigned a name from the command line in turn:

> copy myfile yourfile

So infile gets "myfile", outfile gets "yourfile", etc.

Note that the files must have a type declaration (as required by ISO 7185), and are reset or rewritten by the program manually, instead of letting IP Pascal do it. This effectively allows you to set the read or write mode of the file.

File Handling

Files under ISO 7185 Pascal are anonymous by default, and will cease to exist after the program ends. IP Pascal can give files a name, and so cause them to exist outside the program run. The name can be a program that existed before the program started, or that persists after the program run, or any combination.

The procedure to give a file a name is assign, and comes from the Borland language series:

type f: text;

 

begin

 

   assign(f, 'myfile.txt');

   reset(f);

 

   ...

 

Note that the named file must still be reset or rewritten. All assign does is give the file a name. The file can be any type, and the other handling procedures for the file are as in standard Pascal.

The close procedure allows a file to be reused any number of times:

repeat

 

   assign(f, name);

   reset(f);

 

   ...

 

   close(f)

 

until done;

Its considered good practice to specifically close each file used before the program ends. However, IP Pascal does close each open file automatically when the program ends. If a file was never given a name (anonymous), then it is deleted when the program ends.

Non-text files have a length in number of elements, and the number of elements in a file can be determined by the function length.

l := length(f);

The current location, in number of elements from the beginning of the file, numbered from 1 to n, can be determined by:

location(f);

Note that IP Pascal files do not start at 0 as in some other implementations and languages, the numbering is consistent with arrays.

The position of reading and writing can be set by position:

position(f, p);

Which again, uses the 1..n notation. The position of a file can be set to 1 element beyond the end. In the case of reading, this causes the eof (End Of file) to become true. In the case of writing, this means that the next element past the end will be added.

If a file is to be updated by changing or adding to its contents, update is used in place of rewrite:

update(f);

Update acts just as rewrite, and places the file in write mode, at element 1. However, unlike rewrite, it does not clear the contents of the file first. This allows the contents of the file to be updated. If the file is to be appended, then executing:

position(f, length(f)+1);

After update will cause new data to be written to the end of the file.

update is not valid on a text type file.

As mentioned, text files have no element length, and cannot be the target of any of the above procedures and functions. This is because text files have a special relationship with the character format of their files. In particular, the eoln format is undefined, and the ISO 7185 standard requires the addition of an eoln at the end of the file if none exists.

If positioning is required on a file containing characters, this can be done by using the type:

type f: file of char;

Which is a plain file of character values, with no special interpretations of line endings.

A file can be checked for existence by name, by the function exists:

if exists('myfile.txt') then ...

The function delete removes a file, by name:

delete('oldfile');

Delete gives an error on an attempt to delete a non-existent file. This error can be prevented by using exists to check the file before deleting it.

Filenames can be changed by change:

change('newname', 'oldname');

Its an error if the old name does not exist, while again the exists function can be used to check for.

When a file based on a subrange of integer is declared, the element size on the external medium is only the size needed by the subrange. Specifically, this fact can be used to access a file of byte:

type byte = 0..255;

     bytfil = file of byte;

 

var bf: bytfil;

 

begin

 

   write(bf, $a5);

 

   ...

Text File I/O

The field specifications of text file writes have two extended modes. First, it is possible to specify a negative field:

write(i: -10)

Negative fields come from the C language, and mean to align the output to the left, instead of the standard Pascal right side. For example:

i := 123;

write(i: 10);

write(i: -10);

Would produce  the output:

       123

123

Second, a special field value, 0, can be used to specify padded string output:

var s: packed array [1..20] of char;

s := 'the time is now     ';

write('*', s: 0, '*');

Would produce  the output:

*The time is now*

The field of 0 indicates that the actual field should be determined by the right padded string length. The right padded string length is the index of the rightmost non-blank character, or 0 if there isn't one.

Enumerated Type Conversion

The reverse of the "ord" function can be performed on enumerated types via a "type converter":

 

type number = (one, two, three);

 

var n: number;

 

begin

 

   n := number(1);

 

   ...

Type converters under IP Pascal cannot convert any type to any other type (like a C language "type cast"), but instead have limited uses.

Subrange Constraints

The rules of ISO 7185 Pascal dictate that calculations of integer subranges are automatically promoted to full integer. In some cases, the compiler can determine that less precision is required. However, it is not always possible. Subrange constraints allow the arithmetic precision of any part of an expression to be fully controlled by the programmer using a type converter construct:

type byte = 0..255;

 

var a, b: integer;

 

begin

 

   a := byte(b+1);

 

   ...

 

The ability to specify exactly the precision needed helps greatly in embedded applications on small processors (8 or 16 bit) where it would be inefficient to perform all expression operations at full integer precision. This is equivalent in function to the C language use of different size types such as "char", "int" and "long" using rules of promotion that only promote to the largest element in the expression.

Protected Parameters

In addition to "value" and variable or var parameters, IP can have a protected parameter type view: Like a value parameter, view parameters can accept full expressions:

var x: integer;

 

procedure a(view b: integer);

 

begin

 

   ...

 

end;

 

begin

 

   a(x*5)

 

A view parameter has all of the efficiency of a var parameter, but uses value passing rules. For example:

 

type string10: packed array [1..10] of char;

 

procedure a(view s: string10);

 

...

 

Normally, a value parameter of an array such as "string10" would be inefficient because of the need to copy the entire array to the value parameter. However, typing the parameter as view leaves it up to the compiler how to most efficiently pass the parameter, by value or reference. The effect is the same, since the parameter cannot be modified, just as in a value parameter.

view parameters are also useful for cases where we want to be sure that a parameter is not modified at all within a procedure.

Any attempt to modify a view parameter within the routine is flagged as an error by the compiler, using the same rules as "threatening" of "for" index variables under ISO 7185.

Refer procedure

The system defined procedure "refer" causes any number of its parameter symbols to be considered "referenced" by the compiler:

program test;

 

procedure stub(a, b: integer);

 

begin

 

   refer(a, b) { these parameters are unused }

 

end;

 

begin

end.

 

The refer procedure allows unused identifiers to be used in the program without the need to turn all reference checking off in the compiler. This is commonly needed in the case of dummy/stub procedures and other cases.

Fixed Type

A fixed type is a cross between a variable type and a constant. They can have structured types, but are preinitalized like a constant. Fixed types can be all of the structured types, arrays, records, and combinations thereof, plus all of the normal types that can be constants, such as integers, characters and sets. The only excluded types are files, pointers and variant records, because those are inherently dynamic.

fixed a: integer = 1;

      b: array [1..10] of integer = array 5, 6, 8, 2, 3, 5, 9, 1, 12, 85 end;

      c: record a: integer; b: char end = record 1, 'a' end;

      d: array [1..5] of packed array [1..5] of char = array

  

         'tooth',

         'trap ',

         'same ',

         'fall ',

         'radio'

 

      end;

 

Fixed types have a similar declaration syntax to var declared types, except for the appearance of the initializer (= ...). Fixed types can be used anywhere a var type can, but fixed types cannot be modified or threatened (ISO 7185 contains the definition of a "threatened" variable, including appearing as a for index, var parameter, etc.).

When a structured fixed type appears, it must be in "structure image" format. The constants for an array must be contained within array and end keywords. The constants of a record must be contained in record and end keywords. This makes it clear just what is being defined in the intalizer, which otherwise is a fairly undifferentiated list of constants.

array <const> [,<const>] end

record <const> [,<const>] end

The exception to this rule is constant strings, which can be simply quoted strings of characters.

Fixed types are not preinitialized variables. They may not be modified during the run of the program. To get the same effect as preinitialized variables, a fixed type can be assigned to a variable type at startup using a single assignment statement. However, in many cases, the fixed type can be directly used, so fixed types are actually more efficient than preinitialized variables would be, since they simply hide this copy step.

Declaration Order

In IP Pascal, the declarations can appear in any order, with the only restriction being that symbols be declared before their use. Additionally, pointer declarations must define what they point to within the same type statement. Relaxed declaration order is a better match for the modular environment of IP Pascal, in which declarations are divided into modular groups.

program test;

 

var a: integer;

 

label 99;

 

procedure x; begin end;

 

const a = 5;

 

...

 

A view parameter has all of the efficiency of a var parameter, but uses value passing rules. For example

Include Operator

IP Pascal has a C type line escape, of which include is the only one currently implemented.:

program test;

 

#include myfile.pas

 

The include operator is limited in function, it must be flush left (1st character on line must be "#"), and only whole lines can be inserted at a line boundary. The include file mechanism is not the preferred way to accomplish breaking programs into files, that is the purpose of modularity. However, the include operator has occasional uses.

Integer index arrays

A special notation exists for the case where you want an array with an integer index starting with 1:

program test;

 

var a: array 10 of integer;

    i: integer;

 

begin

 

   for i := 1 to 10 do a[i] := 0

 

end.

The number in place of the index specification is the length of the array in elements. The index is then calculated as 1..N, where N is the number of elements. The following declarations are equivalent:

type a = array 10 of integer;

 

type a = array [1..10] of integer;

Any number of dimensions can be specified:

type a = array 20, 10 of integer;

Standard Pascal strings are declared as:

type a = packed array 10 of char;

General Arrays

IP Pascal has an array model that allows the length of the array to be specified at runtime:

type genint = array of integer;

The array is a "prototype" of the actual array, because it does not specify the length of the array. General arrays cannot be statically allocated. They cannot appear in a variable declaration or as a value parameter, which would require allocation of an array of unknown length. Instead, general arrays are allocated as pointers:

var myarr: ^genint;

    i: integer;

 

begin

 

   new(genint, 100);

   for i := 1 to max(genint) do genint^[i] := 0;

The call for new in this case has an extra parameter, that is the number of array elements in the general array to be created. Once a general array is allocated, it becomes equivalent to a normal array of the same length. In fact, general arrays are completely compatible with standard Pascal arrays.

The system defined function max returns the length of the array.

General arrays don't specify an index type in their declaration. The index type for them is always integer, and the starting index is 1. So a general array has indexes of:

1..max(a);

General arrays can be either packed or not packed, and the ISO 7185 idea of a "string" applies to general arrays as well. A general array of base type char, is a string:

 Two arrays are assignment compatible if:

  1. One or both of the arrays are a general array.
  2. The arrays have compatible base types.
  3. Both arrays have the same packed/unpacked status.

So, for example, the following declarations are compatible:

 

type a = packed array [1..100] of char;

     b = packed array of char;

 

The following declarations are not compatible:

 

type a: packed array [1..10] of integer;

     b: packed array of boolean;

 

The following code demonstrates this:

type a = packed array [(one, two, three, four, five)] of char;

     b = packed array of char;

 

var c: a;

    d: ^b;

 

begin

 

   c := 'prime';

   new(d, 5);

   d^ := c;

   write(c[three]);

   d^[3] := 'x';

   c := d^;

   ...

   

Note that c and d are compatible, and that d maintains its index of 1..max(d) even though it was assigned from an array whose index type was different. When assigning between general and normal arrays, the assignment occurs by paring the first element of the arrays, then the second, then the next, etc. The general array "image" of any array always starts at 1, regardless of its original index. IP Pascal considers a general array to be compatible with any array, regardless of its index type. Put another way, IP Pascal cares only about the elements of the array, and the method used to index them is not important. This is why general arrays are considered a "container" type for other array types.

type a = array [-100..100] of integer;

     b = array of integer;

 

var c: a;

    d: ^b;

 

begin

 

   c[-100] := 12;

   new(d, 201);

   d^ := c;

   write(d^[1]);

   ...

 Note that we had to allocate an array of 201 to allow for the zero crossing in the array type a. In this example, element -100 of the array c becomes element 1 of the array d.

Since general arrays don't have an index type, how are mult-dimensional arrays constructed ? The answer is, you can do it yourself.

type a = array of integer;

     b = ^a;

     c = array of b;

 

var d: ^c;

 

begin

 

   new(d, 100); { create the y dimension }

   for i := 1 to 100 do new(d^[i]); { create all x dimensions }

   d[50]^[70] := 65;

   ...

Not all general arrays are dynamically allocated. A parameter can also be a general array. The general array acts as a "template" for the actual array that is passed:

type a = array of integer;

 

var b: array [1..10] of integer;

 

procedure addone(var x: a);

 

var i: integer;

 

begin

 

   for i := 1 to max(a) do a[i] := a[i]+1

 

end;

 

begin

 

   for i := 1 to 10 do b[i] := 10-i+1;

   addone(b);

A parameter whose type is a general array must be a var or view parameter. It cannot be a value parameter, because that would be allocating a copy of the input parameter, and that can't be done unless we know its length.

As mentioned above, general arrays can be Pascal strings if they have a base type of char, and are packed. So, for example, we can create a general array string output routine:

 

type string = packed array of char;

 

var mystring: packed array [1..8] of char;

 

procedure wrtstr(var s: string);

 

var i: integer;

 

begin

 

   for i := 1 to max(s) do write(s[i]);

   writeln

 

end;

 

begin

 

   mystring := 'hi there';

   wrtstr(mystring);

 

end.

More interesting is the example:

 

type string = packed array of char;

 

procedure wrtstr(view s: string);

 

var i: integer;

 

begin

 

   for i := 1 to max(s) do write(s[i]);

   writeln

 

end;

begin

 

   wrtstr('hi there');

 

end.

 

The first thing that is different with this example is that the view declaration is used for the general array. This allows it to be a reference parameter, but still use value passing rules (because the view parameter cannot be modified). Since value passing rules are in effect for the general string parameter, it can be passed a constant string.

Modularity

Programs in IP Pascal can be broken up into modules, each of which can contain its own declarations, variables, procedures and functions. IP Pascal differs from many modular implementations in that there is no separate interface and implementation parts. Each module represents its own declarations, procedures and functions to its users. This means that they don't have to appear twice in either the same file, or in different files. This makes it easier to modify them, since two parallel copies don't have to be maintained.

The basic module construct is:

 

module mymod(input, output);

 

const one = 1;

 

type string = packed array of char;

 

procedure wrtstr(view s: string);

 

private

 

var s: string;

 

procedure wrtstr(view s: string);

 

begin

end;

 

begin { initialization section for mymod }

end;

 

begin { finalization section for mymod }

end.

 

A module appears very much like a program, which in IP Pascal is just considered a special type of module. A module can have header files. Modules can get access to the input, output, error, list and command files just as a program can. For a module to use any of the files, or perform default input and output, the file must appear in the header, just as in a program. A module can use the command file, or have automatic filename header files. However, such uses of the command line should be avoided, since they create order dependencies.

Declarations appear in a module just as in a program. IP Pascal implements a new keyword private that demarcates between declarations that other modules can use, and declarations that are only to appear within the module. Procedures and functions can be forwarded from the public area to the private area. The private keyword is not restricted to modules, it can also be used in programs.

Modules have two main body blocks, known as the initialization and finalization blocks. The normal main block that you are familiar with from the program construct is the initialization block. The second, optional block is the finalization block. When multiple modules appear in a program image, each of the initialization blocks in the image are executed before the program module gains control. After the program module exits, each module has its finalization section executed. The order in which the initialization and finalization sections are executed is specified at link time, and is set automatically by the compiler shell program pc, which considers which module calls which, and orders the modules so that each module is initialized before the modules that call it. Creating order dependencies in module initialization and finalization sections should be avoided.

A module is compiled on its on as a unit. A separate module that wants to use the module includes it in a "uses" statement, which originated with UCSD Pascal:

 

program test;

 

uses extlib, gralib;

 

...

 

Appearance in a uses statement effectively includes all of the non-private elements of the used module into the using module. Either a program or a module can have a uses statement. IP Pascal automatically keeps track of used modules to prevent duplicate declarations, and recursive or circular uses references are valid. IP Pascal knows how to extract declaration information from either the source code or the compiled code, and will automatically choose whichever is available and most recently modified. The compilation proceeds faster if the precompiled declarations are used.


Standard Modules


IP Pascal is centered around its modules. Everything in the IP system is made up of modules. The support library is made up of modules. Modules can override each other, and thus can take over functions from the low level support code so the fundamental language constructs such as the standard input and output files can be redefined.

Each operating system that IP Pascal runs on usually has one or more modules that is collectively referred to as the wrapper (but usually named something specific to the OS involved, like "windows.pas"). On top of that, a series of modules adapt the operating specific calls needed by programs to an OS independent format referred to as the IP Basic Portability Layer. It is to this system independent layer that most IP Pascal programs will be written.

The portability layer is a series of modules that each adapt a particular function the program will need. These are further classified as I/O vs. non-I/O modules. I/O modules override the standard I/O system, and become a virtual system for the program to model its I/O on. Non-I/O modules simply extend the functions available of IP Pascal. Some of these modules may rely on the wrapper, but some may only be useful extensions to the language.

strlib

Implements a set of string handling functions.

extlib

Handle searching directories, time and date, paths and filenames, file permissions and modes, environment strings, and execution of other programs.

sndlib

Implements sound via MIDIi and waveform devices.

serlib

Implements the basic serial I/O model.

trmlib

Implements I/O on a terminal, or x-y character surface.

gralib

Implements I/O on a graphical surface.

Each modules calls except serlib will be covered in turn. serlib does not need to be covered, because it implements the basic Pascal I/O model, the reads and writes you are already used to.


strlib - Standard String library


The string library implements various useful string functions. Strings are built out of arrays of characters in Pascal, and extended in IP Pascal. There are many ways to represent a string. Space padded right, from standard Pascal, leaves the right side of a fixed length string blank. Borland strings use a character array beginning with 0, then use the zero'th element to describe the string length, from 0 to 255. Dynamic strings are character arrays that are allocated for each size needed, then returned to dynamic storage when no longer needed. And there are many others.

strlib unifies two schemes. The first is the old space padded right scheme familiar from standard Pascal. The second is dynamic strings. Dynamic strings are almost the ideal string type. They are unlimited in length, they can be returned from a function, because they are pointers. And because their storage is recycled, they are space efficient. They can also be fairly speed efficient by managing when and if they are recycled.

The two string schemes complement each other. There are functions, like finding the lower case equivalent of a string, or finding a substring, that don't care which format is used, so the same function serves both string types. Using IP Pascal's compatible general arrays, dynamic arrays can be copied from standard Pascal fixed arrays, operations performed on those strings, then the result returned to a standard fixed array. Also, using general array parameters, even working with padded strings only is easier and more efficient.

The result is strlib is an easy upgrade from standard padded strings. A program can be completely converted to dynamic strings, or left as a mix of the string types.

The declarations for padded or fixed, and dynamic strings are in the module stddef, and appear as:

type string = packed array of char;

     pstring = ^string;

The string functions are often overloaded so that they take both types of strings. This allows their use in nested functions.

Conventions

In some cases, it is ambiguous whether a padded or dynamic string argument is meant. For example, the string compare facility does not know which type its operands are, and it makes a difference to the result, since length plays a part in string comparison. For these situations, a "p" is appended to the name of the procedure or function.

Many functions and procedures must know if case matters. For example, string compare can be with case, or caseless. For these procedures and functions, we append a "c" to the ones that case does matter.

A few functions and procedures perform different actions on strings vs. characters. For the string versions, an "s" is appended to the name.

Words

Some of the functions and procedures treat the strings as a series of words. Words are a series of non-space characters surrounded by one or more spaces. For example:

'      hi          there george'

Has three words, 'hi', 'there', and 'george'. Such words can be counted, indexed and extracted.

Format Strings

Some routines accept a "format string" to output numbers with. The format is an image of the output string the number is converted into. The string will contain a series of format  characters. The entire format string is copied to the result string, but the special format characters are replaced with parts of the number to be converted. The format characters are:

9

Represents a digit. This is replaced with a digit from the number, which is paired with the same digit, counting from the rightmost digit position in the format. If there is no significant digit in the number at that position, a space replaces the specifier. In the fraction, if the digit position is non-zero, or there are non-zero digits to the right, the digit replaces the format character, otherwise space.

0

As "9" above, but "0" replaces the digit if no significant digit is found, instead of space. In the fraction, if the digit position is non-zero, then it replaces the format character, otherwise remains 0.

-

Represents the sign. If the number is negative, it is left alone. If the number is positive, it is replaced by a space. The entire number must be to the right of this.

+

As "-", but "+" appears instead of space on a positive number.

$, &, %

These characters are used to indicate if the number is hex (or USA dollars), octal or binary. If a significant digit can be matched to this position, that is output, otherwise, either the character, or a space is placed. The space is placed if the format character has already appeared.

,

If the comma appears to the right of significant digits, it is output as is. Otherwise, it is either replaced by space, or if a "$", "&" or "%" character appears to the left of it, it will be replaced by that.

.

May only appear on real numbers. This format character is always printed, but specifies where the mantissa of a number appears, and where its fraction appears. The appearance of the decimal point will enable or disable the fraction. If present, fractional digits are output, otherwise, the fraction is discarded.

Recycling

By default, strlib leaves it up to the programmer to determine when dynamic strings should be recycled. To keep from losing space ("memory leaks"), the program must be careful to keep track of all dynamic strings created, and return them to free storage via dispose. However, when performing complex string calculations, especially where strings are returned from functions and then those appear as arguments to other procedures and functions, it can be very tedious to keep track of all such strings.

To answer such problems, strlib can use a nested blocking system to automatically dispose of strings for you. The openstring call begins a new string block, and closestring ends it. When a new block is opened, any dynamic strings allocated are recorded in the block. Then, each of them are disposed of when the block ends. Any number of block levels can exist. With no blocks in effect, the automatic recycling system is off, which is the state strlib starts in. When all blocks are closed, strlib reverts to this state. Because the dynamic strings are returned as a group, this system can be more efficient than returning the strings one at a time.

strlib has a call exportstring to completely remove a given dynamic string from the automated recycling system. This is good for when you wish to keep a particular string and recycle it yourself, while still enjoying the ability to automatically recycle other strings. There is also an upstring call which takes a given string, and moves it to the surrounding string block. This feature is good for when you want to return a result from a calculation inside a block, to be used in the upper block.

It's important to understand that the strlib block system has nothing to do with blocks in Pascal. strlib blocks can cross blocks, functions and even whole modules of Pascal. The two are entirely unrelated.

There is a limit of 100 on how many strings can be allocated at one time, in any string block.

Functions

function lcase(c: char): char;

overload function lcas