10.3 Overview: Learning Language Concepts Through Interpreters

We start by implementing only primitive operations in this chapter. Then, we develop an evaluate-expression function that accepts an expression and an environment as arguments, evaluates the passed expression in the passed environment, and returns the result. This function, which is at the heart of any interpreter, constitutes a large conditional structure based on the type of expression passed (e.g., a variable reference or function definition).

Adding support for a new concept or feature to the language typically involves adding a new grammar rule (in camilleparse.py) and/or primitive (in camillelib.py), adding a new field to the abstract-syntax representation of an expression (in camilleinterpreter.py), and adding a new case to the evaluate_expr function (in camilleinterpreter.py).

Next, we add support for conditional evaluation and local binding. Support for local binding requires a lookup environment, which leads to the possibility of testing a variety of representations for that environment (as discussed in Chapter 9), as long as it adheres to the well-defined interface used by evaluate_expr. Later, in Chapter 11, we add support for non-recursive functions, which raises the issue of how to represent a function—there are a host of options from which to choose. At this point, we can also explore implementing dynamic scoping as an alternative to the default static scoping. This amounts to little more than storing the calling environment, rather than the lexically enclosing environment, in the representation of the function. Next, we implement recursive functions, also in Chapter 11, which require a modified environment. At this point, we will have implemented Camille v2.1, which only supports functional programming, and explored the use of multiple configuration options for both aspects of the design of the interpreter as well as the semantics of implemented concepts (see Table 10.3 later in this chapter).

Next, we start slowly to morph Camille, in Chapter 12, through its interpreter, into a language with imperative programming features by adding provisions for side effect (e.g., through variable assignment). Variable assignment requires a modification to the representation of the environment. Now, the environment must store references to expressed values, rather than the expressed values themselves. This raises the issue of implicit versus explicit dereferencing, and naturally leads to exploring a variety of parameter-passing mechanisms, such as pass-by-reference or pass-by-name/lazy evaluation. Finally, in Chapter 12, we close the loop on the imperative approach by eliminating the need to use recursion for repetition by recalibrating the language, through its interpreter, to be a statement-oriented, rather than expression-oriented, language. This involves adding support for statement blocks, while loops, and I/O operations.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.241.116