CSC 533: Programming Languages
Spring 2015

HW4: Implementing Subroutines


The SILLY interpreter you wrote for HW3 extends the original version to allow for sequences of output values and local scopes. For this final SILLY assignment, you are to extend the language further by implementing simple subroutines (without return values). As in HW2, your submission must include a text file named changeLog.txt that identifies each class that you created or modified for this assignment, along with a short note describing the modifications.

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:

<subdecl> --> 'sub' <identifier> '(' { <identifier> } ')' { <statement> } 'end' <subcall> --> 'call' <identifier> '(' { <expression> } ')'

Note that a subroutine declaration specifies the name of the subroutine and an arbitrary number of parameters (enclosed in parentheses). A subroutine call specifies the subroutine name and the sequence of data values that correspond to the parameters. The parentheses are required in both statements, even if there are no parameters. If the number of data values 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.

A subroutine defines a new scope. Parameters are treated as local variables in that scope, initialized based on the data values provided in the subroutine call. In addition, VarDeclaration statements can be used inside the subroutine to declare additional local variables. It is the intention that subroutines only be declared in the global scope. If a subroutine is declared inside another subroutine or control statement, this should not produce an error but the parent scope is still considered to be the global scope. That is, regardless of where the subroutine declaration appears, any reference to a non-local variable inside the subroutine will access the global scope.

For example:

SAMPLE CODE (output in red)
  >>> sub foo ( n x )
        var temp
        temp = ( 2 * x )
        output ( n m temp )
      end
  >>> n = 8
  >>> m = 9
  >>> call foo ( 100 ( n + m ) )
  100 9 34
  >>> sub bar ( m )
        output ( n m )
        call foo ( 55 66 )
      end
  >>> call bar ( 200 )
  8 200
  55 9 132
  >>> quit
  BYE
  >>> sub countdown ( num )
        output ( num )
        if ( num > 0 ) 
          num = ( num - 1 )
          call countdown ( num  )
        end
      end
  >>> call countdown ( 5 )
  5
  4
  3
  2
  1
  0
  >>> quit
  BYE

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 code for that subroutine should be stored in the code segment. Subsequently, when a subroutine call is executed, the corresponding code must be accessed from the code segment so that it can be executed.