16
Compiler Construction Computations on ASTs Lennart Andersson Revision 2011-02-07 2011 Compiler Construction 2011 F06-1 Hand-coded parser without actions void stmt() { switch(token) { case IF: accept(IF); expr(); accept(THEN); stmt(); break; case ID: accept(ID); accept(EQ); expr(); break; default: error(); } } Compiler Construction 2011 F06-2 Hand-coded parser with semantic actions Stmt stmt() { switch(token.kind) { case IF: accept(IF); Expr e = expr(); accept(THEN); Stmt s = stmt(); return new IfStmt(e, s); case ID: IdExpr id = new IdExpr(token.image); accept(ID); accept(EQ); Expr e = expr(); return new Assignment(id, e); } } Compiler Construction 2011 F06-3 .jj file with actions Stmt stmt() : {Stmt s;} { (s = ifStmt() | s = assignment()) {return s;} } IfStmt ifStmt() : {Expr e; Stmt s;} { "if" e = expr() "then" s = stmt() {return new IfStmt(e, s);} } Assignment assignment() : {Token t; Expr e;} { t = <ID> "=" e = expr() {return new Assignment(new IdExpr(t.image), e);} } Compiler Construction 2011 F06-4

Hand-coded parser with semantic actions .jj le with actions

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

Compiler ConstructionComputations on ASTs

Lennart Andersson

Revision 2011-02-07

2011

Compiler Construction 2011 F06-1

Hand-coded parser without actions

void stmt() {switch(token) {case IF:accept(IF); expr(); accept(THEN); stmt();break;

case ID:accept(ID); accept(EQ); expr();break;

default:error();

}}

Compiler Construction 2011 F06-2

Hand-coded parser with semantic actions

Stmt stmt() {switch(token.kind) {case IF:accept(IF);Expr e = expr();accept(THEN);Stmt s = stmt();return new IfStmt(e, s);

case ID:IdExpr id = new IdExpr(token.image);accept(ID);accept(EQ);Expr e = expr();return new Assignment(id, e);

}}

Compiler Construction 2011 F06-3

.jj file with actions

Stmt stmt() : {Stmt s;}{

(s = ifStmt() | s = assignment()) {return s;}}IfStmt ifStmt() : {Expr e; Stmt s;}{

"if" e = expr() "then" s = stmt(){return new IfStmt(e, s);}

}Assignment assignment() : {Token t; Expr e;}{

t = <ID> "=" e = expr(){return new Assignment(new IdExpr(t.image), e);}

}

Compiler Construction 2011 F06-4

Building trees with JJTree

I jjtree is a preprocessor to javacc using a .jjt file

I grammar productions with tree building directives ”#. . . ”

I jjtree generates a .jj file with tree building actions

I builds a parse tree by default (Assignment 2)

I with option NODE DEFAULT VOID=true building requires #directives

Compiler Construction 2011 F06-5

jjtree stack

jjtree has a stack for collecting the children of a node. It is easy to

I build a node corresponding to a nonterminal

I ignore building a node for a terminal/nonterminal

I build List and Opt nodes

I build “left recursive” trees when the grammar is right recursive

I build trees when the grammar has common prefixes

Compiler Construction 2011 F06-6

Building an AST node for a nonterminal

Add a # directive to the corresponding AST class

void ifStmt() #IfStmt : { } {<IF> expr() <THEN> stmt() <FI>

}

IfStmt: Stmt ::= Expr Stmt

I the current top of stack is marked

I expr() pushes an Expr node

I stmt() pushes a Stmt node

I a new IfStmt node is created

I the nodes above the mark will be popped and

I become children of the IfStmt node

I this node will be pushed on the stack

Compiler Construction 2011 F06-7

Skipping nodes

A terminal or nonterminal without a # directive will be skipped

void ifStmt(): { } {<IF> expr() <THEN> stmt() <FI>

}

I expr() pushes an Expr node if it has a # directive

I no node is pushed for THEN

I stmt() pushes a Stmt node if it has a # directive

I no nodes will be popped by this production

I no IfStmt is created

I this is probably an error

Compiler Construction 2011 F06-8

Building a token node

IdExpr: Expr ::= <ID:String>;

void idExpr() #IdExpr : {Token t;}{

t = <ID>{jjtThis.setID(t.image);}

}

I jjtThis refers to the new IdExpr node

I the setID(String) method has been generated by jastadd

Compiler Construction 2011 F06-9

Building a List node

void program() #Program : {}{

"begin" ([stmt() (";" stmt())*]) #List(true) "end"}Program ::= Stmt*;

