In order to implement simple subroutines, you will need to define two new classes, a subDecl
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 (type/variable pairs in parentheses, separated by commas), and a compound statement specifying the body of the subroutine. A subroutine call specifies the subroutine name and the sequence of expressions (also in parentheses, separated by commas) that correspond to the parameters. A subroutine call should result in a run-time error is there is no declared subroutine with that name, the number of expressions in a subroutine call does not match the number of parameters in its declaration, or if there are type mismatches between the expressions and parameters. Note: 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, there can be nested scopes within a subroutine via compound statements. 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" ; } >>> call foo ( ) ; foo >>> sub avg ( int num1 , int num2 ) { int sum = num1 + num2 ; int val = sum / 2 ; output "average" , "of" , num1 , "and" , num2 , "=" , val ; } >>> call avg ( 2 , 4 ) ; average of 2 and 4 = 3 >>> int n = 20 ; >>> call avg ( n , n + 5 ) ; average of 20 and 25 = 22 >>> sub sumTo ( int num ) { int sum = 0 ; while num > 0 { sum = sum + num ; num = num - 1 ; } output sum ; } >>> call sumTo ( 10 ) ; 55 >>> call sumTo ( 100 ) ; 5050 >>> sub countdown ( int n ) { if n == 0 { output "BLASTOFF" ; } else { output n ; call countdown ( n - 1 ) ; } } >>> call countdown ( 5 ) ; 5 4 3 2 1 BLASTOFF >>> sub stamp ( str word , int times ) { str final = "" ; repeat times { final = final + word ; } output final ; } >>> call stamp ( "foo" , 3 ) ; foofoofoo >>> call stamp ( "X" , 10 ) ; XXXXXXXXXX
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.