The Evolution of Programming Languages
The first electronic computers appeared in the 1940's and were programmed in machine language by sequences of O's and l's that explicitly told the computer what operations to execute and in what order. The operations themselves were very low level: move data from one location to another, add the contents of two registers, compare two values, and so on. Needless to say, this kind of programming was slow, tedious, and error prone. And once written, the programs were hard to understand and modify.
The Move to Higher-level Languages
The first step towards more people-friendly programming languages was the development of mnemonic assembly languages in the early 1950's. Initially, the instructions in an assembly language were just mnemonic representations of machine instructions. Later, macro instructions were added to assembly languages so that a programmer could define parameterized shorthands for frequently used sequences of machine instructions.
A major step towards higher-level languages was made in the latter half of the 1950's with the development of Fortran for scientific computation, Cobol for business data processing, and Lisp for symbolic computation. The philos-ophy behind these languages was to create higher-level notations with which programmers could more easily write numerical computations, business appli-cations, and symbolic programs. These languages were so successful that they are still in use today.
In the following decades, many more languages were created with innovative features to help make programming easier, more natural, and more robust. Later in this chapter, we shall discuss some key features that are common to many modern programming languages.
Today, there are thousands of programming languages. They can be classi-fied in a variety of ways. One classification is by generation. First-generation languages are the machine languages, second-generation the assembly languages, and third-generation the higher-level languages like Fortran, Cobol, Lisp, C, C + + , C # , and Java. Fourth-generation languages are languages designed for specific applications like NOMAD for report generation, SQL for database queries, and Postscript for text formatting. The term fifth-generation language has been applied to logic- and constraint-based languages like Prolog and OPS5.
Another classification of languages uses the term imperative for languages in which a program specifies how a computation is to be done and declarative for languages in which a program specifies what computation is to be done. Languages such as C, C + + , C # , and Java are imperative languages. In imper-ative languages there is a notion of program state and statements that change the state. Functional languages such as ML and Haskell and constraint logic languages such as Prolog are often considered to be declarative languages.
The term von Neumann language is applied to programming languages whose computational model is based on the von Neumann computer archi-tecture. Many of today's languages, such as Fortran and C are von Neumann languages.
An object-oriented language is one that supports object-oriented program-ming, a programming style in which a program consists of a collection of objects that interact with one another. Simula 67 and Smalltalk are the earliest major object-oriented languages. Languages such as C + + , C # , Java, and Ruby are more recent object-oriented languages.
Impacts on Compilers
Since the design of programming languages and compilers are intimately related, the advances in programming languages placed new demands on compiler writ-ers. They had to devise algorithms and representations to translate and support the new language features. Since the 1940's, computer architecture has evolved as well. Not only did the compiler writers have to track new language fea-tures, they also had to devise translation algorithms that would take maximal advantage of the new hardware capabilities.
Compilers can help promote the use of high-level languages by minimizing the execution overhead of the programs written in these languages. Compilers are also critical in making high-performance computer architectures effective on users' applications. In fact, the performance of a computer system is so dependent on compiler technology that compilers are used as a tool in evaluating architectural concepts before a computer is built.
Compiler writing is challenging. A compiler by itself is a large program. Moreover, many modern language-processing systems handle several source lan-guages and target machines within the same framework; that is, they serve as collections of compilers, possibly consisting of millions of lines of code. Con-sequently, good software-engineering techniques are essential for creating and evolving modern language processors.
A compiler must translate correctly the potentially infinite set of programs that could be written in the source language. The problem of generating the optimal target code from a source program is undecidable in general; thus, compiler writers must evaluate tradeoffs about what problems to tackle and what heuristics to use to approach the problem of generating efficient code.
A study of compilers is also a study of how theory meets practice, as we shall see in Section 1.4.
The purpose of this text is to teach the methodology and fundamental ideas used in compiler design. It is not the intention of this text to teach all the algorithms and techniques that could be used for building a state-of-the-art language-processing system. However, readers of this text will acquire the basic knowledge and understanding to learn how to build a compiler relatively easily.
Exercises for Section 1.3
Exercise 1 . 3 . 1 : Indicate which of the following terms:
a) imperative b) declarative c) von Neumann
d) object-oriented e) functional f) third-generation
g) fourth-generation h) scripting
apply to which of the following languages:
1) C 2) C++ 3) Cobol 4) Fortran 5) Java
6) Lisp 7) ML 8) Per1 9) Python 10) VB.