In order to implement simple subroutines, you will need to define two new classes, a subDeclaration
class for declaring a subroutine and a subCall
class for calling a subroutine. The grammar rules for these new statements are:
Note that a subroutine declaration specifies the name of the subroutine, an arbitrary number of parameters (separated by commas), and the statements that make up that subroutine. A subroutine call specifies the subroutine name and the sequence of expressions (also separated by commas) that correspond to the parameters. If the number of expressions in a subroutine call does not match the number of parameters in its declaration, or if there is no declared subroutine with the specified name, the call should result in a runtime error. In addition, every subroutine declaration is assumed to be in the global scope, regardless of where that declaration occurs.
When a subroutine is called, it defines a new, independent scope. Parameters are treated as local variables in that scope, initialized based on the expressions provided in the subroutine call. In addition, local variables can be declared inside a subroutine using the var
statement you wrote in HW3. Because a subroutine's scope is independent, statements within a subroutine cannot access variables outside that subroutine. As a result, it is legal to declare a subroutine parameter or local variable with whose name is already declared in an external scope.
For example:
SAMPLE CODE (output in red) >>> sub foo : output "foo" ; end >>> call foo ; foo >>> sub bar x : var y = 9 ; var z = x + y ; output x , y , z ; end >>> call bar 1 ; 1 9 10 >>> call bar 3 + 2 ; 5 9 14 >>> sub biz a , b , c : var max = a ; if b > a then if b > c then max = b ; else max = c ; end else if c > a then max = c ; end end output "max" , "of" , a , b , c , "is" , max ; end >>> call biz 10 , 20 , 30 ; max of 10 20 30 is 30 >>> var a = 7 ; >>> call biz a - 1 , a + 1 , a ; max of 6 8 7 is 8 >>> var x = 1234 ; >>> sub baz x : var q = 88 ; output x , q ; end >>> var q = true ; >>> call baz 3 * 5 ; 15 88 >>> call baz "cs" ^ "dj" ; csdj 88 >>> output x , q ; 1234 true >>> sub fibonacci N : var previous = 0 ; var current = 1 ; while N > 0 do var temp = current ; current = previous + current ; previous = temp ; N = N - 1 ; end output current ; end >>> var f = 1 ; >>> while f <= 8 do call fibonacci f ; f = f + 1 ; end 1 2 3 5 8 13 21 34 >>> sub recurse num : if num > 0 then call fibonacci num ; call recurse num - 1 ; end end >>> call recurse 8 ; 34 21 13 8 5 3 2 1 >>> var global = "danger" ; >>> sub crashes : output global ; end >>> call crashes ; Exception in thread "main" java.lang.Exception: RUNTIME ERROR: variable global not declared
Note that executing a subroutine declaration does NOT result in its code being executed. Declaring a subroutine simply stores the code associated with that subroutine in memory so that it can be called later. You will need to add a third field to the MemorySpace
class, corresponding to the code segment. When a subroutine declaration is executed, the parameters and code for that subroutine should be stored in the code segment. Subsequently, when a subroutine call is executed, the corresponding parameters and code must be accessed from the code segment so that it can be executed.