Higher-Order and Symbolic Computation 12, 381–391 (1999) c 2000 Kluwer Academic Publishers. Manufactured in The Netherlands. ° Partial Evaluation of Computation Process— An Approach to a Compiler-Compiler YOSHIHIKO FUTAMURA Central Research Laboratory, Hitachi, Ltd., Kokubunji, Tokyo, Japan 185 Abstract. This paper reports the relationship between formal description of semantics (i.e., interpreter) of a programming language and an actual compiler. The paper also describes a method to automatically generate an actual compiler from a formal description which is, in some sense, the partial evaluation of a computation process. The compiler-compiler inspired by this method differs from conventional ones in that the compilercompiler based on our method can describe an evaluation procedure (interpreter) in defining the semantics of a programming language, while the conventional one describes a translation process. Keywords: partial evaluation, program transformation, compiler, interpreter, Futamura projections 1. Introduction It is known that there are two methods to formally describe the semantics (meaning) of programming languages. One of them is to describe the procedure by which the language to be defined is translated into another language whose semantics are already known, i.e., a description of a translator. The other is to describe a procedure evaluating the results of a statement belonging to the language to be defined (a source program), i.e., a description of an interpreter. In a conventional compiler-compiler, the description of a translator is used to describe the semantics of a programming language. That is, the users of a conventional compilercompiler have to write the translation program in terms of a translator description language in defining the semantics of a programming language. The difficulty in writing a translator has been pointed out by Feldman [2] as follows: “One of the most difficult concepts in translator writing is the distinction between actions done at translate time and those done at run time. Anyone who has mastered this difference has taken the basic step towards gaining an understanding of computer languages.” In describing the semantics of a programming language by an interpreter, it is not necessary to set up a distinction between those actions. Therefore, describing an interpreter seems easier than describing a translator. Actually, description by an interpreter is implicitly used at many places in the report on ALGOL 60 [6] and in manuals of many programming languages. The interpreters of such complex languages as ALGOL 60 and PL/I also have been described formally [3, 8]. ∗ This is an updated and revised version of Partial Evaluation of Computation Process—an Approach to a Compiler-Compiler by Yoshihiko Futamura, originally published in “Systems.Computers.Controls”, Volume 2, Number 5, 1971, pages 45–50. Reprinted by permission of John Wiley & Sons, Inc. 382 FUTAMURA Figure 1. Structure of a compiler-compiler. The large bold-line block is the generated compiler. The object language of this compiler is a semantic metalanguage in which an interpreter is described. An object program is translated into machine codes by the metacompiler. However, for reasons described in Section 3, a so-called interpreter is often not as efficient as a so-called compiler in language processing. This paper describes an algorithm to automatically transform an interpreter to a compiler and its application to a compiler-compiler. The algorithm is a sort of partial evaluation procedure (see figure 1). Partial evaluation of a computation process is by no means a new concept [4]. Even in programming languages, POP-2 [1] implies a somewhat similar concept called “partial application.” Nevertheless, it is the author’s belief that this paper is the first instance in which the concept is applied in a compiler-compiler. What kind of partial evaluation algorithm is applicable to a compiler-compiler? It is the purpose of this paper to probe the properties of that algorithm. 2. Partial evaluation The following transformation is called a partial evaluation of a computation process π with respect to variables c1 , . . . , cm , at the values c10 , . . . , cm0 . “In a computation process π containing m + n variables c1 , . . . , cm , r1 , . . . , rn , evaluate the portions of π which can be evaluated using only the values c10 , . . . , cm0 assigned to variables c1 , . . . , cm , respectively, and constants contained in π. The portions which cannot be evaluated unless the values of the remaining variables are given are left intact. Thus, π is transformed into a computation process having n variables. When the computation process thus obtained is evaluated for values r10 , . . . , rn0 assigned to variables r1 , . . . , rn , respectively, its result is equivalent to the result of the evaluation of it for the values c10 , . . . , cm0 , r10 , . . . , rn0 given to variables 383 PARTIAL EVALUATION c1 , . . . , cm , r1 , . . . , rn , respectively.” We denote this transformation by the equation π(c10 , . . . , cm0 , r10 , . . . , rn0 ) = α(π, c10 , . . . , cm0 )(r10 , . . . , rn0 ) (1) We call α the “partial evaluation algorithm,” c1 , . . . , cm the “partial evaluation variables,” and r1 , . . . , rn the “remaining variables,” respectively. We may refer to the usual evaluation as a total evaluation as opposed to a partial evaluation. For example, consider the evaluation of a computation process given by the function f (x, y) = x × (x × x + x + y + 1) + y × y with the values x = 1, y = 1, 2, . . . , l. When we evaluate f (1, y) for each value of y, i.e., when we execute x := 1 ; for y : = 1 step 1 until l do f [x, y] := x × (x × x + x + y + 1) + y × y 3l multiplications and 4l additions are performed. Representing the times elapsed in addition and multiplication by a and m respectively, the above computation requires about (4a + 3m)l. If we have α( f, 1)(y) = 1 × (3 + y) + y × y by partial evaluation of f (x, y) with respect to x = 1, the elapsed time of the partial evaluation, e.g., k, is more than 2a + m, i.e., k > 2a + m (because the partial evaluation involves the evaluation of x × x + x + 1). If we execute for y := 1 step 1 until l do f [1, y] := 1 × (3 + y) + y × y; a computation time of about (2a + 2m)l is required. Therefore, when the relation k + (2a + 2m)l < (4a + 3m)l or k bigm then goto overfl; for k := 1 step 1 until b[0] do a[k + a[0]] := b[k]; a[0] := a[0] + b[0]; end The result of partial evaluation of the above program with respect to b at b[0] = 2, b[1] = 10, b[2] = 20 is described below. begin if a[0] + 2 > bigm then goto overfl; a[1 + a[0]] = 10; a[2 + a[0]] = 20; a[0] = a[0] + 2; end 4. Discussion (a) What is the criterion for the possibility of generating a compiler from an interpreter, i.e., a nontrivial sufficient termination condition of partial evaluation? (b) Which parts of the object program (i.e. the result of partial evaluation of an interpreter with respect to a source program) are more efficient than the corresponding parts of the source program and to what extent? What are the characteristics of the object program and how may it be optimized (with respect to time and space)? (c) Quantitatively, to what extent is describing an interpreter easier than describing a translator? Can we find a partial evaluation algorithm generating a compiler which is as efficient as a compiler generated from a translator? 390 FUTAMURA (d) What kind of semantic metalanguage shall we use to describe an interpreter in order to achieve efficient partial evaluation of the interpreter? At present, the author cannot answer the above questions clearly. It is considered that investigations along the following lines will solve those questions. (1) Understanding structures of interpreters of programming languages. (2) Development of a semantic metalanguage which can be efficiently compiled and by which we can easily describe the abstract syntax of programming languages, the states of abstract machines (stack, table, list, etc.) and their transitions, numerical computation, and list processing. (3) Implementation of a complete partial evaluation algorithm for a specific semantic metalanguage. (4) Theoretical study on the partial evaluation of computer programs. (5) Optimization of semantic metalanguages. The author has made a little progress on item (3). A partial evaluator which is almost equivalent to α1 has been implemented in LISP, and a compiler of program features [5] has been generated from the interpreter of program features by the partial evaluator. The compiler translates ALGOL-like programs written in the program features into an equivalent system of recursion equations. For example, prog [[u; v]; u := n; L1 [null[u] → return[v]]; v := cons[car[u]; v]; u := cdr[u]; go[L1]] is translated into g1[a] = g2[ppair[(U V ); a]] g2[a] = prog2[rplacd[assoc[U ; a]; eval[N ; a]]; g3[a]] g3[a] = g4[a]; g4[a] = g5[a]; g5[a] = [eval[(NULL U ); a] → eval[V ; a]; T → g6[a]] g6[a] = g7[a] g7[a] = prog 2[rplacd[assoc[V ; a]; eval[(CONS(CAR U ) V ); a]]; g8[a]] g8[a] = prog 2 [rplacd[assoc[U ; a]; eval[(CDR U ); a]]; g9[a]] g9[a] = g4[a] where g1–g9 are the function names generated by the compiler and g1[a] is the object program. Superfluous equations such as g3[a] = g4[a], g4[a] = g5[a], etc., can be avoided by optimization of the semantic metalanguage (in this case, LISP). PARTIAL EVALUATION 5. 391 Conclusion The compiler generation method described in this paper is still in the conceptual stage. It remains to determine whether or not the method can be put to practical use in the near future. However, the author hopes that this paper explains the relationship between formal methods of programming language description and actual compilers. It is also hoped that this paper makes a contribution to the study of a compiler-compiler. References 1. Burstall, R.M. and Popplestone, R.J. POP-2 reference manual. Machine Intelligence (2) (1968) 205–249. 2. Feldman, J.A. Formal Semantics for Computer-Oriented Languages. Technical Report, Comput. Ctr., Carnegie Institute of Technology, 1964. 3. Lauer, P. Formal Definition of ALGOL 60. Technical Report TR 25.088, IBM Laboratory Vienna, 1968. 4. Lombardi, L.A. Advances in Computers, Vol. 8. Academic Press, New York, 1967. 5. McCarthy, J., Abrahams, P.W., Edwards, D.J., Hart, T.P., and Levin, M.I. LISP 1.5 Programmer’s Manual, M.I.T. Press, 1962. 6. Naur, P. (Ed.). Revised report on the algorithmic language ALGOL 60. Comm. of ACM (6) (January 1963) 1–17. 7. Rutishauser, H. Description of ALGOL 60. Springer, 1967. 8. Walk, K., Alber, K., Bandat, K., Bekic, H., Chroust, Gerhard, Kudielka, V., Oliva, P. and Zeisel, G. Abstract syntax and interpretation of PL/I. Technical Report TR 25.082, IBM Laboratory Vienna, 1968.