package Compiler; import java.util.ArrayList; import java.util.List; import Compiler.Expression.*; import Compiler.Statement.*; /** * Class to take a convert a list of statements into equivalent C source code */ public class Translator{ List<String> CCode = new ArrayList<>(); private Environment environment = new Environment(); /** * Method to take a list of statements and convert to C code * @param statements a list of statement objects * @param printC a variable to say if the produced C code should be outputted * @return a list of strings for each line of the produced C code */ public List<String> compileToC(List<Statement> statements, boolean printC){ //Write basic include header files CCode.add("#include <stdio.h>"); CCode.add("#include <string.h>"); CCode.add("#include <math.h>"); //Try and write each statement, with a space between each try{ for (Statement statement: statements){ evaluateStatement(statement); CCode.add(""); } } catch (Error e){ } //Output the C code if desired if (printC) { for(String t:CCode){ System.out.println(t); } System.out.println(""); } return CCode; } /** * Method to write a single statement to C * @param statement the statement to write to C */ private void evaluateStatement(Statement statement){ //Call the correct function for each statement type switch(statement.getStatmentType()){ case "main": evalMainFunction((MainFunction)statement); break; case "function": evalFunction((Function)statement); break; case "functionDec": evalFunctionDeclaration((FunctionDeclaration)statement); break; case "exprStmt": evalExpressionStatement((ExpressionStatement)statement); break; case "varDec": evalVariableDeclaration((VariableDeclaration)statement); break; case "stringDec": evalStringDeclaration((StringDeclaration)statement); break; case "arrayDec": evalArrayDeclaration((ArrayDeclaration)statement); break; case "block": evalBlockStatement((BlockStatement)statement); break; case "print": evalPrintStatement((PrintStatement)statement); break; case "ifStmt": evalIfStatement((IfStatement)statement); break; case "doStmt": evalDoStatement((DoStatement)statement); break; case "dowhileStmt": evalDoWhileStatement((DoWhileStatement)statement); break; case "return": evalReturnStatement((ReturnStatement)statement); break; } } /** * Method to write the main function * @param stmt statement to write */ private void evalMainFunction(MainFunction stmt){ CCode.add("int main(){"); evaluateStatement(stmt.block); CCode.add("}"); } /** * Method to write a function * @param stmt statement to write */ private void evalFunction(Function stmt){ String functionString; if(!(stmt.returnType==null)){ functionString = stmt.returnType+" "+stmt.name.text+"("; }else{ functionString = "void "+stmt.name.text+"("; } boolean first=true; //Write each function argument into C for(int i=0;i<stmt.arguments.size();i++){ if(!first){ functionString+=","; } String type = stmt.argumentTypes.get(i); Expression arg = stmt.arguments.get(i); //Define each argument variable in the environment if(type.equals("char*")){ environment.defineVariable(evaluateExpression(arg), "string"); }else{ environment.defineVariable(evaluateExpression(arg), type); } functionString+=type+" "+evaluateExpression(arg); first=false; } functionString+="){"; //Write function block CCode.add(functionString); evaluateStatement(stmt.block); CCode.add("}"); } /** * Method to write a return statement * @param stmt statement to write */ private void evalReturnStatement(ReturnStatement stmt){ CCode.add("return "+evaluateExpression(stmt.returnValue)+";"); } /** * Method to write a function declaration * @param stmt statement to write */ private void evalFunctionDeclaration(FunctionDeclaration stmt){ String functionString; if(!(stmt.returnType==null)){ functionString = stmt.returnType+" "+stmt.name.text+"("; }else{ functionString = "void "+stmt.name.text+"("; } boolean first=true; //Write each argument data type for(int i=0;i<stmt.argumentTypes.size();i++){ if(!first){ functionString+=","; } functionString+=stmt.argumentTypes.get(i); first=false; } functionString+=");"; CCode.add(functionString); } /** * Method to write an expression statement * @param stmt statement to write */ private void evalExpressionStatement(ExpressionStatement stmt){ String value = evaluateExpression(stmt.expr); if(value!=""){ CCode.add(value+";"); } } /** * Method to write a string declaration statement * @param stringdec statement to write */ private void evalStringDeclaration(StringDeclaration stringdec){ //Add variable to the environment environment.defineVariable(stringdec.name.text, "string"); //Get the size of the string int size = (int)((Expression.Literal)stringdec.length).value.value; //Increase the size of the string by 1 to match C code correctly size++; CCode.add("char "+stringdec.name.text+"["+size+"];"); } /** * Method to write a variable declaration * @param vardec statement to write */ private void evalVariableDeclaration(VariableDeclaration vardec){ //Add variable to the environment environment.defineVariable(vardec.name.text, vardec.type); //Write correct data type if(vardec.type.equals("int")){ CCode.add("int "+vardec.name.text+";"); } else if(vardec.type.equals("double")){ CCode.add("double "+vardec.name.text+";"); } } /** * Method to write an array declaration * @param arraydec statement to write */ private void evalArrayDeclaration(ArrayDeclaration arraydec){ //Add variable to the environment environment.defineVariable(arraydec.name.text, arraydec.type); //Get the array data type String arrayString = ""; if(arraydec.type.equals("int")){ arrayString+="int "; } else if(arraydec.type.equals("double")){ arrayString+="double "; } arrayString+=arraydec.name.text; //Write each dimension of the array for(Expression expr:arraydec.dimensions){ if(expr.getExpressionType()!="literal"){ Language.displayError(arraydec.name, "Array sizes must be defined using constants"); throw new Error(); } arrayString+="["; arrayString+=evaluateExpression(expr); arrayString+="]"; } arrayString+=";"; CCode.add(arrayString); } /** * Method to write a block statement * @param block statement to write */ private void evalBlockStatement(BlockStatement block){ //Write each statement in the block for(Statement stmt:block.statements){ evaluateStatement(stmt); } } /** * Method to write a print statement * @param print statement to write */ private void evalPrintStatement(PrintStatement print){ String types=""; String values=""; boolean first=true; //Write each expression in the print statement for(Expression expr:print.exprList){ //Don't add a comma for the first value in the print statement if(!first){ values+=","; }else{ first=false; } String exprType=""; //Get the data type for each expression if(expr instanceof Expression.Literal){ exprType=((Expression.Literal)expr).type; } else if (expr instanceof Expression.Variable){ exprType=(String)environment.getVariable((((Expression.Variable)expr).name)); } else if (expr instanceof Expression.ArrayVariable){ exprType=(String)environment.getVariable((((Expression.ArrayVariable)expr).name)); } //Set the correct data type for the print statement if (exprType.equals("int")){ types+="%d"; } else if (exprType.equals("double")){ types+="%f"; } else if (exprType.equals("string")){ types+="%s"; } else{ Language.displayError("Unknown expression type in print statement"); } //Add the actual expression value values+=evaluateExpression(expr); } //Add a line break at the end of the print statement text types+="\\n"; CCode.add("printf(\""+types+"\","+values+");"); } /** * Method to write an if statement * @param ifstatement statement to write */ private void evalIfStatement(IfStatement ifstatement){ //Write if statement block CCode.add("if("+evaluateExpression(ifstatement.condition)+"){"); evaluateStatement(ifstatement.ifBlock); //Write else block if needed if(!(ifstatement.elseBlock==null)){ CCode.add("}"); CCode.add("else {"); evaluateStatement(ifstatement.elseBlock); } CCode.add("}"); } /** * Method to write a do statement * @param dostatement statement to write */ private void evalDoStatement(DoStatement dostatement){ //Form the sections of the for loop, assuming the step variable has not been defined String start = evaluateExpression(dostatement.variable)+"="+evaluateExpression(dostatement.start); String stop = evaluateExpression(dostatement.variable)+"<="+evaluateExpression(dostatement.stop); String step = evaluateExpression(dostatement.variable)+"++"; //Update step variable if needed if(!(dostatement.step==null)){ step = evaluateExpression(dostatement.variable)+"+="+evaluateExpression(dostatement.step); } CCode.add("for("+start+";"+stop+";"+step+"){"); evaluateStatement(dostatement.codeBlock); CCode.add("}"); } /** * Method to write a do while loop * @param dowhilestatement statement to write */ private void evalDoWhileStatement(DoWhileStatement dowhilestatement){ CCode.add("while("+evaluateExpression(dowhilestatement.condition)+"){"); evaluateStatement(dowhilestatement.codeBlock); CCode.add("}"); } /** * Method to write the correct expression object to C * Since an expression is not a full line, the string is returned * Except for assignment expressions * @param expression the expression to write * @return the string representation of an expression */ private String evaluateExpression(Expression expression){ switch(expression.getExpressionType()){ case "binary": return evaluateBinaryExpression((Binary)expression); case "singular": return evaluateSingularExpression((Singular)expression); case "literal": return evaluateLiteralExpression((Literal)expression); case "bracket": return evaluateBracketedExpression((BracketedExpression)expression); case "assign": //Assignments are full lines, so no need to return the string evaluateAssignmentExpression((AssignmentExpression)expression); return ""; case "arrayvar": return evaluateArrayVariable((ArrayVariable)expression); case "var": return evaluateVariableExpression((Variable)expression); case "functCall": return evaluateFunctionCall((FunctionCall)expression); default: return null; } } /** * Method to write a binary expression * @param expr the expression to write * @return the string representation of the expression */ private String evaluateBinaryExpression(Binary expr){ switch (expr.op.type){ case PLUS: //return "leftEval+rightEval" return evaluateExpression(expr.left)+"+"+evaluateExpression(expr.right); case STAR: return evaluateExpression(expr.left)+"*"+evaluateExpression(expr.right); case MINUS: return evaluateExpression(expr.left)+"-"+evaluateExpression(expr.right); case SLASH: return evaluateExpression(expr.left)+"/"+evaluateExpression(expr.right); case GREATER: return evaluateExpression(expr.left)+">"+evaluateExpression(expr.right); case LESS: return evaluateExpression(expr.left)+"<"+evaluateExpression(expr.right); case GREATER_EQUAL: return evaluateExpression(expr.left)+">="+evaluateExpression(expr.right); case LESS_EQUAL: return evaluateExpression(expr.left)+"<="+evaluateExpression(expr.right); case EQUALITY: return evaluateExpression(expr.left)+"=="+evaluateExpression(expr.right); case AND: return evaluateExpression(expr.left)+"&&"+evaluateExpression(expr.right); case OR: return evaluateExpression(expr.left)+"||"+evaluateExpression(expr.right); case EXPONENT: return "pow("+evaluateExpression(expr.left)+","+evaluateExpression(expr.right)+")"; case NOT_EQUAL: return evaluateExpression(expr.left)+"!="+evaluateExpression(expr.right); default: break; } return null; } /** * Method to write a singular expression * @param expr the expression to write * @return the string representation of the expression */ private String evaluateSingularExpression(Singular expr){ switch (expr.op.type){ case NOT: return "!"+evaluateExpression(expr.right); default: break; } return null; } /** * Method to write a literal expression * @param expr the expression to write * @return the string representation of the expression */ private String evaluateLiteralExpression(Literal expr){ return (expr.value.value).toString(); } /** * Method to write a bracketed expression * @param expr the expression to write * @return the string representation of the expression */ private String evaluateBracketedExpression(BracketedExpression expr){ return "("+evaluateExpression(expr.expr)+")"; } /** * Method to write an assignment expression * @param expr the expression to write */ private void evaluateAssignmentExpression(AssignmentExpression expr){ Token name=null; //Get the name of the variable being assigned to if(expr.variable instanceof Expression.Variable){ name = ((Expression.Variable)expr.variable).name; } else if(expr.variable instanceof Expression.ArrayVariable){ name = ((Expression.ArrayVariable)expr.variable).name; } //Check if the variable has been previously declared if(environment.checkVariable(name)){ //Check if the value being assigned is a literal or some other expression if(expr.value instanceof Expression.Literal){ //Strings are handled differently in C, so the string has to be copied into the variable if(((Expression.Literal)expr.value).type.equals("string")){ CCode.add("strcpy("+evaluateExpression(expr.variable)+","+evaluateExpression(expr.value)+");"); }else{ CCode.add(evaluateExpression(expr.variable)+"="+evaluateExpression(expr.value)+";"); } } else{ CCode.add(evaluateExpression(expr.variable)+"="+evaluateExpression(expr.value)+";"); } } } /** * Method to write an array variable * @param expr the expression to write * @return the string representation of the expression */ private String evaluateArrayVariable(ArrayVariable expr){ //Check the array has been defined if(environment.checkVariable(expr.name)){ String arrayString=""; arrayString+=expr.name.text; //Write each dimension value for(Expression position:expr.positions){ arrayString+="["; arrayString+=evaluateExpression(position); arrayString+="]"; } return arrayString; } return null; } /** * Method to write a variable expression * @param expr the expression to write * @return the string representation of the expression */ private String evaluateVariableExpression(Variable expr){ return expr.name.text; } /** * Method to write a function call * @param expr the expression to write * @return the string representation of the expression */ private String evaluateFunctionCall(FunctionCall expr){ String functioncall=""; functioncall+=expr.name.text+"("; boolean first=true; //Write each argument of the function call for(Expression arg:expr.arguments){ if(!first){ functioncall+=","; } functioncall+=evaluateExpression(arg); first=false; } functioncall+=")"; return functioncall; } }