I a marker for Program is put on the stackI "begin" is parsed without pushing anythingI a marker for List is put on the stackI a node is pushed on the stack for each stmtI the parser reaches #List(true) and all nodes above the List

marker are popped and become children of a new List nodeI this node is pushed on the stackI the "end" token is acceptedI the only node above the Program mark is popped an becomes

the child of a new Program nodeI this node is pushed on the stack

Compiler Construction 2011 F06-10

Why #List(true)?I the List will be built even it is emptyI with #List(false) nothing will be build for an empty listI with jastadd empty trees must be generated

Program

List

Stmt Stmt...

0 or more

Compiler Construction 2011 F06-11

Optional node

void classDecl() #ClassDecl : { }"class" classId() (["extends" classId()]) #Opt(true)ClassBody()

I an Opt node will be built even if there is no extends clauseI with jastadd empty Opt nodes must be generated

ClassDecl

OptClassId ClassBody

...may be empty

Compiler Construction 2011 F06-12

Building left-recursive trees with jjtree

abstract Expr;BinExpr: Expr ::= Left:Expr Right:Expr;Mul: BinExpr;Div: BinExpr;

void expr() : { }{

factor()( "*" factor() #Mul(2)| "/" factor() #Div(2))*

}

I #Mul(2) builds a Mul node

I pops two nodes from the stack

I and turns them into children

I pushes the new Mul node to the stack

Compiler Construction 2011 F06-13

Returning the AST to the client

Creating and using the parser

Parser parser = new Parser(...);Start start = parser.start();

jjtree specification

Start start() #Start : { }{

expr();{return jjtThis;}

}

jjtThis refers to the Start node created by start()

Compiler Construction 2011 F06-14

Semantic analysis

scannersource code

tokens

parser

AST builder

parse tree

AST

semanticanalysis

AST with attributes

codegeneration

Compiler Construction 2011 F06-15

Examples of computations

I Name analysis: find the declaration of an identifier

I Type analysis: compute the type of an expression

I Expression evaluation: compute the value of a constantexpression

I Code generation: compute an intermediate coderepresentation of a program

I Unparsing: compute a text representation of a program

Compiler Construction 2011 F06-16

An expression evaluator

Abstract grammar

abstract Expr;abstract BinExpr : Expr ::= Left:Expr Right:Expr;Add : BinExpr;Sub : BinExpr;IntExpr : Expr ::= <INT:String>;

Compiler Construction 2011 F06-17

Evaluator implementation

abstract class Expr {abstract int value();

}abstract class BinExpr extends Expr { }class Add extends BinExpr {

}class Sub extends BinExpr {

}class IntExpr extends Expr {

}Compiler Construction 2011 F06-18

An unparser

abstract Stmt;IfStmt : Stmt ::= Cond:Expr Then:Stmt [Else:Stmt];

abstract class Stmt {abstract void unparse(PrintStream s, String indent);

}class IfStmt extends Stmt {

void unparse(PrintStream s, String indent) {s.print(indent);s.print("if ");getCond().unparse(s, indent);s.println(" then ");getThen().unparse(s, indent + " ");if (hasElse()) {

s.println(" else ");getElse().unparse(s, indent + " ");

}}

}Compiler Construction 2011 F06-20

The Interpreter design pattern

Intent

Given a language, define a representation for its grammaralong with an interpreter that uses the representation tointerpret sentences in the language.

Example

“Computer” in the OMD course (eda061)

Compiler Construction 2011 F06-21

Interpreter pattern

Expressioninterpret(Context)

TerminalExpressioninterpret(Context)

NonTerminalExpressioninterpret(Context)

*

Compiler Construction 2011 F06-22

Example applications

The expression evaluator

I value() plays the role of the interpret method

I no Context was needed (but would be needed if we had alanguage with identifiers)

I Expr plays the role of the abstract Expression

I IntExpr plays the role of TerminalExpression

I Add and Sub play the role of NonTerminalExpression

The unparser

I unparse() plays the role of the interpret method

I Context consists of two parameters: (PrintStream s, Stringindent)

I Stmt and Expr play the role of abstract Expressions

I IfStmt plays the role of NonTerminalExpression

I . . .

Compiler Construction 2011 F06-23

Count the number of identifiers

Abstract grammar

abstract Stmt;IfStmt : Stmt ::= Cond:Expr Then:Stmt [Else:Stmt];...abstract Expr;abstract BinExpr : Expr ::= Left:Expr Right:Expr;Add : BinExpr;Sub : BinExpr;IntExpr : Expr ::= <INT:String>;IdExpr : Expr ::= <ID:String>;...

