Project 4: SL Interpreter

Semantics of SL

In order to interpret SL programs, we first need to give meaning (or semantics) to the language constructs. For the most part, the semantics of the language is straightforward:

The unique feature in SL is its identifier scoping. In SL: Note that there is no explicit declaration of any variable. The mere use of a variable inside a function amounts to an implicit declaration inside that function. Similarly, the use of a variable inside the program's main body makes it local to the main body.

Let's look at an example. The following program:
    function incrArgs (x, y) {
        let x = (+ x 1); let y = (+ y 1); write x; write y;
    }
    {let x=3; let y=5; call incrArgs(x, y); write x; write y;}
    
should output 4, 6, 3, and 5. You should be able to figure out what this SL program produces:
    function f (n) {
        if (== n 0)
            {return 1;}
        else
            {let x = call f ((- n 1)); return (* x n);}
    }
    { read x; let x = call f(x); write x; }
    

The SL Interpreter

To interpret an SL program you need to provide an interpretation function for each of the parse nodes for that program. In the previous assignment (the SL parser) each parse node had an empty interpret() function. Now you need to fill it out. Here is a partial outline of the interpret function for some of the parse nodes: Thus, the interpretation process is somewhat similar to the print routine you implemented for printing the parse tree. The interpretation of a given parse node is implemented recursively, in terms of those of its children nodes.

As the interpreter executes, the values of variables get changed (by the LET assignment statements, for example). The interpreter can maintain the current value of each variable in the symbol table. Just extend the symbol table to include a current value field for each entry. However, there is still the question of scoping; i.e., how do you maintain two distinct instances of the name x in the first example above? Your parser symbol table has just one entry for it.

To implement SL's scope rules, you can use local symbol tables that are created dynamically. The main program body, and each function body executes in the context of a local symbol table. Every interpret function, as well as the main program body, is given a local symbol table to be used. Changes to variable values during the execution of that function are maintained in its local symbol table. When a function call occurs (i.e., in the interpret function for a CALL node), the following events occur:

Miscellaneous


Ravishankar Mosur
Last modified: Tue Mar 31 12:02:08 EST 1998