In assignment 9, you will implement the semantic analysis phase of the WLPP compiler for which you wrote a scanner in assignment 6 and a parser in assignment 8. In particular, it is the job of this assignment to catch all remaining compile-time errors in the source program; a program that makes it through this phase of compilation is guaranteed to be free of compile-time errors.
Note: In assignments 10 and 11, you will build on your solution for this assignment. In order to proceed to those assignments, you must, at minimum, complete the part of this assignment devoted to building the symbol table (i.e., you must at least do A9P1). As long as your solution does at least this much correctly, you will not be disadvantaged when you start assignment 10.
The specific semantic rules that must be enforced are those outlined in the "Context-sensitive Syntax" section of the WLPP Specification. A summary of the semantic rules of WLPP related to types is available for download, and you should use it as a reference. The summary contains formal descriptions of type rules for WLPP; although these should match the English descriptions on the WLPP specification page, the WLPP specification should be considered the definitive resource. Please report any discrepancies between the two documents.
You must test your semantic analyzer yourself in Unix. To test your semantic analyzer, you will need .wlppi files as generated by the parser specified in A8P4. In case you do not wish to use your own parser, you can use one that we have provided. Invoke it using the command java cs241.WLPPParse.
Starting from a .wlpp source file, the complete sequence of commands to test your semantic analyzer on it is
java cs241.WLPPScan < foo.wlpp > foo.scanned java cs241.WLPPParse < foo.scanned > foo.wlppi mzscheme wlppgen.ss < foo.wlppi
OR
java cs241.WLPPScan < foo.wlpp > foo.scanned java cs241.WLPPParse < foo.scanned > foo.wlppi ./wlppgen < foo.wlppi
OR
java cs241.WLPPScan < foo.wlpp > foo.scanned java cs241.WLPPParse < foo.scanned > foo.wlppi java WLPPGen < foo.wlppi
This can be abbreviated to
cat foo.wlpp | java cs241.WLPPScan | java cs241.WLPPParse | mzscheme wlppgen.ss
OR
cat foo.wlpp | java cs241.WLPPScan | java cs241.WLPPParse | ./wlppgen
OR
cat foo.wlpp | java cs241.WLPPScan | java cs241.WLPPParse | java WLPPGen
Tuesday, November 22, 2011 at 5:00 pm
You are given a starter program (wlppgen.ss or wlppgen.cc or WLPPGen.java) that reads a well-formed .wlppi file and builds a WLPP parse tree from it. The problems that follow ask you to add the various components of the semantic analysis phase of compilation to this skeleton. Each solution should build on previous solutions: for each n, your solution for A9Qn should also be a solution for A9Qn-1.
Modify the starter program so that it collects information about declarations and types. To solve this problem, you must build a symbol table that contains an entry for every declared identifier in the program (including the parameters of wain). If an identifier is declared more than once, or is used without first having been declared, you must output a string containing ERROR to standard error. You are encouraged, but not required, to make your error message more descriptive than this, as long as the string ERROR occurs. These are the only errors you need to check for this problem.
If the input program contains no errors of the kind described above, then you must output a symbol table to standard error containing the name and type of every identifier declared in the program (including the parameters of wain). The symbol table must be formatted as follows: it contains one line for each declared identifer, and each line consists of the (case-sensitive) name of the identifier, followed by a space, followed by the identifier's type (int or int*). The order in which the entries in the symbol table occur is not important.
Extend your solution for problem 1 to check for type errors in expressions. In particular, for every subtree with an expr or lvalue node at its root, compute the type associated with that subtree, or output a string containing ERROR if the expression cannot be validly typed. If every expression in the program has a valid type, and there are no declaration errors, then your program should produce no output.
For example, in a node corresponding to the rule
expr → expr PLUS termif the expr and term on the right-hand side both have type int*, then your program should output ERROR to standard error. Otherwise, the expr on the left-hand side is well-typed, and its type is as given on the semantic rules handout.
Extend your solution for problem 2 to check for type errors in statements and tests. These are program elements that do not themselves have types, but demand that some of their subelements be given particular types. For example, println can only be used with type int, and delete [] can only be used with type int*.
By the end of this problem, your program should check and enforce every type rule given on the semantic rules handout. If a source program contains any kind of semantic error, you must output a string containing ERROR to standard error. If the source program is free of semantic errors, your program should produce no output.