Daniël Mantione schrieb:
In essence your project was good. There are a few remarks to be made:
* There are more theories (religions?) about how maintainable programs
should be made.
Right, paradigms are somewhat religious. But as with every religion, I
only believe what matches my own experience.
* The Pascal language implemented by FPC is a mixed procedural/OOP
language. Procedural programming has its strengths and FPC makes use of
the potential to mix procedural and OOP.
Experience tells me that projects have reached a dead end, when they
contain enough unmanageable parts which nobody can explain, and which
nobody dares to touch any more. Such parts typically reside in
procedural code, rarely in OOP code.
* Global variables cause problems with parallelization, but that does
not mean the choice for a global variable is a bad idea. Variables like
current_module is used so extensively that they are prime candidates to
be global. In my opinion you have been following the OOP bible a bit too
strict here.
In the concrete case of FPC, the bad use of global variables (for
context sensitive purposes) has nothing to do with religion.
Current_module is one of the exceptions, which I consider a state
variable, eligible for a threadvar. Further variables, which have to be
changed together with current_module, are only used for optimizations,
and can be replaced by tmodule members.
My (very raw) measurements didn't indicate any noticeable speed penalty,
when further variables (even current_scanner) are replaced by indirect
references to members of tmodule. Like with source files, I assume that
nowadays existing caches (in both hard- and software) invalidate old
rules, that the use of shadow and buffer variables can speed up
execution a lot. This can be different for other (non-x86) machines,
without such sophisticated caching, where the execution time still is
predictable from a static analysis of the code (by counting fixed clock
cycles per instruction).
* OOP was your goal. Your goal should have been parallelization, OOP
should have been your tool. There are more tools, like threadvars.
Threadvars have the same problematic properties like global variables:
the must be initialized and switched properly. Every such variable
affects the complexity of an application, where it adds a very new
dimension, not only a percentage. My experience and design principles
are based on automatons and states, so that OOP is not a must, but fits
nicely into such a model.
It has been said meny times in this discussion: In a compiler there are
dependencies between a lot of datastrcutures. Handling this effectively
is a challenge.
Yes and no. The number and polymorphism of data structures is not a
problem per se. But...
FPC's approach in many places is to put abstract
interdependent objects in a single unit, so these objects can use each
other wherever they want. We then add functionality in other units that
derive from the abstract units. Tsym, Tdef, Tsymtable are in a single
unit. All kinds of symbols, definitions, and symbtables have their own
unit.
...the strictly hierarchical OPL unit dependency system requires much
coding discipline, and also requires hacks for certain constellations
<sigh>.
I consider this design a strong one, the problem of cyclic dependencies
in the compiler is well controllable. It helps maintenance: I know many
C++ projects where all .cpp files over time started to include all .h
files. In FPC, if you notice you can't access current_module from the
current unit, you know you are doing something wrong.
ACK.
You seemed to have trouble with this model, rather than embrace it and
use it to your advantage, you started to break things apart and change
the dependencies. As expected this caused you a dependency nightmare,
Where do you see a dependency nightmare?
introducing what you tried to eliminate with the better design: You
wanted clear interfaces between certain parts of the compiler, but you
did end up breaking interfaces external tools can use, and fixing them
became beyond any hope.
Which interfaces?
When there ever existed something like such an interface, it has been
broken since long, as can be seen on the comments and copies in ppudump.
The globals unit has its role. Many symbols are there for a good reason.
Please specify.
When the variables in the globals unit are initialized in other units,
which are not included by an external tool, they only can contain garbage.
There can be good reason for moving symbols out of it, if you do so,
first try to understand why that symbol is there, and avoid changes in
unit dependencies at all costs.
You mean something like copying declarations and procedures into
external tools, to bypass the inclusion of unwanted units?
Above all: You can get far with theory. FPC has a practical and
pragmatic code base but I dare to say it has a theoretically sound base
design. New
theory can be a solution for practical problems but implementation of it
requires of new practical compromises to be made. For performance,
memory use or historic reasons, or maybe even because the theory can't
catch 100% of the possible situations (there are even some goto
statements in the compiler).
Nevertheless, you have gotten a lot of knowledge of the compiler, and
this project makes me optimistic you can become a good compiler developer.
:-)
DoDi
_______________________________________________
fpc-devel maillist - fpc-devel@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-devel