CSCE 531
Summer 2019
Project Part III
Due Tuesday April 23, 2019

Process C control statements.

To receive 80% of the credit (90% of the undergraduate credit): You must process if statements, while statements, break statements, and return statements. Be sure to do the appropriate semantic error checking: break cannot appear outside a while, etc.

To receive 90% of the credit (100% of the undergraduate credit): You should also process for statements. Be sure to do any appropriate semantic error checking. The break statement should be implemented in the context of the for statement, too.

To receive 100% of the credit (10 points of extra credit for undergraduates): You must also process switch statements. The only kind of case constant expression you need to support is a simple integer constant (no operators, not even unary minus). You should enforce a rule that the type of the switch expression must be some integer type (it is subject to the usual unary conversions). Be sure to do other appropriate semantic error checking: case constants must be unique within a switch, only one default label for a given switch, etc. The break statement should be implemented in the context of the switch statement, too.

Before starting on switch it is highly recommended that you first familiarize yourself with switch statement semantics. According to Harbison & Steele (5th ed.), Section 8.7, the switch statement is executed as follows:

  1. The control expression (the expression in parentheses after the keyword switch) is evaluated.
  2. If the value of the control expression is equal to that of the constant expression in some case label belonging to the switch statement, then program control is transferred to the point indicated by that case label as if by a goto statement.
  3. If the value of the control expression is not equal to any case label, but there is a default label that belongs to the switch statement, then program control is transferred to the point indicated by that default label.
  4. If the value of the control expression is not equal to any case label and there is no default label, no statement of the body of the switch statement is executed; program control is tranferred to whatever follows the switch statement.
(Above, we say that a label belongs to a switch statement if it appears in the body of the switch statement but is not inside the body of any nested switch statement.) When control is transferred to a case or default label, execution continues through successive statements, ignoring any additional case or default labels that are encountered, until the end of the switch statement is reached or until control is transferred out of the switch statement by a break or return (or goto or continue) statement.

There are four primary functions in the back end that are needed for this assignment: b_jump, b_cond_jump, b_dispatch, and b_encode_return, and two additional supporting functions, b_label and new_symbol.

The function new_symbol returns a fresh, unique, saved string that can be used as a label and jump destination; b_label emits a given string as a label. Each call to new_symbol returns a different string, which is guaranteed not to conflict with any legal C identifier or keyword. The function b_jump generates an unconditional jump to a given label; b_cond_jump generates a conditional jump based on the value of the top of the stack (the value is also popped off the stack).

The function b_dispatch is to be used in handling a switch statement. It takes an integer argument n and generates code that compares n with the value on top of the stack (which must be an integer). If the two values are equal, a jump is executed and the value is popped, otherwise no jump occurs and the value is left on the stack. Thus, several calls to b_dispatch can be called in a row, each comparing the top of the stack with a series of different integer constants.

The function b_encode_return generates code to return from a function with an optional return value. If there is an expression following the return keyword, as in

    return x+3;

then the expression's value should be assignment-converted to the return type of the function (which must be non-void), and this type should be passed to b_encode_return. The generated assembly code expects the converted value to be on top of the stack. If there is no expression after the return keyword, then TYVOID should be passed to b_encode_return, and the resulting code does not assume any value on top of the stack. (Note: In the version of C we are implementing, an expressionless return in a function with a non-void return type is not a semantic error! The return value is undefined in this case; you may wish to issue a warning to this effect, but you don't have to.)

As in Project parts I and II: your compiler should be capable of detecting multiple semantic errors in one file; and you may allow the compiler to stop processing with the first syntax error.

The file proj3test.zip unzips to a directory containing test files and the test script, proj3-test.pl, used just like the previous project scripts. These are not the only test files that will be used when grading, so do your own testing too. All test files will assume the 80% level of Project part I and the 80% level of Project part II. You should test your compiler as described in Project II.

Remember: you get credit for features successfully implemented. You do not get credit for attempting to do something; you get credit for the things that you can successfully demonstrate work.

Also remember: as always you are expected to do your own work on this assignment.

Finally: you should adequately document and structure your program. Remember you may be asked to explain this program orally during a subsequent `quiz.'

The official due time of this assignment is 11:59 pm on the due date above. Late submissions are accepted through Monday April 29 (the last day of classes).

You must turn in all source files (even the ones I gave you) and a Makefile for your compiler. Include a README file describing your code.