Programming Assignment 5

Due by Nov 29 30, 2021 at 11pm.

Overview

For this assignment you will write a type checker for b programs represented as abstract-syntax trees. Your main task will be to write type checking methods for the nodes of the AST.
In addition you will need to:

  1. Modify P5.java.
  2. Write two test inputs: typeErrors.b and test.b to test your new code.

Getting Started

Download zip file

Start the project by downloading p5.zip

The files:

Specifications

Type Checking

The type checker will determine the type of every expression represented in the abstract-syntax tree and will use that information to identify type errors. In the b language we have the following types:

int, bool, void (as function return types only), strings, struct types, and function types.

A struct type includes the name of the struct (i.e., when it was declared/defined). A function type includes the types of the parameters and the return type.

The operators in the b language are divided into the following categories:

The type rules of the b language are as follows:

You must implement your type checker by writing appropriate member methods for the different subclasses of ASTnode. Your type checker should find all of the type errors described in the following table; it must report the specified position of the error, and it must give exactly the specified error message. (Each message should appear on a single line, rather than how it is formatted in the following table.)

Type of Error Error Message Position to Report
Writing a function; e.g., "print << f", where f is a function name. Attempt to write function 1st character of the function name.
Writing a struct name; e.g., "print << P", where P is the name of a struct type. Attempt to write struct name 1st character of the struct name.
Writing a struct variable; e.g., "print << p", where p is a variable declared to be of a struct type. Attempt to write struct variable 1st character of the struct variable.
Writing a void value (note: this can only happen if there is an attempt to write the return value from a void function); e.g., "print << f()", where f is a void function. Attempt to write void 1st character of the function name.
Reading a function: e.g., "receive >> f", where f is a function name. Attempt to read function 1st character of the function name.
Reading a struct name; e.g., "receive >> P", where P is the name of a struct type. Attempt to read struct name 1st character of the struct name.
Reading a struct variable; e.g., "receive >> p", where p is a variable declared to be of a struct type. Attempt to read struct variable 1st character of the struct variable.
Calling something other than a function; e.g., "x();", where x is not a function name. Note: In this case, you should not type-check the actual parameters. Attempt to call non-function 1st character of the variable name.
Calling a function with the wrong number of arguments. Note: In this case, you should not type-check the actual parameters. Function call with wrong number of args 1st character of the function name.
Calling a function with an argument of the wrong type. Note: you should only check for this error if the number of arguments is correct. If there are several arguments with the wrong type, you must give an error message for each such argument. Type of actual does not match type of formal 1st character of the first identifier or literal in the actual parameter.
Returning from a non-void function with a plain ret statement (i.e., one that does not return a value). Missing return value 0,0
Returning a value from a void function. Return with value in void function 1st character of the first identifier or literal in the returned expression.
Returning a value of the wrong type from a non-void function. Bad return value 1st character of the first identifier or literal in the returned expression.
Applying an arithmetic operator (+, -, *, /) to an operand with type other than int. Note: this includes the ++ and -- operators. Arithmetic operator applied to non-numeric operand 1st character of the first identifier or literal in an operand that is an expression of the wrong type.
Applying a relational operator (<, >, <=, >=) to an operand with type other than int. Relational operator applied to non-numeric operand 1st character of the first identifier or literal in an operand that is an expression of the wrong type.
Applying a logical operator (!, &&, ||) to an operand with type other than bool. Logical operator applied to non-bool operand 1st character of the first identifier or literal in an operand that is an expression of the wrong type.
Using a non-bool expression as the condition of an if. Non-bool expression used as if condition 1st character of the first identifier or literal in the condition.
Using a non-bool expression as the condition of a while. Non-bool expression used as while condition 1st character of the first identifier or literal in the condition.
Using a non-integer expression as the times clause of a repeat. Non-integer expression used as repeat clause 1st character of the first identifier or literal in the condition.
Applying an equality operator (==, !=) to operands of two different types (e.g., "j == tru", where j is of type int), or assigning a value of one type to a variable of another type (e.g., "j = tru", where j is of type int). Type mismatch 1st character of the first identifier or literal in the left-hand operand.
Applying an equality operator (==, !=) to void function operands (e.g., "f() == g()", where f and g are functions whose return type is void). Equality operator applied to void functions 1st character of the first function name.
Comparing two functions for equality, e.g., "f == g" or "f != g", where f and g are function names. Equality operator applied to functions 1st character of the first function name.
Comparing two struct names for equality, e.g., "A == B" or "A != B", where A and B are the names of struct types. Equality operator applied to struct names 1st character of the first struct name.
Comparing two struct variables for equality, e.g., "a == b" or "a != b", where a and a are variables declared to be of struct types. Equality operator applied to struct variables 1st character of the first struct variable.
Assigning a function to a function; e.g., "f = g;", where f and g are function names. Function assignment 1st character of the first function name.
Assigning a struct name to a struct name; e.g., "A = B;", where A and B are the names of struct types. Struct name assignment 1st character of the first struct name.
Assigning a struct variable to a struct variable; e.g., "a = b;", where a and b are variables declared to be of struct types. Struct variable assignment 1st character of the first struct variable.

