CS 496 - Assignment 4

Due: Tuesday, April 15th by 11:59 PM

Abstract Syntax Trees

Abstract syntax trees, or ASTs, are a useful intermediate data structure for representing an input program.  The basic idea of an AST is that it pares down the information in the full parse tree to the minimum information needed to represent the full meaningof the program.

Getting Started

Use your implementation of Assignment 3 as a starting point.

Create a new package (underneath the src folder) called edu.ycp.cs496.spl.ast.  Add the following files:





Also, add the following source file to the edu.ycp.cs496.spl package:


Main.java (replacing the old Main.java)

Add the following method just below the definitions of the two makeSymbol methods in Lexer.jflex:

public Location getCurrentLocation() {
return new Location(yyline);

Add the following methods to the parser code section of Parser.cup, just below the definition of the syntax_error method:

// 0 is the last symbol on the right hand side of the production
// 1 is the second-to-last symbol on the right hand side of the production
// ...etc...
public Symbol getSymbol(int which) {
Symbol sym = (Symbol) stack.elementAt(tos - which);
return sym;

public Location getCurrentLocation() {
Lexer lexer = (Lexer) getScanner();
return lexer.getCurrentLocation();

Your Task

You have two tasks:

1. Add semantic actions to your parser so that when an entire SPL source file is parsed, its attribute value is an ASTNode which is the root of an abstract syntax tree representing the entire program.

2. Write code to traverse the AST of the program and output the source code of the input program.  This is a source to source transformation.  If the AST contains enough information to regenerate the original program (or a program equivalent to the original program), then it definitely contains enough information to translate to a target language.

You may find the AST-based solution to lab 2 to be useful as a guide for writing semantic actions to build an AST.

You may modify the ASTNode (and related) classes in any way you see fit.  I used them in my own implementation, but you may want to use a different AST representation.

Your code to output the source code of the input program should be located in the prettyPrint method in the Main class.

At a minimum, you can simply output the program in an arbitrary format: for example, one token per line.

For extra credit, you can pretty print the program by outputting it with correct indentation.


To implement the prettyPrint method, create a class called PrettyPrintingVisitor as a subclass of RecursiveASTNodeVisitor.  The code of the prettyPrint method should be something like:

protected static void prettyPrint(ASTNode parseTree) {
PrettyPrintingVisitor ppv = new PrettyPrintingVisitor(System.out);

Be careful of how you handle operator precedence when outputting the program!  For example: say that the input program is

func main() : void
println((3 + 4) * 5);

When you output the program, you should NOT remove the parentheses:

// this is wrong!
func main() : void
println(3 + 4 * 5);

A very easy way to handle operator precedence is to simply output all expressions (and subexpressions) using explicit parentheses.


Create a zip file containing just the contents of the src directory and upload it to Marmoset as project 4: