// # 2
type
TTest2 = class
private
IInt: Integer;
CCls: TTest1; // : compile error (Undeclared identifier)
end;
TTest1 = class
private
SStr: String;
end;
//
as far as i know,
lonnnnnng ago, there was 1-pass compiler.
this compiler did not allow forward reference.
lonnng ago, there was 2-pass compiler.
this compiler allowed forward reference.
from 'lonnnnnng ago' to 'the present', there was/is the object pascal.
what reason that the object pascal did/do not allow forward reference?
so far as i know, a compiler did not allow forward reference in the past.
your opinion?
Regards
--
Red Orchid
It does allow it, provided it's in the same type block. Look up "Forward
Declarations" in the Delphi help.
Cheers,
Ignacio
Silly me, I read your question wrong.
Delphi doesn't allow forward references so that it can compile in one pass.
The reason for this is speed, plain and simple. It could allow it by doing
two passes, but then it wouldn't be the fastest compiler on the market ;)
Cheers,
Ignacio
delphi's help says as followings:
//
type
TFigure = class; // forward declaration
TDrawing = class
Figure: TFigure;
...
end;
TFigure = class // defining declaration
Drawing: TDrawing;
...
end;
//
i think it is not forward *reference*.
a programmer should write 'forward declaration' in delphi.
in the codes mentioned above, it is 'TFigure = class;'.
is it desirable that a compiler do it?
so far as i know,
the modern language conception don't require 'forward declaration'.
--
Red Orchid
Actually, multipass was the earlier compiler architecture. Each phase of
the compile process was done by a different program. Life was simple, but
compiling took forever because the symbol information kept going back and
forth between disk and memory.
Single pass compilers didn't appear until much later, and even then only for
languages whose structure allows top-down compilation.
-Danny
There is a little more to it than that as you maybe aware. Multi Pass
compilers are often implemented today, even where the language definition
does not require it:
a) To allow advanced optimisation techniques which require data flow
analysis
b) To enhance portability. This can be done by separating the front-end and
the back-end into language-dependent and machine-dependent parts.
Refer to "Compiler Construction - The Art of Niklaus Wirth" by Hanspeter
Mossenbock for more detail.
http://www.informatik.uni-trier.de/~ley/db/conf/birthday/wirth2000.html
Chris Burrows
CFB Software
http://www.cfbsoftware.com
If you want to get technical, the Delphi compiler is a multipass
architecture as well. The compiler builds an intermediate representation
node tree while parsing a procedure body. When it reaches the end of the
procedure body, it makes multiple optimization passes over the node tree,
then emits machine code into unit storage. The node tree is destroyed and
the parser scans for the next procedure body. Smart linking the symbols
into a final executable is also a multi-pass process.
The part that matters to the original question is the parser, the thing that
gives symbolic meaning to identifiers in context. That is single-pass in
Delphi and in most C and C++ compilers. Symbol recognition in C# and Java
cannot begin until the entire source file and all dependencies have been
scanned completely.
-Danny
My opinion is that in a single pass compiler, the compiler must know what
something is before it can accept it.
ex.
TMyRec = record
int: integer; // it knows what this is
SubRec: TSubRec; // but what is this?
end;
SubRec could be of any size. But old Pascal did allow this:
PListNode = ^TListNode;
TListNode = record
Data: ....;
Next: PListNode;
end;
Because PListNode was declared as a pointer, when the compiler reached Next
it knew that PListNode was a pointer, of some unkown sort. A pointer is a
pointer, so it could deal with this.
Same thing with:
TMyClass = class;
TOther = class
fMyClass: TMyClass;
end;
TMyClass = class
...
end;
When the compiler reaches fMyClass, it knows that fMyClass is a pointer to
an object. That's all it really needs to know at that stage.
It seems to me that Delphi/Kylix could well have one pass parsers (even
though they may actually use multipass, I don't know), where parsing is the
first of several stages in a compilation.
Even when TListNode could be of a known type already, it cannot be used
before the end to the current type block, as the type can be redefined.
Following program can, and should be able to compile in Pascal.
Program Test1;
type
tP = ^ Integer;
Integer=String;
var
P: tP;
begin
P:=....; // Initialize P to something
P^:='Correct';
// Cannot compile this! P^:=1;
end.
--
Christen Fihl
http://HSPascal.Fihl.net/
Well... I tried the following now, and it worked fine. This is what we used
to do at uni. (I think before Turbo Pascal was released.)
type
PListNode = ^TListNode;
TListNode = record
Data: string;
Next: PListNode;
end;
> type
> tP = ^ Integer;
> Integer=String;
Eughh!!! =o ;)
I see your point. But normally one does not try to confuse the compiler.
Delphi still allows that.
sm
> Eughh!!! =o ;)
> I see your point. But normally one does not try to confuse the compiler.
The compiler would probably disagree with that claim. :P
-Danny
Brad.
> so far as i know,
> the modern language conception don't require 'forward declaration'.
I don't think one can characterize the fact that Delphi requires declaration
of a symbol before its use as an old-fashioned idea. The concept is, IMO,
integral to the concept of strong type checking and contributes to better
source layout.
>> so far as i know,
>> the modern language conception don't require 'forward declaration'.
>
>I don't think one can characterize the fact that Delphi requires declaration
>of a symbol before its use as an old-fashioned idea. The concept is, IMO,
>integral to the concept of strong type checking and contributes to better
>source layout.
Except when you are faced with a circular situation. I've
been forced to work around something that basically looks like:
Object A holds the various data pieces that comprise a job.
It also has the various forms that the user might use to create and
edit this job. Many of these editors are basically co-equal, there's
no boss. Yet they have to be able to transfer control to other
editors, or create ones that do not currently exist.
The existing forward system is fine for small stuff but this
comprises many modules. I ended up splitting A into an abstract
dispatcher (goto formXXXX), defining the forms all with no knowledge
of each other, and then finishing off A at the end.