The document discusses the visitor design pattern used to traverse abstract syntax trees (ASTs). It explains that the visitor pattern avoids coupling visitor objects like printers to the AST node classes by defining an accept method in a common interface. Each visitor implements methods for each node type, and nodes call the appropriate method. This allows adding new visitor types without changing node classes. The document also covers double dispatch and how it enables dynamic binding to select methods based on both the receiver and argument types at runtime.
The document discusses the visitor design pattern used to traverse abstract syntax trees (ASTs). It explains that the visitor pattern avoids coupling visitor objects like printers to the AST node classes by defining an accept method in a common interface. Each visitor implements methods for each node type, and nodes call the appropriate method. This allows adding new visitor types without changing node classes. The document also covers double dispatch and how it enables dynamic binding to select methods based on both the receiver and argument types at runtime.
Fixed tree with known types of nodes to minimize coupling of processor objects (printing, decorating) to tree objects (AST accept walking)
Each visitor defines procedures to for every type of node visitation: visitProgramTree, visitBlockTree, etc.
Example: PrintVisitor visitor = new PrintVisitor(); tree.accept( visitor); return visitor.visitProgramTree( this ) // in tree object class method definition visitProgramTree: print( “Program”, tree ); // display line of the node visitKids( tree ); // tells each child node to accept the visitor
Apparent type: AST tree (variable and method declarations; super type where calls are legal) Actual type: RelOpTree (AST tree = new RelOpTree();) Ex: getSymbol method in the RelOpTree class not in AST class: AST t = new RelOpTree(); t.getSymbol(); // “cannot find symbol” since the apparent class of t doesn’t contain getSymbol Dynamic Binding (Dynamic Dispatch: mapping a message to method) calls to objects’s actual type at run time through a dispatch vector which references to methods of implementation. AST t = new RelOpTree(); t.accept( pv ); // the accept method in the actual class RelOpTree will be called.
Double Dispatch mechanism (instead of switch statements): call corresponding visitor accept method which then calls the corresponding visitor method, adding polymorphism capability
Switch statements coupled with cases can be omitted in adding all new cases
Single dispatch: an implementation is decided at runtime based on the single run type of the this object
Double dispatch: dynamically select method based on run-time type of receiver (single dispatch) and run-time type of argument, applying dynamic binding twice.
AST tree; PrintVisitor visitor = new PrintVisitor(); Tree.accept( visitor ); // the tree supposely references a ProgramTree object at the parser ( ProgramTree.accept( visitor ) // ProgramTree is passed along with the this argument ); visitor.visitProgramTree( this ); // dynamic dispatch determines at RUNTIME the programTree’s visitProgramTree to execute
Assignment 3: needs to update Compiler to implement a different visitor: OffsetVisitor and DrawOffsetVisitor.
OffsetVisitor.java: post order traversal. create hash table AST->Offset to record x offset, depth.
Setup: create an array to track the next available offset at each level. Assume max depth 100. If a node has no children, assign the node’s offset to the next available offset of its level. Update the next available offset (adding 2).