a great deal of knowledge had accumulated on the theory, design, and implementation of compilers. And the programmers who built the first FORTRAN compiler made no small contribution to this branch of what, in the 1960s, came to be called
programming systems
. 56
A compiler is a computer program of a rather complex and peculiar sort, for it manipulates and processes other computer programs. It takes as input a program written in a machine-independent or high-level programming language (such as FORTRAN, the source program) and produces as output a
functionally
equivalent program (the object code) that is executable on a specific computer. 57 A complier, then, is an
automatic
translator in the strict linguistic sense; it transforms automatically a text in a programming language
L
into another functionally equivalent text specified in a machine language
M
. Both texts are programs. Both texts are liminal. But, the abstract aspect of a program in the programming language (when it describes an algorithm meant
only
for humanâhuman communication, a piece of âsocial textâ) is more pronounced than in the case of a program in a machine language.
The great difference between a compiler and the âautomatic machine translatorsâ that Warren Weaver and others had so desired (see Chapter 11 , Section IX), was that automatic machine translation referred to translating texts in one
natural
language (say, Russian) into another natural language (say, English). The compilerâs task is far more modest. It translates from one
artificial
language into another. And because artificial languages are invented, they can (at least in principle) be defined precisely and restricted in features, without the richness, vagueness, ambiguity, and variability of natural languages.
Thus, the challenges faced by the compiler writers (as, somewhat curiously, programmers who designed and built compilers would come to be called 58 ) may not have been as overwhelming as the task faced by programmers who built automatic machine translation systems. However, the challenge of compiler writing was, in many ways, more complex than any other kind of programming experienced until then.
A compiler is, in fact, a system of many components performing many different tasks. It is an amalgam of many different algorithms of varying intricacy and complexity. It must first ensure that the program syntax adheres to the rules of the language. It must âmake senseâ of the overall structure, and this involves recording the flow of information and the flow of control between program components. To manage the complexity of its work, the compiler must decompose the source program into subunits (called
basic blocks
) that can be analyzed separately. The compiler must gather and record all the information about the program that is necessary for the ultimate task of the complier: generating machine code (look ahead to Figure 15.2 ).
Hovering like the sword of Damocles over all this is the âefficiency imperative.â The proof of the pudding lies in the eating, and âeatingââin this contextâmeans how efficient the object code is compared with âhand-codedâ programs in assembly or machine language.
There is a âculturalâ gap between the language of the source program and the language of the object program. The former language is abstract and symbolic. In fact, it is itself an abstract artifact; its syntax and semantics refer only implicitly to some (abstract) computer. The latter language is relatively more concrete and physical; it is liminal because, although abstract, it is very close to some real physical computer. The compiler has to straddle the two cultures and âmapâ texts from one to the other. The sword of Damocles mandates that the object code must be âoptimizedâ as best as possible for both time to execute the object program and space required to store the object program in computer