AN ENVIRONMENT FOR INTERPRETER-BASED

PROGRAMMING LANGUAGE PROJECTS

 

John Barr and L. A. Smith King

Department of Computer Science, Ithaca College

Abstract

This paper discusses the programming language course and presents an approach to some of the pedagogical challenges presented. We aim to expose students to all the concepts central to a traditional programming language course but also give experience with the implementation of various languages. To this end, we are developing a software environment, MULE, which supports this teaching goal. This paper gives an overview of MULE and discusses our recent experiences using MULE as a teaching tool.

Introduction

The programming languages course is a fundamental part of the undergraduate curriculum in computer science. Students are encouraged to consider how elements of programming languages complement each other and can be used to create powerful abstractions. At the same time, students learn how language elements constrain programming styles. Thus, students are encouraged to see programming languages as objects of study, and not merely as the syntax for expressing an algorithm.

The programming languages course presents a pedagogical challenge because it has a number of competing goals. One goal is to broaden the students' experience with a variety of programming language features. This approach is on firm philosophical foundations, following Aristotle [1] and Locke [2] both of whom argued that specific experience is required in order to recognize general principles. Another goal is to cultivate an understanding of how language features are implemented, since every language construct raises implementation concerns. The challenge is how best to teach a diversity of languages and features in a unified way while fulfilling implementation considerations.

Many programming languages courses compare and contrast the semantic and syntactic features of a number of distinct programming languages and discuss some parsing theory. This comparative approach enriches the students' experience of language diversity and can be used to illustrate common concepts that underlie many programming languages. The student gains practice and insight into different programming language paradigms. However the relationship between implementation and language features is often not satisfactorily explained.

An alternative is an interpreter-based approach [3, 4]. In this approach, programming language features are presented with the aid of an interpreter which implements the features of interest. This approach exposes implementation concerns in a natural way. Additionally, the interpreter acts as a direct and unambiguous explanation of the run-time semantics of language features. By modifying and extending the interpreter, students gain concrete experience with a language concept. Examining interpreters for several languages also exposes the common principles shared between languages. The difficulty is how to avoid emphasizing minutiae specific to a particular interpreter at the expense of larger issues. As interpreters for most actual programming languages are too complicated for use in the classroom, another challenge is to find a suitable language interpreter and access to the source code for modifications and extensions.

We advocate a hybrid approach which combines the comparative and interpreter-based approaches. The goal is to expose students to all the concepts central to a traditional programming language course but also give experience with the implementation of various languages. Towards this end, the lecture material is largely based on the traditional programming course emphasizing exposure to a variety of languages, language similarities and differences, and some parsing theory. This material is supplemented by interpreter-based projects. This approach differs from a pure interpreter-based approaches (e.g. [3]) in two ways: the interpreter projects are ancillary teaching tools, rather than the main focus of the course; and secondly, since central language concepts are presented in traditional lectures, the interpreter projects can be highly simplified but still illustrative. The projects give student experience relating language syntax and semantics with an implementation, but avoid the danger of bogging students down in a large interpreter implementation. In this setting, a system for building and experimenting with interpreters is desirable.

We are developing MULE, a software environment supporting the development of interpreter-based projects for multiple language paradigms. For each paradigm, MULE provides a simple but limited interpreter. As part of their class work, students can extend or modify the appropriate interpreter. MULE currently supports an Object-Oriented programming paradigm, a functional programming paradigm, and an "calculator" environment for arithmetic expressions. However, MULE is an extensible framework to which support for other programming paradigms can be added. For example, we plan to add support for logic programming.

Using a single environment for the projects has several advantages. The interpreters for each paradigm share a common architecture and are stylized to facilitate easy comprehension. Secondly, MULE has a common interface for each supported language paradigm. So the general syntax of the system must be mastered only once, and can be reused for subsequent projects. MULE's common interface is itself a command interpreter and thus serves as a model to which students can refer while working on their own projects. Finally, we have complete access to the MULE source code.

MULE also has a windowed graphical user interface, and incorporates visual representation intended to add a element of fun for the students. For example, objects in the OOP module appear on screen as windows, where the window body contains the object's "value."

In the remainder of this paper we describe MULE and discuss our experience with the OOP portion of MULE in a Programming Languages course. We conclude with a discussion of future development of MULE.

The MULE System

