I'm trying to understand a somewhat magical behavior seen in Free Pascal, compiled in Turbo mode (specifically fpc -Mtp -vw
) under Linux. The code is from Jack Crenshaw's "Let's Build a Compiler", Part IV, specifically the last version with the Input
and Output
procedures, found at http://www.pp4s.co.uk/main/tu-trans-comp-jc-04.html.
What I'm finding "magical" is the Read(Table[GetName]);
line in the Input
procedure. According to the FP docs, "Read
reads one or more values from a file F, and stores the result in V1, V2, etc.; If no file F is specified, then standard input is read."
In the Read
that is in the GetChar
procedure, it appears to behave much like C's getchar()
, i.e., retrieve the next character from the stdin buffer. In the Input
call it instead appears to behave like a sophisticated scanf()
that automatically skips over white space and converts multi-digit numbers to Integers, and obviating the need for the GetNum
function, which doesn't get invoked if you use for example, ?i 345
, but does get called if you try i=345
, and if you leave a space on either side of the =
results in a parse error.
Is there some other documentation that confirms the apparent versatility of Free, Turbo or even generic Pascal's Read
?
2 Answers
The Pascal Read,write routines are magic. IOW it is not a real function that exists in some library, but the compiler generates a series of calls into the runtime. The family consists out of read,readln,write and writeln, as well as the the extended Pascal readstr and writestr which are "from string" versions of read and write.
The calls vary depending on the type of the arguments, and if they have extra parameters (e.g. write takes :x formatting value for integers and :x:y for reals):
writeln(x:10:5);
In Free Pascal, contrary to Turbo Pascal and Delphi, some RTTI like functionality is also exposed, e.g. writeln() a variable of type enum, will print the enum's value as identifer (string).
Errorhandling and formatting are somewhat limited, so their usage varies with the complexity of the problem
As to your observations, your getchar like call probably read a variable of type char, and the typing for the other function is different.
I found a little information about Pascal's Read procedure, which indicates that unlike C's getchar()
, it will automatically fill out various types properly. But that isn't the interesting stuff you're asking about.
Jack's compiler has a GetChar()
procedure that fills in a Look
variable; this is a single-character lookahead, which is fairly commonly employed in parsers though these days usually hidden under many layers of wrapping paper. But all of Jack's methods are apprised of this variable, which is why they examine it first and then call GetChar()
again when they end, for instance:
{ Get a number }
function GetNum : integer;
begin
if not IsDigit(Look) then
Expected('Integer');
GetNum := Ord(Look) - Ord('0');
GetChar;
end;
Remember that in Pascal, assigning to the function name is how you return a value, so what he's doing here is saying "is my lookahead character a digit? if so, decode it and return it, and then read a new lookahead character, otherwise abort." Later in this chapter, he's going to expand this definition to handle multiple digits. Jack actually handled whitespace in the previous chapter, look for SkipWhite in there.