PA 2: IC Syntax and Semantic Analysis

Due: 10pm, Wednesday, Nov. 4

Intermediate checkpoints:
Learning Objectives

After this assignment, you will be able to:

Overview

In this programming assignment, you will implement the syntax and semantic analysis phases for IC. These phases require you to write the parser, the AST and symbol table packages, and the type checker.

Details

You are required to implement the following:

Command Line Invocation. As in PA 1, your compiler will be invoked with the program file name as an argument, with an optional “-d” flag:

    sbt "run <file.ic>"

Equivalently, ./icc.sh <file.ic> (included in the starter, as in PA 1) runs the same command with sbt’s log messages hidden; it takes exactly the same arguments as the compiler.

For scripted testing, the starter includes the same specTests runner as PA 1 (src/test/scala/tests/SpecTests.scala): sbt "specTests <dir>" runs your compiler on every .ic file under <dir> and compares the last line of its output to the matching .ic.expected file. Each test runs in a fresh JVM, so global state or a stray System.exit in your compiler cannot leak between tests. The starter’s tests/ directory holds one test suite per milestone:

The three suites contain the same programs; only the expected verdicts differ. Test against the suite for the milestone you are working toward, e.g. sbt "specTests tests/pa2-cp1". Later assignments add new suites to tests/ — and you should add your own tests there as well.

If you prefer working with a standalone jar (for manual runs or your own scripts), build one with sbt assembly and run it directly — this avoids sbt’s startup time on every file:

    java -jar target/icc.jar <file.ic>

The compiler will parse the input file, construct the AST and symbol tables, perform the semantic checks, and report any error it encounters. In addition, your compiler must support two command-line options to print internal information about the AST and the symbol tables:

  1. The “-printAST" option: prints a textual description of the constructed AST to System.out.

  2. The “-printSymTab" option: prints a textual description of the symbol tables to System.out.

These options should appear after the filename, as in:

    sbt "run <file.ic> -printAST"

or

    java -jar target/icc.jar <file.ic> -printAST

You can design your own textual description of the AST and symbol table structures, but make sure your output provides all important information and is easy to read.

Output Format.

As in the previous assignment, the last line of the output must be either

Success.

or

Failed.

depending on whether any errors were found. Some of the intermediate checkpoints will require other data to be printed, but the final submission should print no information other than error messages and that one last line. The checkpoints and the final submission are graded on Gradescope against staged versions of the course test suite, so complying with the Success./Failed. protocol matters.

Package Structure.

You should implement the new components of the compiler as the following sub-packages of the ic package:

The dot Utility.

You may find it helpful to use the graph visualization tools in the graphviz suite of tools for printing out information about the AST and the hierarchy of symbol tables. You can find information about this tool on the course web site. The most useful tool would be the dot program, which reads a textual specification for a graph and outputs a graphical image (in PDF format, jpg, or other image formats). For instance, the dot specification for the AST of the statement x = y + 1 is:

        digraph G {
          expr [label="="];
          lhs [label="x"];
          rhs [label="+"];
          leftop [label="y"];
          rightop [label="1"];
          expr -> lhs;
          expr -> rhs;
          rhs -> leftop;
          rhs -> rightop;
        }       

Running the dot tool on a file containing this description, as described in HW 4, will produce a graphical tree. Using dot is encouraged but not required.

Getting Started

I have provided a PA2 Starter project on GitHub. It contains a complete lexer for IC (an ic.slex equivalent to the PA 1 solution), the scup library source, and a stub ic.scup for you to grow into the full grammar. I suggest starting with this project even if you’d like to use your own scanner code.

Accept the GitHub invitation email to your team’s repository, which was created from this starter. Then do one piece of one-time setup in your clone: add the course starter repository as a second remote, so you can pull updates I release during the project — bug fixes now, and the PA 3 / PA 4 test suites and starter files later:

git remote add upstream https://github.com/williams-cs/cs434-f26-compiler-starter
git pull --no-rebase --allow-unrelated-histories upstream main

(--allow-unrelated-histories is needed only on this very first pull, because your repository was created from the starter template without its git history.) From then on, whenever a handout or announcement says new files are available, each team member just runs:

git pull upstream main

Releases only ever add files (tests/, handout/, lib/), so the pull will not conflict with your own work.

If you choose to use your own scanner code, you will want to change how it matches integer literals so your parser can properly handle “-”. More specifically, “-” can be interpreted in two ways in IC programs, as the binary minus operator or the unary minus operator. It will be the parser’s job to determine which is the appropriate interpretation. Thus “-10” should be scanned as two tokens, “-” and “10”, and the parser will represent that as the unary minus operator applied to 10 in the program’s parse tree.

One consequence of scanning “-” as a separate token: the most negative int is -2147483648, but the literal 2147483648 on its own is out of range. As the specification now states, the literal 2147483648 is legal only when it appears immediately under a unary minus; anywhere else it is an error.

Working with scup.

Your grammar goes in ic.scup, one rule per nonterminal, each alternative’s block building your AST nodes. Some advice from experience:

Documentation and Testing

Testing and Documentation are crucial moving forward. A few basic thoughts, now that PA 1 is behind us:

Submission

This is a substantial programming project. You have roughly 4 weeks to complete it — use your time wisely. There will be intermediate checkpoints along the way. Develop in your GitHub repository as usual, and submit your sbt project to Gradescope for each checkpoint and for the final deadline. Please be sure that your submission (and your GitHub repository) contains up-to-date versions of the following by the submission deadlines listed below:

As in the first assignment, double check your project when you submit each checkpoint by cloning out a fresh copy and verifying that it works.

Schedule

These milestones are the minimal requirements for the checkpoints. You are of course welcome (if not strongly encouraged) to do more by each deadline.

Tutorial meetings, the week of the first checkpoint:

Your parser must successfully parse valid IC programs and report syntax errors in bad ones. Come to the meetings with an initial design of your AST package, including: 1) The list of operations present in your root node class, and 2) a brief overview of the class hierarchy of node types. This need not be very detailed, but it should at least demonstrate that you have begun to think about how to lay out your ASTs.

Your project should include at least 20–30 test cases, and ideally more.

10pm, Wednesday, Oct. 21:

Your parser must generate ASTs for programs, and you must support the -printAST command-line option. The tests/pa2-cp1 suite encodes exactly what this checkpoint’s Gradescope run expects: sbt "specTests tests/pa2-cp1".

10pm, Wednesday, Oct. 28:

All of the above, plus your compiler must generate the symbol tables and perform name resolution for variable uses. That is, each variable access should be resolved to either a local variable access or a field access. Variable name resolution should be done as a separate pass over the AST after all of the symbol table information has been constructed. You may wish to resolve class names to their declarations at the same time as variable resolution. Otherwise, you’ll likely need a separate pass to do that before type checking. (Field and method name resolution will take place during the subsequent type checking phase.)

At this point your compiler should implement the -printSymTab option. Additionally, you may wish to extend your AST printer to print out some indication of how each variable access in the program has been resolved.

This checkpoint’s suite is tests/pa2-cp2.

10pm, Wednesday, Nov. 4:

PA 2 due: sbt "specTests tests/pa2" should report no failures.