Section 3 with Remik (rz33@cornell.edu) Meeting #10 Notes =========================================================== Administrivia =========================================================== + Assignment #3's deadline has been extended =========================================================== Review =========================================================== + Abstract Syntax Trees (AST) Continued + Code Generation of ASTs in Java =========================================================== Abstract Syntax Trees (AST) Continued =========================================================== Recall the simple expression language from last section. The grammer is given below in BNF PRG ::= START EXP END START ::= "main()" END ::= "return" EXP ::= '(' EXP ')' | int | EXP OP EXP OP ::= '+' | '-' A Sample Program: main() ((1+2) - 3) return Symbolically, this program looks like p | e0 | e /\ /| \ / | \ / | \ e1 '-' e2 | | e3 3 / |\ / | \ / | \ / | \ e4 '+' e5 | | 1 2 where a 'p' denotes a program PRG, and an 'e' an expression EXP. Notice that we can optimize our language if we choose to drop '(' and ')' since both addition and subtraction commute. That is, their order of application will result with the same value. In this case 0. An equivalent grammar and tree: PRG ::= START EXP END START ::= "main()" END ::= "return" EXP ::= '(' EXP ')' | int | EXP OP EXP OP ::= '+' | '-' p | e /\ /| \ / | \ / | \ e1 '-' e2 | | / |\ 3 / | \ / | \ / | \ e3 '+' e4 | | 1 2 =========================================================== Code Generation in Java =========================================================== From our last section, and with the modifications just made, our classes for the tree are now class PRG { EXP exp = null; public static final String START = "main()"; public static final String END = "return"; } class EXP { EXP expLeft = null; EXP expRight = null; int integer; char op; public static final char PLUS = '+'; public static final char MINUS = '-'; } Before we start jugging trees and creating code, lets try something more intuitive and at times useful. Let's reproduce the program when we're only give its tree! And lets do it so it looks nice! Lets 'prettyPrint' our program, and do it using recursion. After all, the grammar tells us that the language is recursively defined. Without further ado: class PrettyPrinter { String output; static void prettyPrint(PRG program) { output = START + "\n\t"; prettyPrint(program.exp); output += "\n\t" + END; } static void prettyPrint(EXP expression) { // check if it exists if (expression == null) return; if ((expression.expLeft == null) && (expression.expRight == null) ) // must be a leaf output += " " + expression.integer + " "; else if ((expression.expLeft == null) || (expression.expRight == null) ) // not possible, it is an error throw Exception("Malformed tree"); else { // lets use infix notation prettyPrint(expression.expLeft); output += " " + expression.op + " "; prettyPrint(expression.expRight); } } What about the Code Generator? Well, if we replace each instance of prettyPrint above with 'codeGen', then we'll have a code generator. Ah, but this would be a special kind. In essence, it would be an identity function because for some program P and virtual machine M M(P) = M(codeGen(P)), assuming we replaced each 'prettyPrint' with 'codeGen' and the virtual machine ran high level code.