Compiler Construction 2011 F06-24

Implementation of the counter

class ASTNode {int countIds() {int count = 0;for (int k = 0; k < getNumChild(); k++)

count += getChild(k).countIds();return count;

}}class IdExp extends Expr {

int countIds() {return 1;}

}

Compiler Construction 2011 F06-25

The Composite design pattern

Intent

Compose objects into tree structures to representpart-whole hierarchies. Composite lets clients treatindividual objects and compositions of objects uniformly.

Example

Any recursive data type.

Compiler Construction 2011 F06-26

The Composite design pattern

Componentoperation(Context)

Leafoperation(Context)

Nodeoperation(Context)

* children

for all g in childreng.operation(context)

Compiler Construction 2011 F06-27

Example applications

The counter

I countIds() plays the role of the operation method

I ASTNode plays the role of Component

I IdExp plays the role of Leaf

I Other classes play the role of Node. Rather thanimplementing count() in each of these classes, they inherit adefault implementation of ASTNode.

Other typical applications

I Graphical objects that can be grouped hierarchically

I Line, Rectangle, Text, etc. play the role of Leaf

I Group plays the role of Node

I paint(Graphics) plays the role of an operation method

Compiler Construction 2011 F06-28

Modularization

How can new functionality be added to the AST classes?

I methods, instance variables, ...

Modify the AST classes?

I not modular

I risky to modify generated code— what happens if we need to regenerate the AST classes?

Modular techniques

I Intertype declarations (Static Aspect-Oriented Programming)

I Visitor pattern

Compiler Construction 2011 F06-29

Aspect-Oriented Programming“Cross-cutting concerns”

I One aspect concerns many classes

I The aspect cross-cuts the normal language constructs andleads to tangled code

AOP techniques

I aspect constructs can modularize cross-cutting code, therebyavoiding tangled code

I A special kind of compiler, an aspect weaver, weaves togetheraspect code and ordinary code during compilation AOPsystems

AOP systems

I AspectJ, http://www.eclipse.org/aspectj/, (from XeroxPARC)

I Hyper/J, http://www.research.ibm.com/hyperspace/ (IBM)

I AOSD homepage: http://aosd.net

Compiler Construction 2011 F06-30

Example of Tangled CodeImplementation of expression evaluator and unparser

abstract class Expr {abstract int value();abstract void unparse(Stream s, String indent);

}abstract class BinExpr extends Expr {

Expr getLeft() { ... }Expr getRight() { ... }

}class Add extends BinExpr {

int value() { ... }void unparse(Stream s, String indent) { ... }

}class IntExpr extends Expr {

String getINT() { ... }int value() { ... }void unparse(Stream s, String indent) { ... }

}Compiler Construction 2011 F06-31

Intertype declarations (Static AOP)

Partial class definitions can be written in different modulesA preprocessor can weave together the code to complete (tangled)classes that are compiled by an ordinary compiler.

Compiler Construction 2011 F06-32

Example

class Add extends Expr {Expr expr1, expr2;void print() {expr1.print();System.out.print(’+’);expr2.print();

}}class IntExpr extends Expr {

int value;void print() {System.out.print(value);

}}

Compiler Construction 2011 F06-33

Extract functionality

aspect Print {class Add extends Expr {

Expr expr1, expr2;void Add.print() {

expr1.print();System.out.print(’+’);expr2.print();

}}class IntExpr extends Expr {

int value;void IntExpr.print() {

System.out.print(value);}

}}

Compiler Construction 2011 F06-34

More functionality

aspect Value {class Add extends Expr {

Expr expr1, expr2;int Add.value() {

int n1 = expr1.value();int n2 = expr2.value();return n1+n2:

}}class IntExpr extends Expr {

int value;int IntExpr.value() {

return value;}

}}

Compiler Construction 2011 F06-35

Weave

java -jar jastadd2.jar Expr.ast Print.jadd Value.jadd

produces

class Add extends Expr {Expr expr1, expr2;void print() {expr1.print();System.out.print(’+’);expr2.print();

}int value() {int n1 = expr1.value();int n2 = expr2.value();return n1+n2:

}}

Compiler Construction 2011 F06-36

The JastAdd system

Typed AST

I Generates AST classes with typed access methods from anabstract grammar

Intertype declarations

I partial class definitions for AST classes are written in .jaddfiles.JastAdd weaves in these partial definitions into the generatedAST classes

Rewritable Reference Attribute Grammars

I Declarative computations using attributes and equations