MULE stands for MUltiple Language Environment. It is a software system written in Scheme and designed to facilitate the creation of interpreter-based programming languages projects. Scheme's excellent abstraction facilities enable the creation of a substantial interpreter that is still compact and comprehensible (with a reasonable amount of effort).

MULE presents the student with a text window in which commands can be typed. A command interpreter parses the typed text and implements the appropriate action using a fetch/parse/execute cycle. From this command window, the student can give commands to access programming paradigms supported by MULE, as illustrated in the following figure.

Figure 1: MULE structure.

 

In the figure above, the rectangles represent windows that appear on the screen and the hexagons describes the treatment of text typed in the associated window. Accessing a programming paradigm supported by MULE causes a new text window to appear on the screen. Text entered in a language paradigm window is parsed by a paradigm-specific interpreter which implements the semantics. An actual screen shot from MULE appears below:

Figure 2: MULE screen shot.

Commands entered in the bottom "Transcript" window are interpreted by the MULE command interpreter. In this example, the user has entered MULE commands to open the OOP window (top right) and to start the OOP interpreter running. Commands in the OOP window are interpreted by the OOP interpreter as statements in the Simple object-oriented language (SOOP). In this example, the user has entered SOOP statements to create two box objects, name the objects y and v respectively, and to set the value of y to be 65 and to set the value of v to be 20. Box objects display themselves as windows, in this case as the two windows in the upper left of the figure.

The bottom MULE command window of figure 2 corresponds to the left-hand side of figure 1. The OOP window at the upper right of figure 2 corresponds to the right-hand side of figure 1.

The source code for both the MULE central command interpreter and the OOP interpreter is accessible to the student and can be modified as part of a class project.

The MULE implementation contains the central command interpreter and a paradigm-specific interpreter for each supported language paradigm. Since a lot of interpreter code can be reused, MULE also contains a library of building-block routines for the creation of interpreters. Additionally, MULE offers an object-oriented library of building-block routines for manipulating text windows. Object orientation was achieved by using Scheme's support for first-class procedures to represent objects as functions.

The MULE system is extensible. To add a new paradigm or interpreter-based environment to MULE, the existing libraries are used to create an object encapsulating a "skeleton" interpreter for the new paradigm and for manipulating a text window interface to the interpreter. The interpreter library routines are used to complete an interpreter for the new paradigm. Finally, the command interpreter is edited to implement a command to access the new paradigm object.

Integrating MULE into a Programming Language Course

MULE is not intended as the primary means of presenting programming language information; it is too stylized and simplistic for that. Rather, MULE is intended as an tool used to reinforce the standard presentation. We envision a programming language course in which students compare a variety of programming paradigms, augmented with project assignments using MULE. In this context, the projects reinforce the issues raised in lecture and give students experience relating syntax and semantics with an implementation. Additionally, we can use the MULE implementation itself to illustrate a lecture point while simultaneously preparing students for a project assignment.

For their projects, students use MULE several ways: as a software environment which structures their work, as a model of how to implement their own projects, and as a software library for the implementation of their own projects. These features minimize the risk of peripheral implementation issues (like how to access text typed in a window) from obscuring original intent of the project. MULE projects generally consist of either adding support for a new language paradigm or extending an existing paradigm with new language features.

The interpreters for language paradigms supported by MULE are independent and do not impose any order of use requirements. Prior knowledge of Scheme is not required, but since MULE is Scheme-based, students need to learn some Scheme to complete their projects. Our approach has been to present Scheme as an example of a functional language during the lecture, and thus teach enough Scheme to complete the projects. MULE also has an object-oriented design, so this argues for some class presentation of the object-oriented programming style as a pre-requisite for starting MULE projects. We envision a study of the MULE command interpreter as a convenient way to investigate these two subjects.

Experience with MULE

We used the object-oriented programming module of our first MULE implementation in the programming languages course during Spring 1994 [5]. Students were asked to implement a recursive-descent parser and associated semantics to evaluate arithmetic expressions. The expression evaluator was added by extending MULE's existing OOP interpreter. A grammar was used to express the class of arithmetic expressions to be parsed. The grammar used for the Project is given below:

E ::= T + E | T - E | T

T ::= F * T | F / F | F

F ::= number | obj_id | < E >

