Administrator
|
The intent is to compile the program using recursion (the compiler design they are steering you towards is known as a "recursive descent" compiler.
Notice that the definition of an expression in the Jack grammar is
expression => term (op term)*
The (op term)* means that the sequence op term can appear zero or more times. Thus an expression could be
expression => term
expression => term op term
expression => term op term op term
and so on.
What you want your compiler to do is be able to have a function that can compile a term (note that CompileTerm() this is one of the functions in the API specification).
When you call CompileTerm(), its job is to generate the code for exactly one 'term' and put the result of evaluating that term on the top of the stack.
So when you call CompileExpression and you have
term1 op1 term2
the result should be that you call CompileTerm() and it handles term1. Then your CompileExpression() function recognizes op1 and so it remembers what op1 is and called CompileTerm() which will take care of term2. Once it is done, the result of term2 is on the top of the stack and the result of term1 is the next thing down. So now CompileExpression just needs to add the code to execute op1 on the top two things on the stack.
If you have a longer expression, such as
term1 op1 term2 op2 term2
then you can just stay in a loop doing the above tasks until your CompileExpression() function doesn't find an op. The result is that all of the operators become left associative. There are other ways to do it and the main alternative results in the operators all being right associative. The Jack language spec very carefully avoided defining operator precedence or associativity specifically to let you implement your compiler in whatever way is easiest for you. The result, however, is that Jack programs that do not use parens to force the desired order of operations for an expression may evaluate differently on different compilers.
Notice that there is nothing here that addresses parentheses. That's because parens are not involved in the grammar rules for an expression. They are dealt with at the 'term' level.
When you call CompileTerm(), it has to look at the next token and decide which of the grammar rules to process. When you have an open paren, there is only one possible rule, namely
term => '(' expression ')'
So when you see an open paren, you throw it away, call CompileExpression(), and then look for and throw away the following close paren, and return.
Done. That's all there is to it.
Don't make it harder than it is.
|