I Declarative transformations of the AST

I Guest lecture by Emma Soderberg.

Compiler Construction 2011 F06-37

Modularizing our example

abstract Expr;BinExpr: Expr ::= Expr Expr;Add: BinExpr;IntExpr: Expr;

aspect Value {abstract int Expr.value();int Add.value() {...}int IntExpr.value() {...}

aspect Print {abstract void Expr.print();void Add.print() {...}void IntExpr.print() {...}

Expr.ast Value.jadd

Print.jadd

jastadd

Expr.javaAdd.java

Compiler Construction 2011 F06-38

JastAdd aspect for the expression evaluator

aspect Value {abstract int Expr.value();int Add.value() {return getLeft().value() + getRight().value();

}

int Sub.value() {return getLeft().value() - getRight().value();

}

int IntExpr.value() {return String.parseInt(getINT());

}}

Compiler Construction 2011 F06-39

What parts of AST classes can be factored out to .jaddfiles?

I Methods

I Instance variables

I “implements” clauses

I “import” clauses

Compiler Construction 2011 F06-40

Example (instance variable)

Add an integer representation of INT values (in addition to theString representation)

aspect IntValue {int Expr.value;...

}

Compiler Construction 2011 F06-41

Visitors

How to modularize in Java (or any other OO language) if we donot have access to AOP mechanisms?

Compiler Construction 2011 F06-42

Example

How can we factor out the code for print()?

class Add extends Expr {Expr expr1, expr2;void print() {expr1.print();System.out.print(’+’);expr2.print();

}}class IntExpr extends Expr {

int value;void print() {System.out.print(value);

}}

Compiler Construction 2011 F06-43

Move functionality

class Add extends Expr { class Visitor {Expr expr1, expr2; void visit(Add node) {void print() { node.expr1.print();expr1.print(); System.out.print(’+’);System.out.print(’+’); node.expr2.print();expr2.print(); }

}}class IntExpr extends Expr { void visit(IntExpr node){

int value; System.out.print(void print() { node.value);System.out.print(value); }

}} }

Compiler Construction 2011 F06-44

Missing functionality

class Add extends Expr { class Visitor {Expr expr1, expr2; void visit(Add node) {

node.expr1.print();System.out.print(’+’);node.expr2.print();

}}

}class IntExpr extends Expr { void visit(IntExpr node){

int value; System.out.print(node.value);

}

} }

Compiler Construction 2011 F06-45

Delegate

class Add extends Expr { class Visitor {Expr expr1, expr2; void visit(Add node) {void accept(Visitor v) { node.expr1.accept(this);v.visit(this); System.out.print(’+’);

node.expr2.accept(this);}

}}class IntExpr extends Expr { void visit(IntExpr node){

int value; System.out.print(void accept(Visitor v) { node.value);v.visit(this); }

}} }

Compiler Construction 2011 F06-46

Why not call visit directly?

class Add extends Expr { class Visitor {Expr expr1, expr2; void visit(Add node) {void accept(Visitor v) { visit(node.expr1);v.visit(this); System.out.print(’+’);

visit(node.expr2);}

}}class IntExpr extends Expr { void visit(IntExpr node){

int value; System.out.print(void accept(Visitor v) { node.value);v.visit(this); }

}} }

Compiler Construction 2011 F06-47

Generalise

class Add extends Expr { interface Visitor {Expr expr1, expr2; void visit(Add node);void accept(Visitor v) { void visit(IntExpr node);v.visit(this); }

class PrintVisitor} implements Visitor {

}class IntExpr extends Expr { void visit(Add node) {

int value; node.expr1.accept(this);void accept(Visitor v) { System.out.print(’+’);v.visit(this); node.expr1.accept(this);

} } ...} }

Compiler Construction 2011 F06-48

Generalise with parameter and return

interface Visitor {Object visit(Add node, Object data);Object visit(IntExpr node, Object data);

}

class IntExpr extends Expr {Expr expr1, expr2;Object accept(Visitor v, Object data) {return v.visit(this, data); }

}}

class PrintVisitor extends Visitor {Object visit(IntExpr node, Object data) {System.out.print(node.value);return null;

}}Compiler Construction 2011 F06-49

Another visitor

class ValueVisitor extends Visitor {Object visit(Add node, Object data) {int n1 = (Integer) node.expr1.accept(this, data);int n2 = (Integer) node.expr2.accept(this, data);return new Integer(n1+n2);

}Object visit(IntExpr node, Object data) {return new Integer(node.value);

}}

