sakumar wrote
On going through the grammar again, I see that <subroutineCall> can occur in two places -- one as part of a do statement and another as part of a <term>. In the former case the return value should be discarded (popped); it should remain on the stack if the subroutine call is part of a <term> since the term's operation will pop it.
That is correct. Conversely, if you mistakenly call a void function in an expression, the 0 that it returns is on the stack and will be used in the continued computation. Although the result of the expression will be incorrect, the stack will not be skewed so the program will not crash when the current function returns. (Think about what would happen if the SP was off by one when the function popped the RIP and jumped to the value it got.)
Further to your point about error checking, it is assumed that the type and number of arguments to a subroutine should match between calls and definition because the compiler specs don't call out that that check should be made. Of course, one of the enhancements could be to add a semantic analysis module to the compiler.
In fact, the supplied compiler does some semantic analysis. Although I use my compiler since it generates better code, I usually also run my programs through the supplied compiler since it often finds argument and function/method mismatches mine misses.
Good error handling is a lot of work, and is often under budgeted in project schedules and is not considered early enough in design. It can be quite troublesome to add it late in the project. On my current work project, we're about 90% through the development and are just discovering the remaining 90% of the work, mostly related to error handling.... (Search the web for 90-90 rule.)
--Mark