The grammar is quite ordinary, with the exception that it includes objects (obj_id) as well as integer constants. Consequently, the expression evaluator had to interact with the existing implementation to find the referenced object and use object methods to get the "value" used to compute the expression.

Several lectures were devoted to discussing the existing OOP module and its implementation in Scheme. These lectures emphasized general programming language implementation issues, and were often accompanied by interactive demonstrations of the existing code using a laptop computer and flat display suitable for overhead projection. In addition, some time was spent discussing recursive-descent parsing.

Our experience was quite positive. The students enjoyed the graphical representation of objects as windows and grasped the connection between the object abstraction and the Scheme code used to implement objects. In particular, the implementation relied on the dynamic binding of message names with object methods, which is usually a difficult concept to convey. All completed the project successfully.

Secondly, most students were comfortable with the Scheme implementation of a recursive descent parser. We actually presented parsers in both Scheme and Pascal, and most of students preferred the Scheme implementation.

Also, the use of a laptop computer and overhead projection to present interactive examples was invaluable. As a result, students had little difficulty finding the correct place to modify the interpreter and were able to focus their attention on writing the expression parser.

Finally, we discovered some idiosyncrasies in our original implementation which hindered its use. The MULE environment described in this paper has corrected those deficiencies.

Alternative Projects

Extending MULE's OOP module by adding an expression evaluator offered an opportunity to experiment with parsing within a working interpreter. This is a more traditional programming languages project. However there are many other project possibilities, as outlined below.

One project would be to add more objects to the OOP system. For example, after studying a stack object, students could add a queue or list object. Less knowledge of the interpreter is needed, since a new object would require a change of only a few lines of code to the interpreter, but would require a more sophisticated understanding of objects, their creation, and the relationship between first class procedures and objects.

Another project would be to add sequential execution to a language module so a user could group multiple statements without entering each individually and waiting for the results. This introduces the notion of a block. In the course of the project, students would have to wrestle with syntactic issues (such as the addition of a begin/end keyword pair) and semantic issues of scope. For example, one could investigate the difference between static and dynamic scopes. An understanding of various scoping strategies would thus be reinforced by implementation. One could continue this extension by adding support for nested blocks.

A third project would be to introduce the notion of type into the OOP module. This would requiring adding some mechanism for type declaration and then modifying the OOP interpreter to do type-checking on operations. This project would provide a vehicle for comparing typed and non-typed languages, and the concept of a dynamically typed language.

Finally, there are a number of possibilities for experimenting with multiple paradigms simultaneously. For example, one could use list and stack objects implemented in the OOP module as the basis for implementing dynamic or static-scoping in the FP (functional programming) module. A more extensive project could use MULE's OOP and FP modules to add a multiple-paradigm language (MP) that has both OOP and FP features. The MP interpreter could be layered over the existing OOP and FP interpreters so that MP statements would actually be implemented by executing the "equivalent" OOP and FP statements.

Summary

The Programming Languages course should expose students to a diversity of languages, explore programming language similarities and differences, and discuss language implementation issues. In our experience, interpreter-based projects can be useful in meeting these educational goals, and provide a particularly natural context for considering language implementation concerns.

We have described a hybrid approach which includes elements of the traditional and interpreter-based approaches to teaching the Programming Language course. Our approach differs from a pure interpreter-based approach (e.g. [3]) because interpreter-based projects are ancillary teaching tools, rather than the central focus.

We also described MULE, a software system we are developing to provide a single environment which supports interpreter-based projects for multiple language paradigms. Our recent experience with MULE suggests it is a useful tool for interpreter-based projects in the Programming Languages course

References

[1] Aristotle, Posterior Analytics, Book 2, Chapter 19, 100A from Aristotle I, Britannica Great Books, 1952

[2] Locke, An Essay Concerning Human Understanding, Book 2, Chapter 1, Section 2 from Aristotle I, Britannica Great Books, 1952

[3] Kamin, Samuel, Programming Languages: An Interpreter-Based Approach, Addison-Wesley Publishing Company, 1990.

[4] Friedman, D., Wand, M. and Haynes, C., Essentials of Programming Languages, The MIT Press, 1992.

[5] Barr, J. and Smith King, L. "Interpreter-based Projects for a Traditional Programming Languages Course, The Journal of Computing in Small Colleges, Vol. 10, No. 2, 1994.