Expr expr = new Add(... );expr.accept(new PrintVisitor(), null);int value = expr.accept(new ValueVisitor(), null);

Compiler Construction 2011 F06-50

The Visitor Pattern

Intent

Represent an operation to be performed on the elementsof an object structure.Visitor lets you define a new operation without changingthe classes of the elements on which it operates.

Compiler Construction 2011 F06-51

Sketch

interface Visitor {  Object visit(Add, Object);  Object visit(Sub, Object);//one visit method for each AST class

class ValueVisitor implements Visitor {Object visit(Add, Object) { ... }Object visit(Sub, Object) { ... }

Visitor.java

ValueVistor.java

UnparseVisitor.java

Expr

class Addaccept(...)

each AST class has anObject accept(Visitor, Object)method

delegates computation

class UnparseVisitor implements Visitor {Object visit(Add, Object) { ... }Object visit(Sub, Object) { ... }

Sub

Compiler Construction 2011 F06-52

Interface Visitor

interface Visitor {Object visit(Add node, Object data);Object visit(Sub node, Object data);Object visit(IntExpr node, Object data);...

}

I The visit method is overloaded for different AST argumenttypes

I Each method returns an untyped object

I Each method has an untyped argument (data)

Compiler Construction 2011 F06-53

Visitor support in AST nodesMethod accept that delegates the computation to a Visitor

abstract class Expr {abstract Object accept(Visitor v, Object data);

}abstract class BinExpr extends Expr {

Object accept(Visitor v, Object data) {return v.visit(this, data);

}}class Add extends BinExpr {

Object accept(Visitor v, Object data) {return v.visit(this, data);

}}class IntExpr extends Expr {

Object accept(Visitor v, Object data) {return v.visit(this, data);

}}

Compiler Construction 2011 F06-54

The evaluator as a visitor

class Evaluator implements Visitor {

}

Compiler Construction 2011 F06-55

To note about this example . . .

I the argument data was not needed for the computation

I the result (of type int) had to be wrapped in an Integer object

I Casts are needed in some places.

I the method value hides implementation detail and makes iteasy to call the visitor from a client

Example of use

Expr e = ...;print("the value is ");println(Evaluator.value(e));

Compiler Construction 2011 F06-56

The unparser as a visitor

class Unparser implements Visitor {

}

Compiler Construction 2011 F06-57

To note about this example . . .

I the argument data is used for representing the argumentindent and needs to be cast to String before use

I the original argument PrintStream s is stored in the Visitorobject and can be used directly by all visit methods

I the result value is not used

I the method unparse hides implementation detail and makes iteasy to call the visitor from a client

Example of use

Program p = ...;PrintStream s = ...;Unparser.unparse(p, s);

Compiler Construction 2011 F06-58

One more example

Count the number of identifiers in a program

abstract Stmt;IfStmt : Stmt ::= Cond:Expr Then:Stmt [Else:Stmt];...abstract Expr;abstract BinExpr : Expr ::= Left:Expr Right:Expr;Add : BinExpr ::= ;Sub : BinExpr ::= ;Int : Expr ::= <INT:String>;IdExpr : Expr ::= <ID:String>;...

How can the Visitor be implemented?

Compiler Construction 2011 F06-59

TraversingVisitor

class TraversingVisitor implements Visitor {Object visit(IfStmt node, Object data) {node.getCond().accept(this, data);node.getThen().accept(this, data);if (node.hasElse()) {

node.getElse().accept(this, data);}

}Object visit(Add node, Object data) {node.getLeft().accept(this, data);node.getRight().accept(this, data);

}}

The code above is independent of the computation and could havebeen generated from the abstract grammar (but this is currentlynot done in JJTree or JastAdd).Compiler Construction 2011 F06-60

CountIdentifiers as a visitor

class CountIdentifiers extends TraversingVisitor {

}

Example of use

Compiler Construction 2011 F06-61

Intertype declarations vs. Visitor

intertype declar-tions

Visitor

what can be mod-ularized?

instance variables,methods, imple-ments clauses

only methods

types for argu-ments and returnvalues

arbitrary Object visit(..., Object)(one untyped argumentand one result)

separate compila-tion?

no – preprocessorrequired

yes

pure Java? no – requires addi-tional tools

yes

Compiler Construction 2011 F06-62

Using the modularization techniques

InterpretationUnparsingMetricsSemantic Analysis

I Name analysis – connect an identifier to its declaration

I Type analysis ? compute the type of an expression

I . . .

Code generation

I Compute the size needed for objects and methods

I Generate instructions

I . . .

Compiler Construction 2011 F06-63