0% found this document useful (0 votes)
35 views7 pages

Abstract Grammar and Abstract Syntax Tree: The Grammar Is Not Parser Friendly

The document discusses abstract syntax trees (ASTs) and how they are constructed from grammars. It covers: - The difference between abstract and concrete grammars and their corresponding derivation trees (AST vs parse tree) - Using a concrete grammar to parse but constructing an AST - Representing the AST using object-oriented classes that mirror the grammar - Traversing the AST using different designs like visitor pattern to separate concerns.
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views7 pages

Abstract Grammar and Abstract Syntax Tree: The Grammar Is Not Parser Friendly

The document discusses abstract syntax trees (ASTs) and how they are constructed from grammars. It covers: - The difference between abstract and concrete grammars and their corresponding derivation trees (AST vs parse tree) - Using a concrete grammar to parse but constructing an AST - Representing the AST using object-oriented classes that mirror the grammar - Traversing the AST using different designs like visitor pattern to separate concerns.
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

Abstract Grammar and Abstract Syntax Tree

In programming languages, we prefer a grammar that is close to the language constructs. We call this grammar the abstract grammar. The corresponding derivation tree is called the abstract syntax tree. A node represents a language construct, a leaf represents a token. E E E E E +E E E num id

The grammar is not parser friendly.

Concrete Grammar and Parse Tree


Therefore, the grammar is tranformed such that it becomes easy to parse. Sample transformations include removing left recursion, left factoring, etc. The resulting grammar is called the concrete grammar. The corresponding derivation tree is called the parse tree. Parse tree: each leaf represents a token, each internal node represents a production rule. It is determined by the grammar. E E T T T E + E | E | num id

More particularly: we use concrete grammar to construct parsers, but we insert code into parser implementation to explicitly generate abstract syntax tree.
2

Recall: Representing Abstract Syntax Tree


One abstract class for each nonterminal in the abstract grammar; one sub-class for each production rule.
public abstract class Exp { } public class PlusExp extends Exp { private Exp e1,e2; public PlusExp(Exp a1, Exp a2) { e1=a1; e2=a2; } } public class MinusExp extends Exp { private Exp e1,e2; public MinusExp(Exp a1, Exp a2) { e1=a1; e2=a2; } } public class IdExp extends Exp { private String f0; public IdExp(String n0) { f0 = n0; } } public class IntExp extends Exp { private int v; public IntExp(int n0) { v = n0; } }

Abstract Syntax Tree Construction


Assume a recursive descendent parser.
Exp parseE() { Exp e1=parseT(); Exp e2=parseEP(e1); return e2; } Exp parseT() { if (next_token()==ID) { return new IdExp (eatID()); } else if (next_token()==NUM) { return new NumExp(eatNum()); } else error(); } Exp parseEP(Exp e1) { Exp e2; if (next_token()==+) { e2=parseE(); return new PlusExp(e1,e2); } else if (next_token()==-) { e2=parseE(); return new MinusExp(e1,e2); } else if (next_token()==$) { return e1; } else error(); }

Traversing AST:Design One


Nodes are visited in a depth-rst order, starting from the root (calling evalVisit()). The behavior of each node varies depending on the node type.
public abstract class Exp { public abstract int evalVisit(); } public class PlusExp extends Exp { private Exp e1,e2; public PlusExp(Exp a1, Exp a2) {...} int evalVisit() { return e1.evalVisit() + e2.evalVisit(); } } public class MinusExp extends Exp { private Exp e1,e2; public MinusExp(Exp a1, Exp a2) {...} int evalVisit() { return e1.evalVisit() - e2.evalVisit(); } }

public class IdExp extends Exp { private String f0; public IdExp(String n0) { ... } int evalVisit() { return getVarValue(f0); } } public class IntExp extends Exp { private int v; public IntExp (int n0) { ... } int evalVisit() { return v; } }

What if we need to traverse the tree for another purpose?


5

Traversing AST:Design Two


Group all methods regarding one functionality into one class. The problem is that we dont know the type of a node.
public class EvalVisitor { public visit(Exp root) { int v1, v2; if (e instanceof PlusExp) { PlusExp t= (PlusExp) e; return visit(t.e1)+visit(t.e2); } else if (e instanceof MinusExp) { MinusExp t= (MinusExp) e; return visit(t.e1)-visit(t.e2); } else if (e instanceof IdExp) { IdExp t=(IdExp) e; return retrieveVal(t.f0); } else if (e instanceof NumExp) { NumExp t= (NumExp) e; return t.v; } } } The instanceof and braches are not OO style.
6

Design Three: Visitor Pattern


The key: do a little handshake and let the node itself tell its type.
public abstract class Exp { public abstract int accept (Visitor v); } public class EvalVisitor public class PlusExp extends Exp { implements Visitor { public Exp e1,e2; public int visit(PlusExp n) { public PlusExp(Exp a1, Exp a2) ... return n.e1.accept(this) int accept(Visitor v) { +n.e2.accept(this) return v.visit(this); } } public int visit(MinusExp n) { } return n.e1.accept(this) public class MinusExp extends Exp { -n.e2.accept(this); public Exp e1,e2; } public MinusExp(Exp a1, Exp a2) ... ... int accept(Visitor v) { } return v.visit(this); } }

A very important design pattern, strongly suggest to use it in the project.

You might also like