Welcome to the FAQ ! I will answer here various questions about ISO 7185 Pascal, and compilers of that language. This FAQ is not limited to any one machine, operating system or language level. Any language that is based on the original Wirth language or the standards that came from it may be covered here. Requests to add information are welcome, submissions are encouraged.
This FAQ covers in main the ISO 7185 Pascal language. We don't cover the ISO 10206 or extended standard, and we don't cover specific implementations or alternate dialects of Pascal except to discuss the differences between some popular dialects and the Pascal standard. We cover differences with several dialects of Pascal, such as P-machine, UCSD and Apple, mainly for their historical significance, since these dialects are no longer have supported installations. We cover the differences between ISO 7185 Pascal and Borland Delphi because this is a widely used dialect of Pascal.
If you are interested in the Borland Delphi language, please be aware that this is a different language. You will be able to find several sites specific to that language.
Finally, this site and the author of this FAQ is certainly biased. My bias is towards the original implementation and idea of Pascal. I'll make it clear. Implementations that implemented the language and then extended it, great! Implementations that changed Pascal to some other language and then called it "Pascal", not so great with me. That's my bias here. I am a Pascal (original, standard, Jensen and Wirth) avocate.
Pascal was named after the French mathematician Blaise Pascal, who created a calculating machine (not a true computer).
Pascal is one of a series of languages put forth by one of the most prolific computer language creators, Niklaus Wirth, a professor at Institut fur informatik, ETH, Zurich, Switzerland. Professor Wirth participated in various versions of Algol, a language put forth by international cooperation that introduced the basic concepts of structured programming to the world. Wirth terms Pascal as a descendant of Algol 60 (for Algol, 1960 standard). The "official" descendant of Algol 60 was Algol 68, famous for having assignment as an expression operator (a basic feature of the later language C). Wirth felt that the design committee for Algol, after Algol 60, was losing focus and creating an unnecessarily complex language.
While Algol W has had it's fans, the language Pascal was considered to be a new high of consistent language design. The first draft of Pascal was created in 1968. The first compiler was operational in 1970, and the language was generally published in 1971. In 1973, after two years of testing and use, the language was revised into it's final form. That was detailed by "The Pascal User Manual and Report" [Jensen and Wirth].
The first compiler for Pascal was implemented on a CDC 6000 computer at ETH, for "unrevised" Pascal. After the language was revised, a new, high optimization compiler for the new language was created using the old compiler, then the source for that compiler itself changed to "revised" format, so that it could compile itself (known as "bootstrapping" a compiler).
In 1974 there were 10 compilers running on various systems. By 1979 there were at least 80. In 1977, various committees began the work to standardize the language. In 1982, the ISO (International Standards Organization) issued ISO 7185, the official Pascal standard. In addition, several countries around the world issued their own national standard for Pascal.
Pascal is in the Algol family of languages. Algol, whose first version was called IAL or "International Algebraic Language", was the first language created by international committee. The resulting language was rather odd for its time. The committee had the goal of designing a unified computer language, but also saw Algol as a way to cleanly express computer algorithms, and so was not directly concerned with creating a practical language for compilation. That is, the language would serve a purpose even if it was only used for publishing algorithms, not running them.
This resulted in Algol not having many data types, or built in I/O. Also, Algol was generally free of the limits common to programming languages of that time, such as number of array dimensions. One of the goals of Algol was for it to be as close to mathematical notation as possible. In particular, Algol used a special operator for assignment, ':=', because '=' had a different meaning in mathematics.
Algol was introduced in 1958 as Algol 58. In 1959, the committee prepared a new version that would become Algol 60. One of the hallmarks of that version was not a language feature, but that a language syntax notation called BNF (Backus-Normal Form), which gave a complete description of the language syntax, a first for any computer language.
The revised report for Algol 60 is available at:
While Algol 60 was a pivotal language, its (relatively) abstract nature reduced the number of practical implementations. It was common to hand translate Algol programs in to Fortran, and real world Algol compilers that existed tended to provide a number of extensions necessary for practical problem solving, for example, I/O statements.
In 1962, working at UC Berkley in California, Niklaus wirth worked on an Algol 60 variant/implementation Euler (after Swiss mathematician Leonhard Euler), on which an ACM paper was based.
After Algol 60, the committee began work on a successor standard. In 1964, Niklaus Wirth was invited to join the Algol working group, in response to his work on the language Euler. The proceedings of the Algol group were contentious, perhaps explaining why many years passed without producing the next Algol standard. There were three proposed versions of Algol. The one proposed by Wirth was Algol W, which was implemented on an IBM 360 computer. The proposal that was accepted as the official standard for Algol was Algol 68, the last widely implemented version of Algol.
You can find the Algol W manual at:
You can find the revised report on Algol 68 at:
http://members.dokom.net/w.kloke/RR/
In 1968, after the working group for Algol, Wirth decided to design a new language in the tradition of Algol 60 and Algol W that met his goals for including advanced data types, structuring of data types, and I/O for the language. It was called Pascal (after Blaise Pascal, 15th century scientist and inventor of early mathematical calculating machine).
In 1969, a bootstrap compiler using Fortran as the implementation language was completed. Although the compiler was written in Fortran, the idea was to rewrite the compiler in Pascal itself, and so bootstrap the compiler. Wirth called that project unsuccessful, and instead, a Pascal compiler was written in a subset of the full Pascal language and translated by hand to a language called SCALLOP on the CDC 6000 computer, and thus bootstrapped.
In 1972, the language Pascal was revised based on working experience with it, and a new compiler for the revised language was created and completed in about 1974.
Meanwhile, in 1973 a Pascal language subset and implementation was created called Pascal-P (for Portable). The implementation included a compiler to an abstract machine called the P-machine, and an interpreter for that machine. The idea was that Pascal could be bootstrapped to new machines by several methods, including writing a fairly small interpreter for the target machine, rewriting the backend of the compiler to target a new machine, or hand translating the intermediate code.
You can find the source and details for the Pascal-P implementation at:
http://www.moorecad.com/standardpascal/p4.html
In 1978, Kenneth Bowles at the University of California at San Diego created a microcomputer implementation of Pascal-P that kept the p-system interpreter and did not produce final machine code. Bowles believed that microprocessors weren't powerful enough to run such an advanced system, and were better off running on the interpreter on such machines, using a carefully coded interpreter written in assembly language. Under Bowles, an entire programming system, including operating system, editor and compiler and even graphics system were created that all ran on the interpreter.
Implementations of UCSD Pascal existed on machines as diverse as a MITS Altair and a PDP-11. In 1979, a version of the system appeared on the Apple II.
Also in 1978, the IEEE computer society began the proceedings to standardize the Pascal language. In 1983, the full IEEE/ANSI Pascal language standard and the first ISO Pascal language standard were issued.
In 1984, Borland introduced a version of Pascal for both CP/M on the Z80 CPU and the IBM PC with an 8088/8086 CPU, as a full compiler. Borland released several versions throughout the years, with the latest being Delphi, which incorporated Borland Pascal as the base language.
In 1986, Apple released a version of its Pascal that supported objects, which was developed in consultation with Niklaus Wirth.
In 1989, Borland introduced a version of Turbo Pascal with objects.
The original Pascal standard was an unofficial standard documented by the author, Niklaus Wirth, in "The Report". The first official standard was ISO 7185 issued in 1983. This was followed by the extended standard, ISO 10206, in 1990. Another standard was the Object-Oriented Extension to Pascal. However, this standard was never finished, and was basically abandoned for lack of interest.
In 1989, ISO 7185 was revised to correct various errors and ambiguities found in the original document. This resulted in ISO 7185:1990.
There have been many implementations of Pascal, both those that follow and those that don't follow the ISO 7185 standard. For the most part, ISO 7185 was followed on large computer installations during the early days of Pascal (1973-1990), and some installations on microcomputers. The other dialect that became popular was the UCSD (University of California at San Diego) Pascal, followed by Borland's Turbo Pascal. Although there are virtually no current implementations of UCSD Pascal, Borland's products exist today for advanced IBM-PC compatibles under Borland's Delphi name.
When the IBM-PC became 32 bit with the introduction of the Intel 80386, virtually all of the 32 bit implementations of Pascal on the PC were ISO 7185 conforming. However, there was a large shakeout of compiler vendors in the early 1990's that removed many of these installations from the market.
Today, the basic choice is between Borland's Delphi dialect, which is also covered by the popular FreePascal open source compiler, and the ISO 7185 Pascal language, which is offered by several vendors and by the open source GPC (GNU Pascal) compiler. It is not true that these dialects are mutually exclusive. GPC offers both the ISO 7185 language and much of the Delphi language at the same time.
For more on the exact differences between Delphi and ISO 7185 Pascal, please see "Q. What are the differences between Delphi and ISO 7185 Pascal?".
ISO 7185 standard is the original language as created by N. Wirth, more precisely defined and more secure than the original. The standard makers refrained from making large improvements or extensions to the language. In fact, because Pascal is one of the most carefully designed languages and also one of the most carefully standardized, there is a high degree of ability to determine, unambiguously, if a given program construct is legal according to ISO 7185 rules.
Pascal, almost from the first of its use, was widely extended and changed. One reason for this may be that the language was also very popular in compiler classes, and tended to produce many experimental versions.
The original language, Pascal/1972 or J&W Pascal, has been around since 1972, standardized in 1982, and only minor changes were required to programs to bring them into compliance with it (see below). The basis of the language is still very strong, and the ISO 7185 standard is freely available. Further, there are many books written with the standard in mind.
While it is certainly true that a standard does no good if you cannot find anyone who makes product according to it, using a non-standard Pascal puts you at the mercy of the compiler maker for that dialect. If they go out of business, you may have to rewrite your program. Also, even if they stay around, the more obscure rules of using a non-standard dialect may be found only by trying out test examples. It can and has happened that non-standard compilers have changed and broken even programs that worked previously on earlier versions of that compiler.
Interest in standards complying Pascal has always been there, but in recent years it has been growing again, especially with the advent of a freeware ISO 7185 standard compiler with good efficiency.
Recently, I have had some folks email me that they are surprised I am supporting advanced extentions for Pascal.
Pascal always had extentions. The original compiler created by Niklaus Wirth had extentions specific to the CDC 6000 series computers. The idea of the standard was never to forbid extentions, but rather that the basic implementation be as standardized across processors and implementations as possible.
In the old days of the mainframe and line printers, there was actually a chance that a program completely coded within the language standard would be all that was needed. Now, with advanced graphics and sound, and advanced devices like video editing, the general portable program is thought by many to be dead. I would say that nothing is further from the truth. We need clean and portable interfaces and programs more than ever. What has changed is that we rely more on library construction and interfaces. Windows, OS X and Linux/X Windows can be thought of as advanced intefaces with somewhat limited portability. Advanced graphics libraries such OpenGL are paving the way to new levels of functionality and portabilty.
Now, more than ever, the choice is clear. You can always be thinking about portability, which is just another way of saying you are thinking about how to address general problems with general solutions, and see your programs have staying power. Alternately, you can design for this weeks vogue interface and see your program sink with along with that interface.
I personally love well designed standards and interfaces. To me they are the planks in a well designed and built whole. So yes, I believe in extentions. I believe Pascal can be brought forward and has something to say for decades to come.
Pascal is a structured language, using if-then-else, while, repeat-until, and for-to/downto control structures. It differs primarily from proceeding languages in that data structures were also included, with records (a feature borrowed from COBOL), arrays, files, sets and pointers.
Pascal is also unusual for forging an effective compromise between language simplicity, power, and matching of language structures to underlying machine implementation.
Pascal also has many features for compiler writers. The language is constructed to have a minimum of ambiguity. Pascal, with few exceptions, can be processed "forward" with all of the smaller elements (like constants, types, etc) being defined before they are used. Pascal requires the types and exact sizes of operands to be known before they are operated on, again leading to simplified language processing and efficient output code (although this feature has often been called a problem). For this reason, Pascal still remains a popular language to implement compilers for as part of a compiler science class.
This assertion has often been used to imply that Pascal is a toy language. I can't state the answer any better than Niklaus Wirth himself did:
"Occasionally, it has been claimed that Pascal was designed as a language for teaching. Although this is correct, its use in teaching was not the only goal. In fact, I do not believe in using tools and formalisms in teaching that are inadequate for any practical task." - Niklaus Wirth, from the 1984 ACM A.M. Turing award lecture.
This refers to the "Pascal user manual and report", by Kathleen Jensen and Niklaus Wirth. This is the original bible of Pascal. The second edition contained the finalized language under Wirth. The current edition is the fourth, containing almost twice as many pages, and contains the second edition extensively revised to meet the ISO Pascal standard.
No. The stated goal of the standards committees was to keep Pascal unchanged, but simply address the insecurities and ambiguities that had been discovered by users of the language.
Change |
Backward compatible? |
1. Procedure and function parameters |
No |
2. Var parameters on procedure and function parameters |
Yes |
3. Standard procedures and functions as parameters |
No |
4. Exact definition of type compatibility |
Yes |
5. Removal of symbol length limit |
Yes |
6. Goto target restrictions |
No |
7. "for" control variables must be local |
No |
The "backward compatible" collumn means that this change would not cause J&W compliant programs to break if they used the given feature.
The MAJOR changes are:
1. Procedure and function parameters (where the procedure or function itself was passed as a reference) appeared without a parameter list in the declared procedure or function. The standard requires that the parameter list appears as well, so that it can be checked against any call of that procedure or function. For example:
<old way>
procedure junk(function y: real);
begin
y(z);
...
end;
...
x := junk(sin, y);
<new way>
procedure junk(function y(x: real): real);
etc.
2. The original language only allowed procedure and function parameters to have value parameters. The standard allows value or VAR parameters.
3. In conjunction with (2), standard procedures and functions (those defined by the compiler itself) are no longer acceptable as procedure or function parameters in the standard. The REPORT shows several examples of passing such functions.
4. In the original language, it was left as implementation defined as to the exact rules of whether type x was compatible with type y. In fact, the first implementations at ETH (which were not documented in the REPORT) were based on "best effort", such that:
var rx: record x, y: integer; c: char end;
and
var ry: record x: integer; y: 0..10; c: char end;
Were considered compatible because they had the same basic structure. The standard tightened these rules up considerably. In the standard, types are compatible with a few exceptions only if they are the same type or "aliases" of the same type as:
type a = b;
The standard also exactly defines the rules for assignment and other compatibility modes.
Note I have marked this as a backward compatible change because the original J&W left the matter of type compatibility up to the installation, and the ISO 7185 qualify as such a definition of type compatibility.
5. The REPORT defined symbol lengths to be implementation defined. The standard defines them as "unlimited", which for practical purposes means that if the program lines will fit through the compiler, and a symbol fits on one of those lines, it should work.
6. The report leaves the rules for interprocedure goto's as implementation defined. The standard says they must only target the OUTER level of the block.
7. The control variable in a "for" statement must be a variable local to the procedure, function or program block in which it appears. This change was to allow a more efficient implementation with better checking.
In fact, most of what the standard did was simply acknowledge what were already good coding practices. The original REPORT method of assuring portability could be stated as:
APPLICATION: Stay within the guidelines as possible. Don't rely on implementation dependent features, such as the compiler's ability to recognize the similarity between types, etc.
COMPILER: Implement the language as fully as possible, and always try to do the most reasonable thing for implementation dependent features, such as attempting to determine whether types are compatible as best as possible.
The idea being that a program will not fail unless it is a poorly written program run on a poorly written compiler. The standard changed that to a much more exact set of rules that all compilers and programs must follow. As an example of the compatibility between the REPORT language and the standard, I moved several thousand lines of my own Pascal source from the "old" to a standard compiler without A SINGLE CHANGE because of the standard. The only error I found was that the compiler would not accept:
var s: array [1..10] of char;
...
writeln(s);
Because such Pascal strings must be "packed". This was actually also required in the REPORT, I just had not read it correctly (or well enough).
There are many more aspects to the ISO 7185 standard than we cover here. However, these are all specific details of the ISO Pascal language that would have qualified as "implementation specific" details under the rules of original Pascal.
The reason we discuss the P-machine variant of Pascal here is because it was used to form several other implementations, such as UCSD and Apple Pascal. In addition, UCSD Pascal was a major influence on the Borland dialects.
P-machine Pascal was designed to be a proper subset of pre-standard Pascal. Therefore, P-machine Pascal will have all the differences that exist between 1972 Pascal above and the 1982 standard, except concerning those features that are missing from P-machine Pascal, and the following additional changes/subsetting:
1. Procedures and functions may not appear as parameters.
2. Goto statements cannot reference targets outside procedure/function bodies (so called "intraprocedural gotos").
3. Only files of type "text" can be used, and then only the ones that are predefined by P4, which are "input", "output", and two special files defined so that P4 can compile itself.
4. The predeclared identifiers maxint, text, round, page, dispose, reset, rewrite, pack, unpack, and the functions they represent, are not present.
5. Dispose is not implemented, it is replaced by "mark" and "release".
6. The brackets {} for comments are not implemented.
7. Cannot write values of "boolean" to a text file.
8. Undiscriminated variant records (variant records without tagfields) are not implemented.
9. Cannot output reals in fixed format.
10. Cannot construct sets using subranges ('0'..'9').
Source: Pascal-P Implementation Notes [Nori, Ammann, Nageli & Jacobi], the PUG Newsletters, and personal testing.
First, please be warned, the contents of this section comes from my reading of the UCSD Pascal manuals, and not from running programs on the system. Note that early versions of the system had more restrictions than the later ones. What is documented here was the latest known version, which represents UCSD Pascal in 1982. It comes from "The UCSD Pascal Handbook" [Randy Clark and Stephen Koehler]. I'll be trying to get a running version of UCSD to test with in the future.
UCSD Pascal was derived from the same code as the P-machine, so you will notice similarities. The UCSD implementors added some of the standard features back in, and also removed some.
1. Procedures and functions may not appear as parameters.
2. Goto statements cannot reference targets outside procedure/function bodies (so called "intraprocedural gotos").
3. The pack and unpack system procedures are not implemented. Note that UCSD does have the "packed" keyword.
4. {} comment brackets ARE implemented, but in a nonstandard way. In ISO 7185, the following IS a valid comment:
{ hi there *)
But this is not valid in UCSD Pascal. In UCSD Pascal, comments using different brackets can be nested:
{ this is a (* nested *) comment }
Which would not work in ISO 7185 Pascal.
5. Numbers are not printed out in their "default" field widths, but are printed in the minimum amount of space. For example:
write(':', 5, ':');
Outputs:
:5:
in UCSD, but:
: 5:
(spaces depend on compiler integer width) In ISO 7185 Pascal.
Early Apple Pascal was based entirely on the UCSD system, and the same comments apply to it as to UCSD Pascal above.
Because Borland Delphi is a widely used version of Pascal, it is useful to compare the two languages. Note that I compare here only the differences between Borland Delphi and the basic ISO 7185 standard. Undiscussed are any extensions provided by Borland Delphi. In other words, this section answers the question "why doesn't my standard Pascal program run under Borland Delphi?", and perhaps "what can I write in Borland Delphi that will also be compatible with the ISO 7185 standard?".
1. Procedures and functions may not appear as parameters (it is true that it can be done, but a non-standard syntax must be used).
2. Goto statements cannot reference targets outside procedure/function bodies (so called "intraprocedural gotos").
3. No file buffer variable handling. Standard Pascal has file "buffer variables", and "get" and "put" procedures to operate on them. This functionality is not present in Borland Delphi.
4. No "sized" dynamic variable allocation. Given a variant record, the size of a particular variant cannot be specified as per the standard. I.e., the following statement is invalid:
new(p, t)
Where t is a variant record tag type.
5. The functions "pack" and "unpack" are not implemented.
6. { and (*, } and *) are not synonyms of each other as required by the standard. Ie.:
{ comment *)
is not valid in Borland Delphi (Delphi uses the scheme of allowing the different comment types to indicate nested comments).
7. Does not replace eoln with space as the standard requires. When reading through the end of a line, the eoln character is supposed to be replaced with a space in ISO 7185. Instead, reading through eoln in Borland Delphi gives the character code for carriage return (13), followed by line feed (10).
8. Numbers and booleans are not printed out in their "default" field widths, but are printed in the minimum amount of space. For example:
write(':', 5, ':');
Outputs:
:5:
in Delphi, but:
: 5:
(spaces depend on compiler integer width) in ISO 7185.
For booleans:
write(':', true, ':');
outputs:
:true:
in Delphi, but:
: true:
In ISO 7185.
9. Temporary files are not supported. Applying reset() or rewrite() to a file results in an error under Delphi. Under standard Pascal it opens a temporary file that exists only for the run of the program.
Note: The following shell command line was used to test for the above behavior.
> dcc32 -CC test.pas
The option "-CC" tells the Delphi compiler to assume that the application is a shell application, and the resulting test.exe was run in a shell.
I have collected the above points into a program that demonstrates each:
program dephitest(output);
label 1;
type r = record case b: boolean of true: (i: integer); false: (c: char) end;
var f: text;
c: char;
rp: ^r;
s1: packed array [1..10] of char;
s2: array [1..10] of char;
procedure x(procedure y); begin y end;
procedure z; begin goto 1 end;
procedure y; begin end;
begin
{ 1: Procedure and function parameters }
x(y);
{ 2: Interprocedure goto }
z;
1:
{ 3: File buffer handling }
rewrite(f);
writeln(f, 'hi there');
reset(f);
c := f^;
{ 4: Sized variant record allocation }
new(rp, false);
{ 5: Pack and unpack }
s1 := 'hi there ';
unpack(s1, s2, 1);
pack(s2, 1, s1);
{ 6: Comments as synonyms *)
writeln('This should print');
{ 7: Replacement of eoln with space }
rewrite(f);
writeln(f);
reset(f);
read(f, c);
writeln('The value of eoln is: ', ord(c));
writeln('Should be same as space (32)');
{ 8: Numbers and booleans with default width }
writeln(1);
writeln(true);
writeln(false);
writeln('Should be similar to:');
writeln;
writeln(' 1');
writeln(' true');
writeln('false');
writeln;
writeln('(ie., justified right in field)');
{ 9: Anonymous (temp) files }
rewrite(f);
end.
Most of the points simply won't compile, but the other ones that will, but do the incorrect thing will print out to that effect. The program run on an ISO 7185 compilant system appears as:
This should print
The value of eoln is: 32
Should be same as space (32)
1
true
false
Should be similar to:
1
true
false
(ie., justified right in field)
I get the occasional email telling me that Delphi is capable of performing all these operations. Please note once again, we are not comparing features here, but compliance with the original language Pascal. Pascal didn't and does not restrict what extensions or local methods exist on a particular implementation, just what minimum set of standard features must exist to compile programs that are in the Pascal language.
No. The standard story which you can find on Wikipedia and several other web sites goes like this. Borland introduced Turbo Pascal before the ISO 7185 standard was produced, and followed the UCSD dialect of Pascal that existed before the standard.
The original Pascal language definition appeared in 1970, and Pascal was revised by its author into its final form in 1972. In 1983, the ISO 7185 standard appeared. Also in 1983-1984, Borlands first compiler for Turbo Pascal appeared.
The problem with this "theory" is Turbo Pascal implemented neither the ISO 7185 1983 standard nor the 1972 version. Also, the draft standard that became ISO 7185 was printed in public several times during its development, in the Pascal Users Group newsletters in 1979 (#15, page 5, you can find it on this site). UCSD also followed neither the 1983 nor 1972 standards, but at least had the excuse that it came well before the ISO 7185 standard. Further, the limitations of UCSD were, for the most part, due to the limitations of the P-series compiler kit it was based on. In any case, UCSD implemented more of full Pascal than Turbo did, so it is hard to say that Turbo was simply following UCSD.
All of the facts above can be verified by the original documents appearing on this web site. We invite you to look for yourself, and please remember that there is no consequence for placing incorrect statements on web sites such as Wikipedia.
When Turbo Pascal first came out, the users' manual had the following text (taken from Turbo Pascal Version 2.0 Reference Manual):
--------------------------------------------------------------------------------------
TURBO Pascal closely follows the definition of Standard Pascal as defined by
K. Jensen and N. Wirth in the Pascal User Manual and Report. The few and
minor differences are described in section F.
...
F. TURBO VS. STANDARD PASCAL
The TURBO Pascal language closely follows the Standard Pascal defined by
Jensen & Wirth in their User Manual and Report, with only minor differen-
cies introduced for the sheer purpose of efficieny. These differencies are desc-
ribed in the following. Notice that the extensions offered by TURBO Pascal
are not discussed.
F.1 Dynamic Variables
Dynamic variables and pointers use the standard procedures New, Mark, and
Release instead of the New and Dispose procedures suggested by Standard
Pascal. Primarily this deviation from the standard is far more efficient in terms
of execution speed and required support code, and secondly it offers compati-
bility with other popular Pascal compilers (e.g. UCSD Pascal).
The procedure New will not accept variant record specifications. This restric-
tion, however, is easily circumvented by using the standard procedure Get-
Mem.
F.2 Recursion
CP/M-80 version only: Because of the way local variables are handled dur-
ing recursion, a variable local to a subprogram must not be passed as a var-
parameter in recursive calls.
F.3 Get and Put
The standard procedures Get and Put are not implemented. Instead, the Read
and Write procedures have been extended to handle all I/O needs. The reason
for this is threefold: Firstly Read and Write gives much faster I/O, secondly
variable space overhead is reduced, as file buffer variables are not required,
and thirdly the Read and Write procedures are far more versatile and easier to
understand that Get and Put.
F.4 Goto Statements
A goto statement must not leave the current block.
F.5 Page Procedure
The standard procedure Page is not implemented, as the CP/M operating sy-
stem does not define a form-feed character.
F.6 Packed Variables
The reserved word packed has no effect in TURBO Pascal, but it is still allo-
wed. This is because packing occurs automatically whenever possible. For the
same reason, standard procedures Pack and Unpack are not implemented.
F.7 Procedural Parameters
Procedures and functions cannot be passed as parameters.
--------------------------------------------------------------------------------------
Actually, there are five other points on which TP differs from orginal Pascal that were not mentioned in the manual, see the section Q. What are the differences between Borland Delphi and the ISO 7185 standard, points 3, 6, 7, 8, and 9. Further, at the time TP was published in 1983, it also lacked variant records, not mentioned in the above manual. In any case, TP differed from Pascal substantially (despite the "minor differences" comment above), and Borland was well aware of what the original standard was.
No, it is not possible for several reasons. First, there are several differences between the languages that are purely syntactic in nature, such as the way comments work. Second, there is no way to write, for example, file handling functions that accept all types of files. Third, there are features such as interprocedure gotos that only the compiler can implement.
I have seen many claims in the past that it is "easy to bridge the differences between ISO Pascal and the Delphi language", but it is usually apparent that those making the claim haven't read the ISO 7185 standard in any detail. It simply isn't that easy.
Sure. You can use the ISO 7185 "level 0" Pascal with the following omissions:
1. Do not use procedure or function parameters. These have different syntax in ISO 7185 and Delphi.
2. Do not use intraprocedural gotos (gotos that leave the current procedure or function). If you need a deep nested bailout to a higher level procedure, try setting an error variable and checking that after each procedure/function call that might have an error, then skipping either out of the routine, or to the goto point.
3. Do not use file buffer handling (such as f^ accesses, where f is a file), nor the built in routines "get" and "put". Basically, this just means you cannot use the lookahead buffering that ISO 7185 Pascal provides.
4. Do not size your variant records with "new". Delphi knows about variant records, but not how to size them (at least not the standard way). This will have no functional effect on your program, it will just take more runtime space.
5. Always use the keyword "packed" on a string that is intended to be printed with "writeln". Delphi does not require this, but ISO 7185 does.
6. Always include the files "input" and/or "output" in the program header if they are used in the program (and remember that write/read with no file parameter is a defacto reference). Delphi simply ignores these header parameters, so they don't hurt.
7. Always use format specifications to set the width that will be output for integers. This will remove differences from varying default fields.
8. Don't use external files other than input and output, since Delphi does nothing with header files.
Finally, note that you MUST also comply with the ISO 7185 standard requirements for this to work. For example, ISO 7185 forbids gotos into program structures like "for" loops, etc, whereas delphi does not. There are many other such restrictions of ISO 7185 that are relaxed in Delphi, not to mention the many Delphi extensions that you need to refrain from using (of course, all Pascal compilers have extensions, so that would apply to them as well). Also remember: Pascal originally had no "string" type. This might seem strange, but C (for example) does not have one either. Instead, you use an array of characters (which is not the same thing!). See other sections of this FAQ.
The old bit of cross compiler checking that applies here is to run a check compile on all of the different compilers you plan to be compatible with frequently. This will tell you about problems with different compilers before they get out of hand.
For this author's point of view, I lived with mutiple Pascal compilers for years, and made it work by isolating system specific code to modules that implemented that on a particular platform. For example, I had a set of routines in a module I called "basicio.pas" that contained things like opentext(f, filename), closetext(f) and similar functions. Then, this module is simply recoded or swapped out to move to a different compiler.
Yes. GNU GPC is such a compiler. It will take switches that configure it for either ISO 7185 use or Borland use. Note that several of the differences between the languages are compatible between the languages without the need for an option. Here is a list of ISO 7185 to Delphi differences, and whether they require a configuration optiion, and why. See the section "Q. What are the differences between Borland Delphi and the ISO 7185 standard?", for details on each difference.
1. Procedure and function parameters.
Requires a configuration option: No
Reason: In delphi mode, the keyword procedure or function in a procedure or function header is an error. In ISO 7185 Pascal, it introduces a procedure or function parameter. This makes it essentially an extention to Delphi.
2. Interprocedural gotos.
Requires a configuration option: No
Reason: Such gotos cause an error in Delphi, so it behaves as simple extention.
3. File buffer access, "get" and "put" procedures.
Requires a configuration option: No
Reason: There are no get or put procedures in Delphi, and access to a file variable is illegal, so both of these act as simple extentions.
4. Sized variant record allocation.
Requires a configuration option: No
Reason: Allocating a variant record with new with Delphi is illegal, so it behaves as a simple extention.
5. "pack" and "unpack" functions.
Requires a configuration option: No
Reason: There are no pack or unpack procedures in Delphi, so it behaves as a simple extention.
6. { and (*, } and *) are not synonyms.
Requires a configuration option: Yes
Reason: There is no way to have a single behavior that covers both the ISO 7185 and Delphi definition of how comments work.
7. End of line returns ASCII codes.
Requires a configuration option: Yes (or a different runtime library)
Reason: There is no way to satisfy both program standards. An eoln is either a space or the underlying ASCII characters.
8. Default field widths.
Requires a configuration option: Yes (or a different runtime library)
Reason: The basic behavior of the compiler must be changed.
So of the 9 basic differences between the standards, 3 of them are mutually incompatible between the two languages, and require options to change the basic workings of the compiler. The 6 remaining differences that are simply additions to the Delphi language can also be thought of as "freebies" that can be added to any Delphi compatible compiler in order to enable it to be closer to the ISO 7185 standard without compromising its Delphi processing in any way.
If your compiler is ISO 7185 compliant, it is required to have a "compliance statement", either printed by the compiler itself, or appearing in the documentation, of the form:
(this processor) complies with the requirements of level (number) of ISO/IEC 7185.
Note that some compilers may require a standard "switch" or option be turned on to output this message. See your documentation.
If your compiler does NOT have this statement, think of it this way. If the company is claiming to be compliant, how could they have missed this required message, which is on page 5 of the standard ? They probably missed a lot of other standard requirements as well.
It is possible for you to perform your own tests to determine how standard your compiler is. Simply download and compile one of the standard-obeying programs from my site:
http://www.moorecad.com/standardpascal/source.html
These have all been tested to at least compile under ISO 7185 Pascal. They are all fairly famous programs that have been around since the 1970s. They are large programs, and tend to use most of the language.
NOTE: several persons on the Internet have been claiming to have compiled and run these programs on non-standard compilers, and claim that as "proof" of a compilers standard compliance. In reality, they have simply hacked the source until it compiles on the non-standard processor. This proves nothing. Do not believe in source you have obtained from elsewhere, get it from my site where it is in ISO 7185 form.
Also, most of these programs were written before the ISO 7185 standard, and needed some small modification to get them to compile under ISO 7185 rules. Again, the answer is to only get the files from the above mentioned web site.
We have done a full evaluation of several ISO 7185 compilers for you, which can be found at:
Pascal is a strongly typed language and attempts to protect references and types. In C, it is possible to use a cast to convert any type to any other, and the address of any variable can be taken at any time.
Pascal does not have these features. It is still possible to generate invalid references (see next question), but it is not as common. The inability of Pascal to generate type conversions can require more work to accomplish the same programming results. However, functionally there is little difference between these languages, and there is usually a way to do the same operation in both.
One significant difference that comes up is that Standard Pascal does not allow variable length data structures (such as arrays), and C and similar languages do. It is a common extension to Pascal compilers to allow this, but of course those implementations may not be standard.
UCSD Stands for University of California at San Diego. Professor Kenneth Bowles was teaching Pascal there, and used the P-machine implementation of Pascal to create a Pascal that ran on Microcomputers (as PCs were called back in the 1970's). It was an interpreted only version of Pascal, and included a complete operating system written in Pascal. Professor Bowles, at the time, said he didn't believe that the microprocessors of the time had enough power to directly run compiled Pascal programs. What Professor Bowles did is to use the P2 compiler as a front end, then write an interpreter for the intermediate code directly in machine code on the target machine, then run the P2 compiler on that. This was not a unique step, and in fact this was one course of action suggested by the "Pascal P compiler implementation notes" published in December of 1974.
The Pascal P compiler implementation notes
What made the UCSD implementation unique is that Professor Bowles created a complete operating system that remained interpreted, and used the fact that the code was interpreted to increase the portability of the system, across multiple machines and processors. This work served as the inspiration for the later Java language.
UCSD was not strictly compatible with Wirths Pascal. Some of the incompatabilities came from the Wirths P-machine implementation, which didn't, and was not designed to implement the whole language. However, the UCSD implementation did put back several standard Pascal features like typed file I/O (as indeed was the intent of the P compiler bootstrap method).
UCSD Pascal is said to be a direct ancestor to Apple Pascal, and Borland Pascal implements many of the original functions of UCSD Pascal, perhaps making it also a defacto derivative of UCSD Pascal (note that Borland Pascal did not come from the P-machine implementation, but was written in machine code).
See also:
http://www.threedee.com/jcm/psystem
http://en.wikipedia.org/wiki/UCSD_Pascal
http://en.wikipedia.org/wiki/UCSD_p-System
http://www.ics.uci.edu/~archive/documentation/p-system/p-system.html
http://www.siconic.com/crap/pascal
The original manuals for the system can be found here:
http://www.mirrorservice.org/sites/www.bitsavers.org/pdf/univOfCalSanDiego/
There was a IBM-PC/DOS version of the UCSD p-system, and this can still be run via the Windows operating system (I tried it with WIndows XP) for people interested in running the system. In addition, the full source code for the entire system has been released in both the UCSD USUS Software library:
http://www.threedee.com/jcm/usus/
In addition, people interested in UCSD will find a lot of history of the system written in the PUG newsletters:
http://www.moorecad.com/standardpascal/pug.html
The "P-Machine" was actually a "kit" of software issued by N. Wirth and people working with him that was aimed at getting more Pascal compilers running on different machines. The kit consisted of a compiler that output to a "virtual machine" or a machine that was never designed to be implemented on real hardware, as well as the actual binary file that was created by having the compiler self-compile itself. The idea was that if you wrote a program to interpret the "virtual machine" code on another CPU, you could quickly get an interpreted version of Pascal running. Then, you would create a "back end" program to create actual machine code for your system, recompile the new compiler with that, and effectively "bootstrap" the compiler to your brand of machine.
The P-machines most famous use was in UCSD Pascal. In that case, a full compiler was not made and the interpreter was the final product.
See also:
http://www.cwi.nl/~steven/pascal/book/pascalimplementation.html
And our P-machine section at:
And the document "Pascal P compiler implementation notes" published in December of 1974.
The Pascal P compiler implementation notes
There has been a fair amount of confusion concerning the fact that the P-machine implementation didn't implement the entire language Pascal. This sometimes leads to the claim that, since this compiler was sanctioned by Wirth himself, that it represents a valid alternate version of the Pascal language. The P-machine was a bootstrap kit designed to make porting a Pascal compiler faster. It didn't implement the entire Pascal language, because it didn't need it for its own use. For example, it was restricted to text only input and output because the Pascal source, and even the intermediate code in an assembly like format, was all in text. The P-machine was designed to bootstrap itself as a compiler, that's all. It was never designed to define a subset of Pascal.
Like most languages, it is possible to write Pascal programs that crash or otherwise compromise the computer they are running on.
1. Infinite loop. There is nothing to keep a program from locking up in a loop forever:
while true do;
Will do that. On many modern systems it is possible to terminate such programs manually.
2. Insecure variants. The following procedures will effectively change a pointer to an integer and back:
type intptr = ^integer;
convert = record
case boolean of
false: (i: integer);
true: (p: intptr)
{ end }
end;
function int2ptr(i: integer): intptr;
var cr: convert;
begin
cr.i := i; { place integer }
int2ptr := cr.p { return pointer }
end;
function ptr2int(p: intptr): integer;
var cr: convert;
begin
cr.p := p; { place pointer }
ptr2int := cr.i { return integer }
end;
So that you can usually crash your program by:
p := int2ptr(500);
p^ := 6;
(ie, poke an arbitrary location). Note that it assumes that pointers and integers have the same length. Note that it IS possible for the compiler to prevent this, see your local documentation.
3. Initialization. There is no guarantee that a variable in Pascal is initialized to a known value:
procedure x;
var p: ^integer;
begin
p^ := 6;
...
Use of "p" before it is initialized could result in a random area being written to, and perhaps a program crash. Note that many compilers provide for initialization of global, local or dynamic variables, so this insecurity may not exist. Check your local documentation.
4. Reuse of disposed data. The following code illustrates this:
var p: ^integer;
...
new(p);
...
dispose(p);
p^ := 1;
The variable "p" points to was returned to general storage by dispose. The assignment statement below that writes the (now invalid) space that it used to occupy. The system is free to give that space to a completely different piece of data or even another application running concurrently. This is a setup for a serious data corruption error.
Some compilers make sure that the pointer "p" would be set to NIL afterwards, and the value NIL can be checked for as an invalid reference. But even that can be circumvented. If there is another pointer of the same type that "p" is copied to, that pointer is not cleared by dispose.
Curing the dispose problem is very difficult. It can be done, but usually dramatically increases the amount of overhead in the system as the compiler is forced to track all possible pointers in the program.
There is nothing about the C language that is inherently more efficient than Pascal, there are simply good compilers and bad compilers. Most programmers who believe that C is fundamentally more efficient aren't understanding the foundations of how compilers work. For example, the C statement:
a++;
Would correspond to the Pascal statement:
a := a+1;
While it may look more efficient, because compilers commonly turn "a++" into a single "increment" instruction on the target machine, Pascal compilers can recognize that "a:= a+1" is an increment as well, and give the same single instruction.
Many books have been published on ISO 7185 Pascal. I will be happy to collect reviews here. I have also included some books that discuss Pascal in J&W "report" form. See the "differences" section above. In practice there is virtually no difference.
There are really countless books available on the standard language. However, it is difficult to tell from the descriptions if a particular book does or does not deal with general Pascal, or with a specific implementation of Pascal. You cannot always tell by the title. All of the below titles are titles I own, and verify that they are either standard Pascal or language neutral, meaning that they cover different implementations, but are not directed at a specific implementation.
Kathleen Jensen and Niklaus Wirth, Revised by Andrew Mickel and James Miner.
Published by Springer-Verlag.
ISBN-10: 0387976493
ISBN-13: 978-0387976495
A definitive reference on standard Pascal and a must have book.
Doug Cooper.
Published by W. W. Norton and Company.
ISBN-10: 0393301214
ISBN-13: 978-0393301212
Doug Cooper is a Professor at UC Berkley. If you buy just ONE book on standard Pascal, this would be it. Contains ALL of the points in the standard, in the most readable format anywhere.
Niklaus Wirth.
Published by Prentice-Hall.
ISBN-10: 0130224189
ISBN-13: 978-0130224187
A good textbook on programming in general, with examples in Pascal, written by the author of Pascal.
Doug Cooper.
Published by W. W. Norton and Company.
ISBN-10: 0393963977
ISBN-13: 978-0393963977
Another Doug Cooper blockbuster, this is probably the most used classroom book on Pascal. Recommended if you are learning standard Pascal.
Doug Cooper.
Published by W. W. Norton and Company.
ISBN-10: 0393955400
ISBN-13: 978-0393955408
This is a combined version of Doug's Oh! Pascal and Standard Pascal books, with some revisions.
Henry F. Ledgard, Paul A. Nagin and John Hueras.
Published by Hayden.
ISBN-10: 0810451247
ISBN-13: 978-0810451247
A writing style guide for Pascal.
The Art of Scientific Computing: William H Press, Brian P. Flannery, Saul Teukolsky and William T. Vetterling.
Published by Cambridge University press.
ISBN-10: 0521375169
ISBN-13: 978-0521375160
Something of a mathematical textbook that contains example programs, it has a huge selection of various numeric processing routines with copious descriptions, all written in Pascal.
Henry Ledgard
Addison-Wesley.
ISBN-10: 0201117762
ISBN-13: 978-0201117769
An excellent overview of Pascal programming and style tips.
Richard Conway, David Gries, E. Carl Zimmerman.
Published by Winthrop.
ASIN: B000O154NIGood general introduction on Pascal.
Henry Ledgard and Andrew Singer
Random House (1982)ASIN: B000WAH8YE
An introduction to Pascal written in a very interesting and original way.
Walter Savitch.
Published by Benjamin/Cummings Publishing Company.
ISBN-10: 0805374582
ISBN-13: 978-0805374582
Comments: Fairly intense introduction to Pascal.
Richard J. Tingey and Suzanne Loyer Antink.
Published by South Western Publishing Co.
ISBN-10: 0538622539
ISBN-13: 978-0538622530
A Pascal language textbook, in detail.
Guy J. Hale and Richard Easton.
Published by D. C. Heath and Company.
ISBN-10: 0669075795
ISBN-13: 978-0669075793
A Very detailed overview of data structures in Pascal.
Aaron M. Tenenbaum and Moshe J. Augenstein
Prentence-Hall, Inc.
ISBN-10: 0131966685
ISBN-13: 978-0131966680
An intensive overview of data structures.
Per Brinch Hansen
Prentice-Hall
ISBN-10: 0130830984
ISBN-13: 978-0130830982
It is both a good book on compiler construction, and contains a complete Pascal compiler for the IBM-PC (16 bit mode)..
Michael Rees and Dave Robson
Addison-WesleyISBN-10: 0201184877
ISBN-13: 978-0201184877
Sort of a user's and implementor's manual for the Pascal-S compiler/interpreter.
Robin Hunter
John Wiley and Sons Ltd
ISBN-10: 0471907200
ISBN-13: 978-0471907206
A good textbook on making compilers using Pascal and the P-machine implementation.
Hayden Book Co
ISBN-10: 0810457679
ISBN-13: 978-0810457676
Programming methodologies for Pascal programmers.
Cambrdige University Press
ASIN: B000K7JAQ8
Use of recursion in Pascal.
William C Brown Pub
ISBN-10: 0205113192
ISBN-13: 978-0205113194
Pascal from an engineering point of view, with example engineering applications.
Van Nostrand Reinhold Computer
ISBN-10: 0442218893
ISBN-13: 978-0442218898
An unusual book, gives extensive techniques for logic design with Pascal programs and what appears to be a beginning RTL compiler, also written in Pascal.
Addison-Wesley
ISBN-10: 080537082X
ISBN-13: 978-0805370829
Very complete coverage on implementing structures in Pascal files.
William C Brown Pub
ISBN-10: 069700080X
ISBN-13: 978-0697000804
Textbook for beginning to intermediate Pascal.
W.H. Freeman & Company
ISBN-10: 0716782588
ISBN-13: 978-0716782582
Contains a series of programming examples in Pascal.
Prentice Hall
ISBN-10: 0131644432
ISBN-13: 978-0131644434
Introduction to the computer science subject of computability using Pascal.
John Wiley & Sons
ISBN-10: 0471901334
ISBN-13: 978-0471901334
Details the ISO 7185 Pascal validation suite. This book will only be of interest to folks writing ISO 7185 Pascal compilers. See also the PUG newsletters.
Prentice Hall
ISBN-10: 0131627937
ISBN-13: 978-0131627932
A great book, it discusses graphics implementation in a compiler independent way. It does this by describing, in detail, the base graphics calls used to costruct the routines in the book. Most books of this kind are pinned to a particular implementation. The last time I checked, it could be had new for $0.26 (26 cents), a very underappreciated book.
John Wiley & Sons
ISBN-10: 0471311987
ISBN-13: 978-0471311980
A good overall textbook on Pascal, notable for its history of computing and advanced subject coverage.
John Wiley & Sons
ISBN-10: 0471823090
ISBN-13: 978-0471823094
A style and methods guide for Pascal.
Prentice Hall
ISBN-10: 0137010788
ISBN-13: 978-0137010783
A very unusual book, it covers schemes to implement concurrent programming algorithims using Pascal, and introduces a modified Pascal-S implementation that simulates multiple CPUs and threads. There is a new, expanded second edition of this book for a breathtaking price, but I suspect it is possible that Pascal as an implementation language might have been purged from the book, and I don't have the $70 to gamble with. In the meantime, the first edition is Pascal based, interesting, and has a nifty cover drawing to boot.
Springer
ISBN-10: 0387912487
ISBN-13: 978-0387912486
It's the ANSI standard, reprinted with annotations, sidebars that explain what each part of the standard means in english and not standardise (that strange language that people learn when going to standards meanings). The bad news is that ANSI basically took their standard out back and shot it in the head. The good news is that it was 99% identical to the ISO 7185 standard, so this book is still useful. In any case, it incorporates the entire original ANSI standard and don't feel like shelling out the $0.32 (32 cents) used copies of the original ANSI standard are going for on Amazon.
The most common source for books is:
Also good is:
Powells Books in Portland, OR, USA (a great place to spend an afternoon, by the way).
And:
Probably the best all around book is:
Per Brinch Hansen
Prentice-Hall
ISBN 0-13-083098-4
It is both a good book on compiler construction, and contains a complete Pascal compiler for the IBM-PC (16 bit mode)..
A Small, toy language compiler for a Pascal-like compiler is contained in:
Niklaus Wirth
Prentice-Hall
ISBN 0-13-022418-9
A good textbook on programming in general, with examples in Pascal, written by the author of Pascal which has a last chapter on compiler construction. This book is also perhaps Wirth's best all around "how to" book on programming in general, and all the examples are in Pascal.
Michael Rees and Dave Robson
Addison-WesleyISBN-10: 0201184877
ISBN-13: 978-0201184877
Sort of a user's and implementor's manual for the Pascal-S compiler/interpreter.
Robin Hunter
John Wiley and Sons Ltd (October 2, 1985)
ISBN-10: 0471907200
ISBN-13: 978-0471907206
A good textbook on making compilers using Pascal and the P-machine implementation.
John Wiley & Sons
ISBN-10: 0471901334
ISBN-13: 978-0471901334
Details the ISO 7185 Pascal validation suite. This book will only be of interest to folks writing ISO 7185 Pascal compilers. See also the PUG newsletters.
Niklaus Wirth created a complete compiler-interpreter system known as PASCAL-S, which processes a subset of the full Pascal language. You can find that here:
It was originally designed for the CDC computer, but I have changed a few lines to use file handling on a typical PC based compiler.
You can also find the related P4 compiler project on this site, which is a separate compiler/assembler/interpreter for the P4 stack computer language. See the p4 compiler page:
The newsgroup news:comp.compilers discusses this quite frequently. Pascal is still a very popular language to implement as a class project.
Pascal and C have one feature in common, they include no basic support for handling of strings of characters. Strings, as implemented in Basic and other languages, are a "high cost" data element, mainly because a lot of character copying must occur within the string functions. Most "professional" (ie., used for paid programming) languages choose to leave creation of string handling up to the programmer. The reason is that in many or even most cases, applying primitives to simple arrays of characters can achieve the same result at less expense.
However, one big difference between C and Pascal is that C allows variable length string passage, which greatly facilitates creation of general purpose string handling routines, and manipulation of string constants. In Pascal, by contrast, you must declare a string as a fixed length array:
var string: packed array [1..50] of char;
Which means that all of your strings must have the same length as the handler routines expect. Further, for any reasonable size of string, assigning string constants to strings is prohibitive:
string = 'hello, world ';
And this is a short example ! More commonly, strings must be 100-200 characters in length, so the assignment of string constants is just impractical.
No matter what the length of string, the first and best trick is to make extensive use of space padded strings:
var s: packed array [1..10] of char;
...
s := 'hello ';
For example to read a word from the input:
var i: integer;
s := ' ';
i := 1; { set 1st character in string }
while not input^ = ' ' do { read characters }
if i <= 10 then begin { not overflow }
read(s[i]); { get next character }
i := i+1 { next character }
end;
If the user inputs "one more", s would be "one ", or the first word with blank padding. Note the trick used to find the end of the word. Eoln is returned as a space, which also happens to be the word delimiter, and in standard Pascal every line must be terminated by a eoln. Once all your strings are in space padded form, operations on them are easy:
var s1, s2: packed array [1..10] of char;
...
s1 = s2; { find if strings are equal }
s1 < s2; { find if s1 is lexographically smaller than s2 }
s1 := s2; { assign strings }
Note that comparing strings for order depends on the value of the space character. If space has a smaller ordinal value than all other characters, then "ab " is going to be less than "abc ". If space has a larger values than others, the opposite would be true. In ASCII, the space is the lowest value character, and this indeed gives the lexographical sort that is most popular (ie., that shorter character sequences go first). Space padded strings even work for strings with embedded spaces ! A string like:
s := 'this is a very long string ';
Would be equal to "this is a very long string" without the trailing blanks. This works because, reading left to right as we do, any spaces after the text are unimportant (and typically will not make any difference to printout). Putting text through processing using blank padded strings will have the effect of trimming all the trailing blanks off the text, often a desirable side effect.
To find the length of a blank padded string:
var e: integer;
s: packed array [1..100] of char;
...
e := 100; { set maximum }
{ find end of string }
while (s[e] > 1) and (s[e] = ' ') do e := e-1;
Will set e to be the last character of the string, or to 1 if the string is empty. A check for a blank string need not be:
s = ' ';
or similar, but simply:
s[1] = ' ';
Because if the first character is empty, the entire string is usually empty as well.
Here is a series of routines, and a test for them, that perform common string operations.
program test(output);
label 99; { terminate program }
const strmax = 20;
type string = packed array [1..strmax] of char;
var a, b, c: string;
function len(var s: string): integer;
var i: integer;
begin
i := strmax; { index the end of the string }
{ back up over any spaces }
while (s[i] = ' ') and (i > 1) do i := i-1;
{ adjust for zero }
if s[i] = ' ' then i := 0;
len := i { return length }
end;
procedure clear(var s: string);
var i: integer;
begin
for i := 1 to strmax do s[i] := ' '
end;
procedure insert(var a, b: string; p: integer);
var la, lb, i: integer;
begin
la := len(a); { find lengths }
lb := len(b);
if la+lb > strmax then begin
writeln('*** String too long');
goto 99
end;
for i := la+lb downto p+lb do a[i] := a[i-lb]; { copy remainder up }
for i := 1 to lb do a[i+p-1] := b[i] { copy new section in }
end;
procedure extract(var a, b: string; p, l: integer);
var i: integer;
begin
clear(a);
for i := 1 to l do a[i] := b[p+i-1]
end;
procedure cat(var a, b: string);
begin
insert(a, b, len(a)+1)
end;
procedure right(var a, b: string; l: integer);
begin
if l > len(b) then begin
writeln('*** Not enough characters');
goto 99
end;
extract(a, b, len(b)-l, l) { find net right characters }
end;
procedure left(var a, b: string; l: integer);
begin
if l > len(b) then begin
writeln('*** Not enough characters');
goto 99
end;
extract(a, b, 1, l) { find net left characters }
end;
function int(var s: string): integer;
var v, i, sgn: integer;
function next: char;
begin
if i <= strmax then next := s[i] else next := ' '
end;
begin
v := 0; { set default value }
sgn := 1; { set sign positive }
i := 1; { set 1st character }
while (next = ' ') and (i <= strmax) do i := i+1;
if next = '+' then i := i+1
else if next = '-' then begin i := i+1; sgn := -1 end;
if not (next in ['0'..'9']) then begin
writeln('*** Number not found');
goto 99
end;
while next in ['0'..'9'] do begin
v := v*10+ord(next)-ord('0');
i := i+1
end;
int := v*sgn
end;
begin
a := 'hi there ';
b := ' george ';
clear(c);
writeln('length a: ', len(a));
writeln('length b: ', len(b));
writeln('length c: ', len(c));
c := a;
cat(c, b);
writeln('cat: ', c);
b := ' boy ';
insert(c, b, 9);
writeln('insert: ', c);
extract(a, c, 4, 9);
writeln('extract: ', a);
left(a, c, 2);
writeln('left: ', a);
right(a, c, 3);
writeln('right: ', a);
a := ' -50 ';
writeln('int: ', int(a));
99: { terminate }
end.
Which resulted in the run:
length a: 8
length b: 7
length c: 0
cat: hi there george
insert: hi there boy george
extract: there boy
left: hi
right: org
int: -50
Of course, you can set strmax to any convient value. If you have used the Basic function mid$(), you will find this equivalent to extract().
If you needed several different lengths of strings, you would have to create a set of the above routines for each length, say "cat40()" for 40 character strings. A better way is if your Pascal installation allows variable length string parameters, such as ISO 7185 level 1 conformant parameters. This allows you to define the above set of routines so they can operate on any length of padded strings.
The best way to achieve "tight" code in standard Pascal is to use custom strings for given tasks. For instance, if you are going to input a command from the user, and use that to look up a string in a table, you can create a string type based on the maximum length of command string that you are going to need. Then you can create a few custom handling procedures for that string type. This works best if you use constant declarations to declare the length of the string:
const strlen = 100;
type mystring = packed array [1..strlen] of char;
This way, if you must change the string size later, there is less difficulty. Although these kinds of solutions may create a larger program listing, the result is usually faster than using a general purpose string library, and the cost saved by not including the general string library may pay for any duplication of effort.
Clearing long strings is best done as:
const strlen = 100;
var i: integer;
s: packed array [1..strlen] of char;
...
for i := 1 to strlen do s[i] := ' ';
This code will usually take less space than spelling out a long string of blanks, take the same amount of CPU time (after all, a string assign uses a copy loop as well), and most importantly, won't have to be changed if we change the string lengths in the program.
If you have to have both long strings and also occasionally set these strings to a known constant, it helps to create a procedure to initialize them. You simply find what the longest constant string you are going to assign to the variable strings, then create a special string type for that. Finally, you create a custom procedure to do the copy:
const strlen = 250; { our "big" string }
cstlen = 12; { our constant strings }
type string = packed array [1..strlen] of char;
cstring = packed array [1..cstlen] of char;
var s: string;
procedure inistr(var s: string; c: cstring);
begin
for i := 1 to strlen do s[i] := ' '; { clear result }
for i := 1 to cstlen do s[i] := c[i] { place string }
end;
...
inistr(s, 'hello, world';
Because Pascal does not have built in strings does not prevent you from implementing them yourself:
type string = record
len: integer;
str: packed array [1..100] of char;
end;
Then you can go ahead and define a full set of procedures to concatenate, find substrings, etc., within a string. The principal drawback here is again, initializing strings. But using the "initialize procedure" method, in combination with the "find end of blank string" method, it is easy to create a procedure that will do the job:
var s: string;
...
inistr(s, 'mystring ');
Here intstr would find the exact length of the string by blank termination, then assign that to the string length, and place the string body.
One of the more irritating things in Pascal is converting an integer to an enumerated type. Even though there is a one to one correspondence between integers and enumerateds, and you can find the integer value of any enumerated value with ord, you cannot go the other way easily. You might use a case statement or similar kludge.
But there is a trick you can use that takes a bit of space, but incurs very little speed penalty and is as easy to use sourcewise as the hypothetical "unord" function would be:
type enum = (one, two, three, four, five, six, seven, eight, nine, ten);
var ei: enum;
etran: array [10] of enum;
begin
{ initalize translation array }
for ei := one to ten do etran[ord(ei)] := ei;
...
ei := etran[5];
Now translating an integer back to the enumerated type is simply a fast array lookup. The price for this is the size of the translation array, and the fact that you must declare the translation array with a number that depends on the size of enum.
Standard Pascal has no capability to create tables of fixed data at compile time. But your compiler may be able to turn assigns into preinitalized data if it can determine that the assign will ABSOLUTELY happen before anything else. The best way to do this is to perform all such assigns first:
var table: record
a: integer;
b: integer;
c: char
end;
begin
table.a := 1;
table.b := 45;
table.c := 'x';
...
Typically this should only be done at the program block level. Doing this gives the compiler the maximum chance to perform this optimization.
You may be able to do a better job of dynamic variable recycling than the standard "dispose" routine. getting and putting a lot of variable length blocks tends to create "fragmentation" difficulties. You can illustrate this problem fairly simply. If you are using two different data types in dynamic storage, one of 100 bytes length, and the other 200 bytes in length, and these are created and disposed at random, the standard new and dispose procedures may be taking back the 200 byte blocks and breaking them down into 100 byte blocks to satisfy the calls for that size of variable. The result is that eventually, the call to new for a 200 byte block may fail, even though plenty of storage still exists, because it is all broken into 100 byte blocks.
I have found that in many cases, it is better to hold on to unused blocks, and recycle them yourself:
program mine(input, output);
type blkptr = ^block;
block = record
next: blkptr;
array [1..100] of byte
end;
var freblk; { the free block list }
{ get a new block }
procedure getblk(var p: blkptr); { returns the block }
begin
if freblk <> nil then begin { recycle existing block }
p := freblk; { index the top block }
freblk := freblk^.next { remove from list }
else new(p) { otherwise create a new one }
end;
{ put an unused block }
procedure putblk(p: blkptr); { block to dispose of }
begin
p := freblk; { insert to free list }
freblk := p
end;
begin
freblk := nil; { clear free block list }
...
This system reduces fragmentation, because blocks are reserved for a particular use, and not broken down into smaller parts. It tends to be storage efficient, because programs typically do the same sort of work over and over again. That is, if you needed to get N blocks of X type, this means that X type blocks will be used a lot in the run of the program (although obviously there are programs that break that rule).
I add "meters" to count the number of blocks going in and out of the free list to tell me how the system is performing in real life.
If you require variable length arrays in standard Pascal, what do you do ? For example, if you are going to create a text editor, and want to store variable length lines, but you don't want to place a limit on the length of a line. You need a variable length string type to contain each line. The answer is that you can create variable length arrays yourself ! The secret is to chain dynamically allocated arrays together to make a larger array:
type
blkptr = ^block;
block = record
next: blkptr;
data: packed array [1..10] of char
end;
var p: blkptr;
{ allocate string in terms of blocks }
procedure alcblk(var p: blkptr; { returns string allocated }
l: integer); { length of string }
var t: blkptr;
begin
p := nil; { clear result list }
while l > 0 do begin { allocate blocks }
new(t); { get new block }
t^.next := p; { link into list }
p := t;
l := l-10 { count that block }
end
end;
{ get character from string }
function getblk(p: blkptr; { string to fetch character from }
i: integer); { index to get character from }
: char; { returns character from index }
begin
while i > 10 do
begin p := p^.next; i := i-10 end; { index proper block }
getblk := p^.data[i] { return resulting character }
end;
{ place string character }
procedure putblk(p: blkptr; { string to put character to }
i: integer; { index to put character to }
c: char); { character to place }
begin
while i > 10 do
begin p := p^.next; i := i-10 end; { index proper block }
p^.data[i] := c { place character }
end;
This technique could be used on any array type. Although it looks pretty horrible, the system can keep any number of any length of string, such as an advanced editor would do, and the fact that all strings are broken down into fixed length "quanta" keeps storage fragmentation to a minimum, or even eliminates it entirely (and important attribute for an editor). The processing cost of the system can be lessened by pulling the variable length data to/from a large buffer, and working on it there. But of course, this would limit the length of data you can work on.
If you have to perform booleans on a standard compiler that does not have boolean bitwise operators on integers, you can create them. First, if you know that the same bits are not set in two words, then you can just add them to get "or" functionality:
i := i+64; { set bit 7 }
You can also mask off bits in an integer using "div" and "mod":
i := i mod 256; { is equivalent to i and $ff }
i := i div 256*256; { is equivalent to i and not $ff }
For generalized boolean operations you can use:
program test(output);
function bor(a, b: integer): integer;
var i, r, p: integer;
begin
r := 0; { clear result }
p := 1; { set 1st power }
i := maxint; { set maximium positive number }
while i <> 0 do begin
if odd(a) or odd(b) then r := r+p; { add in power }
a := a div 2; { set next bits of operands }
b := b div 2;
i := i div 2; { count bits }
if i > 0 then p := p*2; { find next power }
end;
bor := r { return result }
end;
function band(a, b: integer): integer;
var i, r, p: integer;
begin
r := 0; { clear result }
p := 1; { set 1st power }
i := maxint; { set maximium positive number }
while i <> 0 do begin
if odd(a) and odd(b) then r := r+p; { add in power }
a := a div 2; { set next bits of operands }
b := b div 2;
i := i div 2; { count bits }
if i > 0 then p := p*2; { find next power }
end;
band := r { return result }
end;
function bxor(a, b: integer): integer;
var i, r, p: integer;
begin
r := 0; { clear result }
p := 1; { set 1st power }
i := maxint; { set maximium positive number }
while i <> 0 do begin
if odd(a) <> odd(b) then r := r+p; { add in power }
a := a div 2; { set next bits of operands }
b := b div 2;
i := i div 2; { count bits }
if i > 0 then p := p*2; { find next power }
end;
bxor := r { return result }
end;
begin
writeln('or: ', bor(67, 43));
writeln('and: ', band(42, 83));
writeln('xor: ', bxor(62, 12))
end.
With the result:
or: 107
and: 2
xor: 50
The routines only work on positive integers, but and, or, and xor on negative integers is kinda questionable in any case (what does it mean?).
Often you must deal with files that randomly mix different types of data that are not amenable to declaration as a file of records. If you read and write large integers using only standard constructs (and not "type changing" using variant records), you may find it easier to use a format known as "signed magnitude" than to try to write the number using the 2's complement format used by the CPU. In signed magnitude, the sign bit is determined and written out separately from the value of the integer:
{ write integer to byte file }
procedure wrtint(var f: bytfil; { file to write to }
i: integer); { integer to write }
var s: byte; { sign holder }
begin
{ remove sign and save }
if i < 0 then s := 128 else s := 0;
i := abs(i); { remove sign }
write(f, i div 16777216+s); { output high byte with sign }
write(f, i div 65536 mod 256); { output high middle }
write(f, i div 256 mod 256); { output low middle }
write(f, i mod 256) { output low byte }
end;
{ read integer from byte file }
procedure rdint(var f: bytfil; { file to read from }
var i: integer); { integer to read }
var s: boolean; { sign holder }
b: byte;
begin
s := false; { set no sign }
read(f, b); { get high byte }
if b >= 128 then begin s := true; b := b-128 end;
i := b*16777216;
read(f, b); { get high middle }
i := i+b*65536;
read(f, b); { get low middle }
i := i+b*256;
read(f, b); { get low byte }
i := i+b;
if s then i := -i { add back sign }
end;
For 32 bit integers written in "big endian" format (high order bytes first). Note that the sign is written and read as bit 32, the same place as the normal sign. Note that the only value you lose this way is $80000000, which is an invalid value under standard Pascal in any case.
Since random numbers where not in the Pascal standard, a routine to generate them is a common request. This is the classical routine:
var rndseq: integer; { global variable }
function rand: real;
const a = 16807;
m = 2147483647;
var gamma: integer;
begin
gamma := a*(rndseq mod (m div a))-(m mod a)*(rndseq div (m div a));
if gamma > 0 then rndseq := gamma else rndseq := gamma+m;
rand := rndseq*(1.0/m)
end;
In the main program, rndseq gets initialized to 314159. The result of each call to rand is a random number in the range 0 < N <= 1 (random number generators cannot give 0 as a number, since that terminates the algorithm). This program is a recoding of the routine by N. Wirth (Sans authorization) from the book "programming in Oberon", and uses the multiplicative linear congruential algorithm. The big trick with this method, which as you can see is pretty simple, is choosing the right magic numbers, and Wirth says: "there will be over 2 billion random numbers that passed stringent statistical tests".
If you have a command line Pascal compiler only, with NO graphical operations whatever, can you perform graphical operations?
Yes ! The following short program reads, processes and writes a Windows .bmp file (black and white only):
{
BMP file processor, reduces the number of colors
}
program reduce(source, dest, output, command);
type byte = 0..255;
type bmphead = record { bitmap header }
fsize: integer; { size of file }
off: integer; { offset of header }
size: integer; { size of header }
width: integer; { image width in pixels }
height: integer; { image height in pixels }
planes: integer; { image planes }
bitcnt: integer; { bit per pixel }
comprs: integer; { compression type }
sizimg: integer; { size in bytes of image }
xpelsm: integer; { horizontal resolution }
ypelsm: integer; { vertical resolution }
clrusd: integer; { number of colors used }
clrimp: integer; { number of colors important }
end;
var source: file of byte;
dest: file of byte;
bh: bmphead;
store: array [1..2000, 1..2000] of byte; { picture storage }
reduce: integer;
x, y: integer;
procedure getint(var i: integer);
var b: byte;
begin
read(source, b);
i := b;
read(source, b);
i := i+b*256;
read(source, b);
i := i+b*65536;
read(source, b);
i := i+b*16777216
end;
procedure getwrd(var i: integer);
var b: byte;
begin
read(source, b);
i := b;
read(source, b);
i := i+b*256
end;
procedure wrtint(i: integer);
begin
write(dest, i and $ff);
write(dest, i div 256 and $ff);
write(dest, i div 65536 and $ff);
write(dest, i div 16777216)
end;
procedure wrtwrd(i: integer);
begin
write(dest, i and $ff);
write(dest, i div 256 and $ff)
end;
{ load picture }
procedure loadpic;
var r: integer;
b: byte;
x, y, i: integer;
red: byte; { colors }
green: byte;
blue: byte;
reserved: byte;
begin
read(source, b);
if b <> ord('B') then begin writeln('*** bad signature'); halt end;
read(source, b);
if b <> ord('M') then begin writeln('*** bad signature'); halt end;
writeln;
getint(bh.fsize);
getwrd(r);
getwrd(r);
getint(bh.off);
getint(bh.size);
getint(bh.width);
getint(bh.height);
getwrd(bh.planes);
getwrd(bh.bitcnt);
getint(bh.comprs);
getint(bh.sizimg);
getint(bh.xpelsm);
getint(bh.ypelsm);
getint(bh.clrusd);
getint(bh.clrimp);
if bh.comprs <> 0 then begin writeln('*** Cannot process compressed'); halt end;
if bh.clrusd <> 256 then begin writeln('*** Must be 256 colors'); halt end;
if bh.width > 640 then begin writeln('*** Picture too wide'); halt end;
if bh.height > 480 then begin writeln('*** Picture too high'); halt end;
if bh.planes <> 1 then begin writeln('*** Must be 1 plane'); halt end;
if bh.bitcnt <> 8 then begin writeln('*** Must be 8 bits per pixel'); halt end;
for i := 0 to bh.clrusd-1 do begin { list color map }
read(source, blue);
read(source, green);
read(source, red);
read(source, reserved);
if (blue <> i) or (red <> i) or (green <> i) then
begin writeln('*** Bad color map'); halt end
end;
writeln('Loading ', bh.width:1, ' by ', bh.height:1);
{ read in picture }
for y := 1 to bh.height do begin { read rows }
for x := 1 to bh.width do { read pixels }
read(source, store[y, x]); { read that in }
{ read and discard row padding }
for i := 1 to 4-bh.width mod 4 do read(source, b)
end;
writeln('Picture loaded')
end;
{ write picture }
procedure strpic;
var x, y, i: integer;
begin
writeln('Writing picture');
write(dest, ord('B'));
write(dest, ord('M'));
wrtint(bh.fsize);
wrtwrd(0);
wrtwrd(0);
wrtint(bh.off);
wrtint(bh.size);
wrtint(bh.width);
wrtint(bh.height);
wrtwrd(bh.planes);
wrtwrd(bh.bitcnt);
wrtint(bh.comprs);
wrtint(bh.sizimg);
wrtint(bh.xpelsm);
wrtint(bh.ypelsm);
wrtint(bh.clrusd);
wrtint(bh.clrimp);
{ write color map }
for i := 0 to 255 do begin
write(dest, i);
write(dest, i);
write(dest, i);
write(dest, 0)
end;
{ write bitmap }
for y := 1 to bh.height do begin
for x := 1 to bh.width do { write pixels }
write(dest, store[y, x]);
{ write row padding }
for i := 1 to 4-bh.width mod 4 do write(dest, 0)
end;
writeln('Picture written')
end;
begin
reset(source);
rewrite(dest);
read(command, reduce);
loadpic;
for y := 1 to bh.height do
for x := 1 to bh.width do { read pixels }
store[y, x] := store[y, x] div reduce*reduce;
strpic;
writeln('Function complete')
end.
The program processes a picture as an input and an output file. Using any simple graphics display program, including Windows "paint", you can then look at the result of the operation.
No, but it is possible on most compilers in any case:
type intptr = ^integer;
convert = record
case boolean of
false: (i: integer);
true: (p: intptr)
{ end }
end;
function int2ptr(i: integer): intptr;
var cr: convert;
begin
cr.i := i; { place integer }
int2ptr := cr.p { return pointer }
end;
function ptr2int(p: intptr): integer;
var cr: convert;
begin
cr.p := p; { place pointer }
ptr2int := cr.i { return integer }
end;
Converts integers to pointers and vice versa. The effect of placing a value in an untagged variant are not defined in the standard, which means you must know what you are doing, and the resulting code is probably not portable.
So Pascal HAS a defacto type conversion method, but it is far more painful to use than, say, C. In this authors' humble opinion, its a good thing that it is painful. Its bad coding practice, and should be avoided.
Step back for a second. If you need to put a lot of different data types in a file and want to do that to a byte file, you are probably applying thinking used in other languages to Pascal. There is nothing to prevent you from doing that entirely within standard Pascal:
type r1 = record
x: integer;
U: char
end;
r2 = record
O: real;
S: integer
end;
out = (rtr1, rtr2);
r3 = record
case out of
rtr1: (r1d: r1);
rtr2: (r2d: r2)
{ end }
end;
soufflé = file of r3;
Now "soufflé" can be used to output any of these record types. You are letting Pascal format the data for you, but the result is the same. If you REALLY want to write all data a byte at a time, use type conversion (above), and output the bytes. For example, to output a real value (floating point:
type bytefile = file of 0..255;
procedure writereal(var f: bytefile; r: real);
var rc: record
case boolean of
false: (r: real);
true: (ra: array [1..8] of 0..255)
{ end }
end;
i: 1..8;
begin
rc.r := r; { place real in converter }
for i := 1 to 8 do write(f, rc.ra[i])
end;
Note that this relies on reals being 8 bytes long, and that the machine is byte addressable. The exact format written will be system dependent.
To read and write data files, and keep maximum portability for your programs, you should look to do these types of I/O, in order of desirability:
1. Use standard Pascal record structured I/O.
2. Use standard methods of conversion to get byte values, then read or write those values (see "how to read and write integers to a byte file", "how to find an enumerated value from an integer" above).
3. Use type conversion.
The full evaluation problem goes like this:
if (p <> nil) and (p^.stuff = 1) then ....
This is technically wrong, because all parts of an expression may be evaluated, so if pointer p is nil, the access p^.stuff will fail even though we apparently check for that.
Although the evaluation problem is often quoted as a major failing of Pascal, it is really a non-issue in practical use. A simple rewrite of the condition cures it:
if p <> nil then if p^.stuff = 1 then ...
A more interesting problem is "skip blanks" in an array:
while (i <= maxlen) and (a[i] = ' ') then i := i+1;
Will fail because if i > maxlen (the length of the array), the access a[i] may still take place, and a fault results. Here is the reformulation:
t := a[i]; { get next character }
while (i <= maxlen) and (t = ' ') then begin
i := i+1;
if i < maxlen then t := a[i] else t := '*'
end;
A less complex formulation is to simply not use the last location of the array for data, ie., use the last location as a "sentinel":
while (i < maxlen) and (a[i] = ' ') then i := i+1;
Will be fine as long as no data is placed in the last element.
Standard Pascal has a series of structured statements, if-then-else, while-do, repeat-until, for-do. A common feature of other languages are statements like "break" and "continue" (which come from C). Unfortunately they are also common extentions to Pascal compilers. I say unfortunately, because break and continue are really nothing but a disguised "goto". The whole idea of structured programming is that the flow of control in programs can be stated with very few constructs. In fact, the classical structured program statements are while-do and if-then-else. repeat-until and for-do are really just special cases of the while-do loop.
The problem with break and continue is that they break the logical structure of the loop, in an arbitrary way similar to a goto. A break or continue can be executed at any statement depth and "bail out" to the outside loop. So what are the equivalents to common uses of break an continue in ISO 7185 Pascal?
while x do begin if y then continue; ... if z then continue; ... end; while x do begin if not y then begin ... if not z then begin ... end end end;
while x do begin if y then break; ... if z then break; ... end; while x and not q do begin if y then q := true else begin ... if z then q := true else begin ... end end end;
Of course, this dosen't account for deep nested breaks or continues, but these kinds of statements look more and more like a defacto "goto".
Structured programming is a tequinque you have to live with for a while to get right. I converted to structured programming from "spagetti" assembly language programming some time ago, and it took a while of using flags before I saw that the majority of code just needs to be arranged and thought out properly to be structured. Now, it's second nature. I do a lot of C coding as well, and I never feel the temptation to use break or continue even in that language. It's just a crutch.
I see a lot of folks frustrated with the read/readln statements and numbers. If you perform a read/readln and there is nothing but spaces in the input, Pascal will cross multiple lines until it sees a number, leading to users hitting return again and again wondering why the program seems to be "hung". If there is a number, great. If not, it's an error, and your program gets terminated.
This confusion comes from the fact that buffered input I/O, a standard feature of Pascal, has been largely ignored, and has even been removed from the language in the UCSD/Apple/Delphi dialects of the language. Buffered input is a cool and natural way to solve these issues, and yes, it even works on interactive files like the console, thanks to "lazy I/O".
Buffered input means that you can always "look ahead" to see the next character coming in a file:
var f: text
...
if f^ in ['0'..'9'] then writeln('Number present');
You can skip blanks in the input to find a number:
while (f^ = ' ') and not eof(f) do get(f);
Now, let find a number, and reject multiple lines, then read the number if present: For extra credit, we will even ensure that no other garbage exists on the input line after the number.
produre getnum(var f: text; var n: integer);
begin
n := 0;
while (f^ = ' ') and not eoln(f) do get(f);
if not (f^ in ['0'..'9', '-', '+']) writeln('*** No number was found')
else begin
read(f, n);
while (f^ = ' ') and not eoln(f) do get(f);
if not eoln(f) then writeln('*** Invalid input')
end
end;
The skip space we have used in getnum only skips within a single line. It takes advantage of the fact that ISO 7185 is defined such that all lines are properly formed, and we won't see an eof without an eoln before it. The read of n will work even if there is garbage after the number, because read/readln stops when it runs out of valid digits. So the line:
+567bark
Will leave the next input position at the "b" following the number, and we will then generate an error.
The last issue left is that the line:
-
Will generate an error, because what apparently looks like a number, the "-", isn't followed by valid digits. Lets reformulate getnum to account for this:
produre getnum(var f: text; var n: integer);
var sgn: integer;
begin
n := 0;
sgn := 1;
while (f^ = ' ') and not eoln(f) do get(f);
if not (f^ in ['0'..'9', '-', '+']) writeln('*** No number was found')
else begin
if f^ = '+' then get(f)
else if f^ = '-' then begin get(f); sgn := -1 end;
if not (f^ in ['0'..'9', '-', '+']) writeln('*** No number was found')
else begin
read(f, n);
while (f^ = ' ') and not eoln(f) do get(f);
if not eoln(f) then writeln('*** Invalid input')
end
end;
n := n*sgn
end;
Just following the standard is not enough to create a useable compiler. Obviously freedom from limits (except the limit of available memory) is a good attribute of a compiler, as well as producing the best code possible. Other points to look for:
1. Should be able to represent "set of char" without problem. This is really a must. For a while, it was under consideration to add this requirement to the standard. It wasn't, but most character handling programs I have seen rely on being able to use character sets, so you might as well consider it part of the standard.
2. Represents 8 bit characters. I have found it best if the compiler leaves it entirely up to you what is done with the 8th bit (of ASCII or ISO character sets). This allows you do either deal with parity, or manipulate 256 valued extended character sets (like IBM ASCII).
3. "file of char" is not the same as "text". This is a somewhat obscure point. Normal "text" files are "filtered" by Pascal I/O. Line endings are made regular, and eolns are converted to spaces. But there may be times that you want to talk in terms of the exact characters themselves, read and write carriage returns and line feeds directly, etc. For example, when reading direct from the computer console, you may want to see directly if the user hits the "return key".
There is no requirement in the standard that "file of char" do the same filtering that "text" does, and better compilers consider "file of char" a clue to completely get out of the way and pass raw characters to and from the file. Also, it is common for Pascal systems to buffer up whole lines when reading from the console (see below). File of char will allow this buffering to be bypassed.
4. Input from the computer console is buffered. If you have a program like:
program readit(input, output);
var i: integer;
begin
writeln('Input the number: ');
readln(i);
writeln('The number was: ', i:1)
end;
If the console is read directly, and the user makes a mistake on entry, it will not be possible to back up and correct typing. If the line is buffered, the user can back up, correct the error, and continue without the simple program above needing to do anything about it. Without this capability, you would have to write such "line editing" features in yourself.
A REALLY GOOD Pascal system would implement a complete set of line editing features, such as back up, insert characters, etc.
5. Ability to represent a file of bytes. One thing that really amazes me about some compilers I have seen is an inability to read or write files of byte value:
type byte = 0..255; { a predefined type on many compilers }
bytfil = packed file of byte;
If your compiler decides that bytes are integers, at whatever the size of integers are on your machine (16, 32 or 64 bits), it may just read or write integer size values to the file ! This forces you to break apart the bytes from an integer yourself, which is not only tedious, but creates a nonportable program.
6. Does something with the program parameters. Somewhere, at some time, it became standard to just ignore the program parameters. This is a real shame, because when implemented, they are really useful for creating quick, short programs:
program copy(source, destination);
var c: char;
begin
while not eof(source) do begin
if eoln(source) then begin
readln(source);
writeln(destination)
end else begin
read(source, c);
write(destination, c)
end
end
end.
Now if the compiler ignores the program header parameters, this program does nothing, and will probably terminate with an error. But the compiler can also automatically attach program parameters other than the standard "input" or "output" to command line parameters, like:
> copy myfile.txt thatfile.txt
So the compiler opens source to "myfile.txt", and destination to "thatfile.txt". The result is a program that is completely standard, and yet performs named file manipulation. Further, this method is much simpler than using the "open by name" extensions present in most Pascals, and allows you to create simple example programs faster.
7. Ability to tolerate control characters in source. If the compiler ignores control characters in the source, such as tabs and form feeds (treats them as spaces), you will have the freedom to format your source so that it can be directly printed.
8. Don't have "extended" modes that break the standard rules. The Pascal standards basically require that the compiler have a "switch" (command line option) that causes the compiler to accept only standard Pascal, and rejects any nonstandard constructs. Because some compilers were originally nonstandard, and were brought into compliance with the standard after the fact, it is quite possible that they may not accept standard programs with this switch off ! This is truly unfortunate, since it forces you to write nonstandard Pascal just to take advantage of Extensions to the language provided by your compiler.
9. Accept Pascal strings for extended file naming procedures. A common extension to Pascal is a Basic string construct (as opposed to a Pascal string, which is just an array of characters). Unfortunately, it has become common to REQUIRE that such strings be used with extended file "open by name" procedures. A good compiler should accept standard Pascal strings as arguments to such procedures, which allows you to use the string types you choose. These procedures should also ignore trailing spaces in such strings, which allows use of the typical Pascal "space padded string".
10. Implements "lazy I/O". Pascal as originally specified assumes that all files are batch files (ie., not "interactive", or connected to a console). This creates problems, say, reading from the console keyboard, because Pascal assumes that the first character in a text file is always available. Lazy I/O will only request the first character from a file when it is actually used, and is completely compatible with the standard.
11. Can provide "strict packing". Strict packing means that if a record is marked as "packed":
var r: packed record
a: boolean;
b: integer;
c: char
end;
Then, for example, "a" will only occupy a single bit, and all fields will be packed into as few bits as possible. This has implications beyond simple storage savings, because you can use strict packing to emulate ANY data structure from any language. for example, if you receive the date as packed into a single word:
-------------------------------------------
| year (0-99) | month (1-12) | day (1-31) |
-------------------------------------------
7 bits 4 bits 5 bits
This exactly fits in a single 16 bit word ! Then, this can be declared as a packed record:
var date: packed record
year: 0..99;
month: 1..12;
day: 1..31
end;
And no "format conversion" is required. Note that if the alignment of packed records to the data structure desired is not perfect, you can insert "shims" in the form of single bits (boolean values).
Most compilers include extensions to the basic language, the most popular (and necessary) extensions being manipulation of external files by name, and ability to separate program parts into modular form. In fact, a standard of sorts was created by the popular UCSD Pascal system for these two items. This is a list of popular extensions, and the form they usually take. Note that extensions that are part of the extended Pascal standard are discussed in the extended standard section.
1. Specification of hex constants. Allows you to directly specify hex numbers in the source:
var a: integer;
...
a := $56;
"$" as the leading character seems to be the most common. Some compilers also allow binary radix (base 2) and octal radix (base 8).
2. Integer bit boolean operations. Many or even most compilers allow you to use the "and" and "or" operators on integers:
var a, b: integer;
...
a := a and b;
The result is a bitwise "and" of the two integers. Note that the results when one or more of the operands is signed varies from compiler to compiler, and use of these operators with signed integers should be avoided. Many compilers also include an "xor" operator.
3. File open and close by name. This allows you to open an external file by name. Many compilers use the Borland plan:
var f: text;
...
assign(f, 'myfile.txt');
reset(f);
...
In this method, an ASCII name can be "bonded" to the file by the procedure "assign". If no assign is performed on a file, it is opened as a "temporary" file (ie., the system just coins a name for the file, and deletes it when complete) upon "reset" or "rewrite". This allows the system to be completely backward compatible with standard Pascal. If your system also has a "close" function:
close(f)
You should use it. Closing open files releases system space (used to keep track of files), and allows a series of files to be opened under one file variable.
4. Modular compilation. Again the most common, UCSD adds an extra declaration section before "label":
program junk(input, output);
uses trash, mylib;
label 1, 2;
...
This tells the compiler to include all the (public) constants, types and blocks in the files specified within uses list to the present program. The exact details of the format needed to compile the module (called a "unit" in UCSD) vary quite a bit from compiler to compiler.
5. Include statements. Most compilers allow the appearance of an "include" statement or marker in the source, that specifies that another file is to be included in line. Most common is UCSDs "control comment":
{$I file.pas}
Which would include the file "file.pas" inline, replacing the entire comment.
5. Flexible declaration order. Many or most compilers allow the declarations to occur in any order, and for declaration sections to occur multiple times:
program test;
var this, that: integer;
const x = 10;
var mystring: packed array [1..x] of char;
...
The reason for this is that when using modular compilation or include statements, each program section must have its own set of declarations. Note that the rule that objects must be declared before use is still in effect even though the declarations can appear in any order.
6. Strings. A full Basic string type is included in Pascal, which allows constructs as:
var s: string;
s := 'hi there'; { any length can be assigned }
s := s+' george'; { concatenation }
writeln('Length is: ', len(s)); { find string length }
...
This also was in UCSD Pascal.
7. "_" allowed in identifiers. This convention comes (mainly) from C, where identifiers like:
my_label
Are common. Since Pascal must live in systems where these names are common, this is allowed in most implementations.
8. "goto" labels as normal identifiers. The appearance of goto labels as integers might be considered a sort of arcane plot to punish people for using "goto"s. Many or most implementations allow goto labels to appear in the same form as identifiers:
label destination;
...
destination:
...
goto destination;
Please see our ISO 7185 compilers page:
The PUG or Pascal Users Group was formed shortly after Pascal was introduced by N. Wirth as a collection of letters that were printed and circulated to a mailing list. It started publishing in January 1974, and ended in November, 1983.
The PUG newsletters were important because they cronicle, in an indisputable way, the early history of Pascal.