The final extension to your SILLY interpreter is to add subroutines and exit statements to the language. You will need to define three new classes, a Sub class for defining a subroutine, a Call class for calling a subroutine, and an Exit class for exiting a subroutine.
A subroutine declaration specifies the name of the subroutine, an arbitrary number of parameters in parentheses, and a body. It is important to recognize that executing a subroutine declaration does NOT result in its code being executed — it simply stores the parameters and body in memory so that it can be called later. Executing a subroutine declaration when there already exists a subroutine with that same name should result in a run-time error.
A subroutine call specifies the name of the subroutine and an arbitrary number of expressions in parentheses. When a subroutine call is executed, the corresponding parameters and body must be retrieved from memory and executed in a new, independent scope. The parameters are treated as local variables in that scope, initialized with the values specified in the subroutine call. Because a subroutine's scope is independent, statements within a subroutine cannot access variables outside that subroutine. Evaluating a subroutine call should result in a run-time error if there is no declared subroutine with that name or if the number of expressions does not match the number of parameters.
Exit statements are similar to break statements, except they specify that the enclosing subroutine should halt its execution (as opposed to an enclosing loop). Note that the enumeration value Statement.Status.EXIT is defined in Statement and can be utilized in much the same way that Statement.Status.BREAK was used for break statements.
SAMPLE CODE (output in red) >>> sub foo() does print "foo" endsub >>> call foo() "foo" >>> sub hail(num) does print num if (num = 1) then print "DONE" elseif ((num % 2) = 0) then call hail((num / 2)) else call hail(((num * 3) + 1)) endif endsub >>> call hail(5) 5 16 8 4 2 1 "DONE">>> sub sum(low high) does if (low > high) then print "ILLEGAL_VALUES" exit endif var total gets 0 while ((low < high) | (low = high)) do total gets (total + low) low gets (low + 1) endwhile print total endsub >>> call sum(1, 10) 55 >>> call sum(33, 33) 33 >>> call sum(33, 32) "ILLEGAL_VALUES"
This assignment is more complex than the previous two, requiring the creation and modification of multiple classes. It is strongly recommended that you complete this assignment in stages:
Sub class that reads in a subroutine definition with no parameters and no exit statements. You will need to modify the MemorySpace class so that the body of the subroutine can be stored when the Sub statement is executed. You will then need to define the Call class that represents a subroutine call. Executing a Call statement requires looking up the corresponding body for that subroutine and executing those statements.Sub should read and store the parameters along with the subroutine body. Executing a subroutine call will require evaluating the input expressions and assigning those to local variables (the parameters) prior to executing the body. If the subroutine is not defined or the number of inputs does not match the number of parameters, an exception should be thrown with a meaningful (runtime) error message.Exit class that is similar to the Break class from HW3, but is used to exit a subroutine execution.