import java.util.Hashtable; import rebelsky.compiler.lexer.Token; import rebelsky.compiler.misc.Symbol; import rebelsky.compiler.misc.SymbolTable; import rebelsky.compiler.parser.Node; import rebelsky.compiler.pascal.PascalIdentifier; import rebelsky.compiler.pascal.PascalInteger; import rebelsky.compiler.pascal.PascalNonterminals; import rebelsky.compiler.pascal.PascalReal; import rebelsky.compiler.pascal.PascalString; import rebelsky.compiler.pascal.PascalTokens; import rebelsky.pal.*; /** * A simple example of a program that walks a Stupid parse tree and * build a corresponding PAL program. * * @author Samuel A. Rebelsky * @version 0.6 of December 2002. */ public class StupidTranslator { // +----------------------+---------------------------------------------- // | Implementation Notes | // +----------------------+ /* I've defined each type as an object constant. I'll use pointer comparison (==) when I need to compare types. I use objects rather than integers because (1) objects are easier to store in hash tables; (2) objects are easier to print. */ // +-----------------+--------------------------------------------------- // | Private Classes | // +-----------------+ /** * All the fun information returned by an expression. */ private static class ExpressionStuff { Instruction code; Object type; Variable loc; } // class ExpressionStuff /** * Types of functions. Functions have both a parameter type * and a result type. */ private static class FunctionType { Object paramType; Object resultType; } // class FunctionType // +-----------+--------------------------------------------------------- // | Constants | // +-----------+ /** The type used for integers. */ private static Object TYPE_INTEGER = "integer"; /** The type used for real numbers. */ private static Object TYPE_REAL = "real"; /** The type used for single characters. */ private static Object TYPE_CHAR = "char"; /** The type used for boolean values. */ private static Object TYPE_BOOLEAN = "boolean"; /** The type used for string values. */ private static Object TYPE_STRING = "string"; /** The type used for undefined values. */ private static Object TYPE_UNDEFINED = "undefined"; // +------------------------+-------------------------------------------- // | Private Helper Methods | // +------------------------+ /** * Report an error. A more concise way to say * "throw new Exception(...)". */ public static void boom(String msg) throws Exception { throw new Exception(msg); } // boom(String) /** * Convert an identifier token that describes a type to the * corresponding type. */ public static Object getType(Token t) throws Exception { if (t == PascalTokens.TBOOLEAN) return TYPE_BOOLEAN; if (t == PascalTokens.TCHAR) return TYPE_CHAR; if (t == PascalTokens.TINTEGER) return TYPE_INTEGER; if (t == PascalTokens.TREAL) return TYPE_REAL; throw new Exception("Invalid type: " + t.toString()); } // getType(token) // +-----------------------------+-------------------------------------- // | Private Translation Methods | // +-----------------------------+ /** * Translate an assignment statement. */ public static InstructionSequence translateAssignment(Node assignment, SymbolTable symbols) throws Exception { // Get the variable. Symbol var = assignment.getChild(0).getSymbol(); // Sanity check: Can't assign to variables that don't exist. if (!symbols.isSet(var, "location")) throw new Exception("Invalid variable: " + var); InstructionSequence code = new InstructionSequence(); ExpressionStuff stuff = translateExpression(assignment.getChild(1), symbols); code.add(stuff.code); if (stuff.type != symbols.get(var, "type")) throw new Exception("Cannot assign values of incompatible types."); code.add(new Move(stuff.loc, (Variable) symbols.get(var, "location"))); return code; } // translateAssignment(Node, SymbolTable) public static Instruction translateBooleanExpression(Node expression, SymbolTable symbols, Label truelabel, Label falselabel) throws Exception { InstructionSequence instructions = new InstructionSequence(); Symbol sym = expression.getSymbol(); // Case 1: Constant true if (sym == PascalTokens.TTRUE) return new Jump(truelabel); // Case 2: Constant false else if (sym == PascalTokens.TFALSE) return new Jump(falselabel); // Case 3: Variable else if (sym instanceof PascalIdentifier) throw new Exception("Can't handle boolean identifiers yet."); // Case 4: Anything else. Punt! else throw new Exception("Can't handle that kind of boolean expression."); } // translateBooleanExpression() /** * Translate a compound statement. * * @exception Exception * If the tree does not type check. */ public static InstructionSequence translateCompoundStatement(Node statements, SymbolTable symbols) throws Exception { // Make sure that it's actually a list of statements. if (statements.getSymbol() != PascalNonterminals.COMPOUND_STATEMENT) throw new Exception("Didn't find compound statement!"); // Set up the list of instructions. InstructionSequence instructions = new InstructionSequence(); // Translate each statement in turn. int numStatements = statements.numChildren(); for (int statementNum = 0; statementNum < numStatements; ++statementNum) { instructions.add(translateStatement(statements.getChild(statementNum), symbols)); } // for return instructions; } // translateCompoundStatement(Node) /** * Translate a conditional statement. */ public static InstructionSequence translateConditional(Node conditional, SymbolTable symbols) throws Exception { InstructionSequence code = new InstructionSequence(); Label truepart = new Label("TRUE_PART"); Label falsepart = new Label("FALSE_PART"); Label end = new Label("END_IF"); code.add(translateBooleanExpression(conditional.getChild(0), symbols, truepart, falsepart)); code.add(truepart); code.add(translateStatement(conditional.getChild(1), symbols)); code.add(new Jump(end)); code.add(falsepart); code.add(translateStatement(conditional.getChild(2), symbols)); code.add(new Jump(end)); code.add(end); return code; } // translateConditional(Node, SymbolTable) /** * Translate a parse tree for an expression. Returns a triplet * of code for expression, type of expression, and location of * result of expression. */ public static ExpressionStuff translateExpression(Node expression, SymbolTable symbols) throws Exception { Symbol sym = expression.getSymbol(); ExpressionStuff result = new ExpressionStuff(); // Case 1: Identifier if (sym instanceof PascalIdentifier) { if (!symbols.isSet(sym, "location")) throw new Exception("Identifier " + sym + " is undefined."); result.type = symbols.get(sym, "type"); result.code = new NoOp(); result.loc = (Variable) symbols.get(sym, "location"); } // Case 1: Identifier // Case 2: Integer else if (sym instanceof PascalInteger) { result.loc = new Temporary(); result.code = new Move(new IConstant(((PascalInteger) sym).getValue()), result.loc); result.type = TYPE_INTEGER; } // Case 2: Integer // Case 3: Real else if (sym instanceof PascalReal) { result.loc = new Temporary(); result.code = new Move(new FConstant(((PascalReal) sym).getValue()), result.loc); result.type = TYPE_REAL; } // Case 3: Real // Case 4: Operator applied to two symbols. else if (expression.numChildren() == 3) { InstructionSequence code = new InstructionSequence(); result.loc = new Temporary(); // Translate the two subexpressions and retain their code. ExpressionStuff left = translateExpression(expression.getChild(0), symbols); ExpressionStuff right = translateExpression(expression.getChild(2), symbols); code.add(left.code); code.add(right.code); // Sanity check: Are the two result types the same? if (left.type != right.type) throw new Exception("Cannot apply operation to mismatched types."); result.type = left.type; // Decide what operation to generate Symbol op = expression.getChild(1).getSymbol(); if (op == PascalTokens.TPLUS) { if (result.type == TYPE_INTEGER) code.add(new IAdd(left.loc, right.loc, result.loc)); else if (result.type == TYPE_REAL) code.add(new FAdd(left.loc, right.loc, result.loc)); else throw new Exception("Cannot add variables of type " + left.type); } // addition result.code = code; } // Case 4: Operator // Case 5: Unknown else { throw new Exception("Unknown expression type."); } // That's it, we're done. return result; } // translateExpression(Node,Hashtable) /** * Translate a sequence of function declarations. Update the * symbol table while translating. */ public static InstructionSequence translateFunctions(Node functions, SymbolTable symbols) throws Exception { // Make sure that it's actually a list of functions. if (functions.getSymbol() != PascalNonterminals.FUNCTION_DECLARATIONS) throw new Exception("Didn't find sequence of function declarations!"); // Set up the list of instructions. InstructionSequence instructions = new InstructionSequence(); // Translate each statement in turn. int numFunctions = functions.numChildren(); for (int functionNum = 0; functionNum < numFunctions; ++functionNum) { Node declaration = functions.getChild(functionNum); // MORE TO COME ... } // for return instructions; } // translateFunctions(Node,SymbolTable) /** * Translate the global variable declarations. Update the symbol table * while translating. Since translation of the globals does not generate * any code (or shouldn't generate any code), this returns nothing. * @exception Exception * If the tree does not type check. */ public static void translateGlobals(Node declarations, SymbolTable symbols) throws Exception { // Make sure that it's a collections of declarations if (declarations.getSymbol() != PascalNonterminals.VARIABLE_DECLARATIONS) throw new Exception("Didn't find declarations"); // Deal with each declaration in turn. int numDeclarations = declarations.numChildren(); for (int decNum = 0; decNum < numDeclarations; ++decNum) { Node dec = declarations.getChild(decNum); if (dec.getSymbol() != PascalNonterminals.VARIABLE_DECLARATION) throw new Exception("Didn't find declaration where I expected it in " + dec.getSymbol()); int numIds = dec.numChildren() - 1; Object type = getType((Token) dec.getChild(numIds).getSymbol()); for (int idNum = 0; idNum < numIds; idNum++) { Token identifier = (Token) dec.getChild(idNum).getSymbol(); symbols.set(identifier, "type", type); // may throw an exception symbols.set(identifier, "location", new MemLoc(new IConstant(idNum))); } // for (idNum) } // for (decNum) } // translateGlobals(Node, SymbolTable) /** * Translate a statement. */ public static Instruction translateStatement(Node statement, SymbolTable symbols) throws Exception { if (statement == null) { return new NoOp(); } // null statement if (statement.getSymbol() == PascalNonterminals.COMPOUND_STATEMENT) { return translateCompoundStatement(statement, symbols); } // compound statement else if (statement.getSymbol() == PascalNonterminals.ASSIGNMENT) { return translateAssignment(statement, symbols); } else if (statement.getSymbol() == PascalNonterminals.PROCEDURE_CALL) { Token proc = (Token) statement.getChild(0).getSymbol(); if (proc == StupidTokens.TREAD) { Symbol variable = statement.getChild(1).getSymbol(); if (!symbols.isSet(variable, "type")) throw new Exception("Variable " + variable + " is not defined."); Object type = symbols.get(variable,"type"); if (type == TYPE_INTEGER) return new IRead((Variable) symbols.get(variable,"location")); else if (type == TYPE_REAL) return new FRead((Variable) symbols.get(variable,"location")); else throw new Exception("Cannot read variables of type " + type + "."); } // if (proc == StupidTokens.TREAD) else if (proc == StupidTokens.TWRITE) { // Get the expression Node expression = statement.getChild(1); // Special case: write a string if (expression.getSymbol() instanceof PascalString) return new SWrite(((PascalString) expression.getSymbol()).getName()); // Prepare to generate a sequence of instructions. InstructionSequence instructions = new InstructionSequence(); ExpressionStuff result = translateExpression(expression, symbols); instructions.add(result.code); if (result.type == TYPE_INTEGER) instructions.add(new IWrite(result.loc)); else if (result.type == TYPE_REAL) instructions.add(new FWrite(result.loc)); else throw new Exception("Invalid type: " + result.type); return instructions; } // if (proc == StupidTokens.TWRITE) else { throw new Exception("Cannot call procedure '" + proc + "'"); } // Unknown procedure call } // procedure call else { throw new Exception("Unexpected type of statement."); } // unknown statement type } // translateStatement(Node, SymbolTable) // +----------------+---------------------------------------------------- // | Public Methods | // +----------------+ /** * Translate the parse tree for a program. What fun. * * @exception Exception * If the tree does not type check. */ public static InstructionSequence translate(Node program) throws Exception { // Make sure that it's a program. if (program.getSymbol() != PascalNonterminals.PROGRAM) boom("Not a program!"); // Initialize the symbol table SymbolTable symbols = new SymbolTable(); symbols.set(PascalTokens.TTRUE, "type", TYPE_BOOLEAN); symbols.set(PascalTokens.TFALSE, "type", TYPE_BOOLEAN); // Set up the main sequence of instructions. InstructionSequence code = new InstructionSequence(); Label body = new Label("BODY"); code.add(new Jump(body)); // Check the variable declarations and update the symbol table. translateGlobals(program.getChild(0), symbols); // Check the function declarations and update the symbol table. code.add(translateFunctions(program.getChild(1), symbols)); // Check the main body using the updated symbol table. code.add(body); code.add(translateCompoundStatement(program.getChild(2), symbols)); code.add(new Halt()); // That's it, we're done. return code; } // translate(Node) } // class StupidTranslator