Preventing Cascading Errors

A single type error in an expression or statement should not trigger multiple error messages. For example, assume that P is the name of a struct type, p is a variable declared to be of struct type P, and f is a function that has one integer parameter and returns a bool. Each of the following should cause only one error message:

print << P + 1     // P + 1 is an error; the write is OK
(tru + 3) * 4      // tru + 3 is an error; the * is OK
tru && (fls || 3)  // fls || 3 is an error; the && is OK
f("a" * 4);        // "a" * 4 is an error; the call is OK
1 + p();           // p() is an error; the + is OK
(tru + 3) == x     // tru + 3 is an error; the == is OK
                   // regardless of the type of x

One way to accomplish this is to use a special ErrorType for expressions that contain type errors. In the first example above, the type given to (tru + 3) should be ErrorType, and the type-check method for the multiplication node should not report "Arithmetic operator applied to non-numeric operand" for the first operand. But note that the following should each cause two error messages (assuming the same declarations of f as above):

tru + "hello"  // one error for each of the non-int operands of the +
1 + f(tru)     // one for the bad arg type and one for the 2nd operand of the +
1 + f(1, 2)    // one for the wrong number of args and one for the 2nd operand of the +
ret 3+tru;     // in a void function: one error for the 2nd operand to +
               // and one for returning a value

To provide some help with this issue, here is an example input file, along with the corresponding error messages. (Note: This is not meant to a complete test of the type checker; it is provided merely to help you understand some of the messages you need to report, and to help you find small typos in your error messages. If you run your program on the example file and put the output into a new file, you can use the Linux utility diff to compare your file of error messages with the one supplied here. This will help both to make sure that your code finds the errors it is supposed to find, and to uncover small typos you may have made in the error messages.)

Other Tasks

P5.java

The main program, P5.java, will be similar to P4.java, except that if it calls the name analyzer and there are no errors, it will then call the type checker.

Writing Test Inputs

You will need to write two input files to test your code:

  1. typeErrors.b should contain code with errors detected by the type checker. For every type error listed in the table above, you should include an instance of that error for each of the relevant operators, and in each part of a program where the error can occur (e.g., in a top-level statement, in a statement inside a while loop, etc).

  2. test.b should contain code with no errors that exercises all of the type-check methods that you wrote for the different AST nodes. This means that it should include (good) examples of every kind of statement and expression.

Note that your typeErrors.b should cause error messages to be output, so to know whether your type checker behaves correctly, you will need to know what output to expect.

Part of the grade depends on how thoroughly the input files you used, test the program. Make sure that you submit the input files you used to test your program.

Some Advice

Here are few words of advice about various issues that come up in the assignment:

Handing in

Please read the following handing in instructions carefully. You will be needed to submit the the entire working folder as a compressed file as given below.

lastname.firstname.lastname.firstname.P5.zip
+---+ deps/
+---+ ast.java
+---+ b.cup
+---+ b.jlex
+---+ DuplicateSymException.java
+---+ EmptySymTableException.java
+---+ WrongArgumentException.java
+---+ ErrMsg.java
+---+ Makefile
+---+ P5.java
+---+ Symb.java
+---+ SymTable.java
+---+ Type.java
+---+ typeErrors.b
+---+ test.b
+---+ lastname.firstname.lastname.firstname.P5.pdf


Please ensure that you do not include any extra sub-directories. Do not turn in any .class files. If you accidentally turn in (or create) extra files or subdirectories, please remove them from your submission zip file.

If you are working in a pair, have only one member submit the program. Include both persons' name as given above. Also, mention the teammate's name as a comment while submitting the assignment on canvas. If you are working by yourself, submit the program with only your name in the zip and pdf files.