For this assignment, you are to extend the SILLY interpreter from HW2 to allow for subroutines and local/global variables. The expanded EBNF for the SILLY language is as follows:
The Sub statement defines a simple subroutine, which may have an arbitrary number of parameters. There is an optional local variable declaration that can appear at the beginning of a subroutine, followed by the sequence of statements that make up the subroutine. The Call statement calls a specific subroutine, with a series of expressions specifying the input values that correspond to subroutine parameters.
When a subroutine is called, the expressions in the call are evaluated and assigned to the subroutine parameters in a new local scope. If any local variables are declared, then these must be created in the local scope as well. When executing the statements inside the subroutine, static scoping is used. That is, when looking up or assigning to a variable, the local scope is checked first. If that variable does not appear in the local scope (i.e., it is not a parameter or local variable), then the global scope is used. As before, if this is the first time the variable has been encountered, it will be considered to have value 0 (in the global scope).
SAMPLE CODE (output in red) >>> sub foo ( x y ) local ( z ) z = ( x + y ) output z output a end >>> call foo ( 4 9 ) 13 0 >>> z = 10 >>> a = 20 >>> call foo ( ( z + a ) 5 ) 35 20 >>> output z 10 >>> quit BYE >>> sub countdown ( num ) output num if num num = ( num - 1 ) call countdown ( num ) end end >>> call countdown ( 5 ) 5 4 3 2 1 0 >>> quit BYE
In the following impementation files, the
VariableTable class has been replaced with a
Bindings class, which keeps track of the stack of variable tables for
local and global scopes, as well as a table of subroutine definitions. Skeletons for the
Call classes have been provided so that the interpreter will execute
as is. You must complete the implementations of these two classes to produce a full-featured interpreter.
It is strogly suggested that you complete this assignment in stages. It is much better to have code that works on a limited range of options as opposed to code that tries to do everything but succeeds at nothing. I would recommend the following breakdown:
callmethod should do is call
bindings.beginScope()to create a new scope for the subroutine (i.e., push it on the runtime stack), and the last thing it should do is call
bindings.endScope()to end that scope (i.e., pop it off the runtime stack).
callthat Sub, you will need to declare each of those local variables by calling
tis the token for that variable. After doing that, the methods of the Bindings class will do the right thing with respect to local vs. global.
callyou will need to not only declare each parameter but also assign each a value (taken from the ArrayList passed into the
callmethod). Within the Call statement, you will need to store the expressions that correspond to the subroutine parameters. When the Call statement is executed, it must evaluate each of the expressions, collect the corresponding values in an ArrayList, and pass that ArrayList as a parameter to the Sub