In the HW2 version of the SILLY interpreter, all of the variables in a SILLY program were treated as being in the same, global memory space. The first time a variable was assigned a value, an entry was stored for that variable and any subsequent reference to that variable would access that same entry. While this simple model works fine for regular variables, it causes problems once constants are introduced. Recall that a constant declaration creates a variable that cannot be reassigned once it is initialized. Consider what happens when a constant declaration is placed inside a loop:
repeat 3 const x = 10 output ( x ) end
During the first pass through the loop, the constant x
is declared, initialized, and displayed. However, the second pass produces a runtime error, since the variable x
already exists when the constant declaration is executed again. In most languages, control structures such as repeat
, while
and if
define new scopes. Variables (including constants) that are declared within the structure are considered local to that scope. The lifetime of such a variable begins when its declaration is reached and ends when the enclosing scope ends. Using scopes, the above example would execute without error, since each pass through the loop would define a new scope and so no conflicts would occur.
For this assignment, you are to extend the SILLY intepreter so that each control structure (if, while, repeat) defines a new scope. In addition you will define a new statement type for local variable declarations. Any variable that is explicitly declared inside a control structure, using either a const
or local
declaration, belongs to the scope defined by that structure. That means it exists only while executing that scope and it disappears after the scope is finished. For example, consider the following code segment:
x = 100 if ( x > 0 ) const x = 200 if ( x > 100 ) local x = 300 output ( x ) end output ( x ) end output ( x )
This code segment defines three nested scopes. The global scope encompasses everything and includes any variables that are not explicitly declared (using const
or local
). As such, the first assignment statement assigns a value to x
in the global scope. The if
statement defines a scope that contains the constant x
. Within that scope, a nested if
statement defines an additional scope, in which local variable x
is declared. When evaluating expressions in SILLY, static scoping is used. Thus, if a variable is declared within the current scope, the corresponding value should be accessed. If it is not declared in the current scope, the enclosing scope should be checked next, then the scope that encloses that scope, etc.
To implement nested scopes, you will need to modify the MemorySpace
class so that a stack of scopes (variable/binding maps) is maintained (as opposed to a single, global scope/map). New methods should be added to the MemorySpace
class for beginning a new scope (i.e., pushing it on the stack) and ending the current scope (i.e., popping it off the stack). When the statements in a control structure are executed, a new scope should begin so that any local variables or constants declared within are stored within that scope. When execution ends, that scope should end, removing any local variables/constants. Note that variables that are assigned a value without being declared are assumed to belong to the global scope (as before), and so persist once they are assigned.
The methods for declaring, storing, and looking up variables in the memory space will also need to be modified so that they access and modify the appropriate scope in the runtime stack.
SAMPLE CODE (output in red) >>> x = 100 >>> repeat 3 const x = 10 output ( x ) end 10 10 10 >>> output ( x ) 100 >>> repeat 3 local x = 10 x = ( x * 2 ) output ( x ) end 20 20 20 >>> output ( x ) 100 >>> if ( x > 0 ) const x = 200 if ( x > 100 ) local x = 300 output ( x ) end output ( x ) end 300 200 >>> output ( x ) 100 >>> if true const vbl = "foo" output ( vbl ( vbl + vbl ) ) end foo foofoo >>> vbl = 99 >>> output ( vbl ( vbl + vbl ) ) 99 198 >>> i = 1 >>> while ( i <= 8 ) local sum = 0 local j = 1 while ( j <= i ) sum = ( sum + j ) j = ( j + 1 ) end output ( i sum ) i = ( i + 1 ) end 1 1 2 3 3 6 4 10 5 15 6 21 7 28 8 36 >>> global1 = 100 repeat 2 const localConst = 200 local localVar = 300 global2 = 400 if ( localConst > 0 ) local localVar = 500 global1 = ( global1 + 1 ) global2 = ( global2 + 1 ) if ( global2 > global1 ) global3 = ( global1 + global2 ) end output ( localConst localVar global1 global2 global3 ) end output ( localConst localVar ) end output ( global1 global2 global3 ) 200 500 101 401 502 200 300 200 500 102 401 503 200 300 >>> output ( g1 g2 g3 ) 102 401 503