package de.fuberlin; import java.io.File; import java.util.HashMap; import java.util.logging.Level; import de.fuberlin.bii.lexergen.BuilderType; import de.fuberlin.bii.lexergen.Lexergen; import de.fuberlin.bii.lexergen.LexergeneratorException; import de.fuberlin.bii.tokenmatcher.errorhandler.ErrorCorrector.CorrectionMode; import de.fuberlin.commons.lexer.ILexer; import de.fuberlin.commons.lexer.IToken; import de.fuberlin.commons.parser.IParser; import de.fuberlin.commons.parser.ISyntaxTree; import de.fuberlin.commons.util.LogFactory; import de.fuberlin.optimierung.LLVM_Optimization; import de.fuberlin.optimierung.LLVM_OptimizationException; import de.fuberlin.projectF.CodeGenerator.CodeGenerator; import de.fuberlin.projecta.analysis.DebuggingHelper; import de.fuberlin.projecta.analysis.SemanticAnalyzer; import de.fuberlin.projecta.analysis.SemanticException; import de.fuberlin.projecta.lexer.Lexer; import de.fuberlin.projecta.lexer.io.FileCharStream; import de.fuberlin.projecta.lexer.io.ICharStream; import de.fuberlin.projecta.parser.ParseException; import de.fuberlin.projecta.parser.Parser; import de.fuberlin.projectci.LRParserMain; import de.fuberlin.projectcii.ParserGenerator.src.LL1Parser; public class Main { /* * Konstanten für die Parameter */ // Parser static final String PARAM_LR_PARSER = "-lr"; // benutzt den LR Parser static final String PARAM_LL_PARSER = "-ll"; // benutzt den LL Parser // Lexer static final String PARAM_DEF_FILE = "-d"; // Gibt den Pfad zur Datei mit den regulären Definitionen an static final String PARAM_BI_LEXER = "-bi"; // benutzt die indirekte Umwandlung static final String PARAM_BII_LEXER = "-bii"; // benutzt die direkte Umwandlung static final String PARAM_REBUILD_DFA = "-rb"; //Gibt an, dass der DFA neu erstellt werden soll // Codegenerierung static final String PARAM_ASM_TYPE = "-asmType"; // "gnu" oder "intel" waehlt den Assemblertyp. Standard ist "gnu" static final String PARAM_EXEC = "-e"; // angeben um die Ausgabedatei auch auszuf�hren static final String PARAM_CONFIG_FILE = "-C"; // Pfad zu einer alternativen Konfigurationsdatei // Allgemein static final String PARAM_SOURCE_FILE = "-f"; // Gibt den Pfad zum Quellprogramm an static final String PARAM_OUTPUT_FILE = "-o"; // Gibt den Pfad zur Ausgabedatei an static final String PARAM_LLVM_INPUT_FILE = "-llvm"; // Gibt den Pfad zur LLVM Quelldatei an static final String PARAM_LOG_LEVEL_CONSOLE = "-logLevelConsole"; // Gibt den zu verwendenen LogLevel für die Console an static final String PARAM_LOG_LEVEL_FILE = "-logLevelFile"; // Gibt den zu verwendenen LogLevel für die Console an static final String PARAM_LOG_FILE = "-logFile"; // Gibt den Pfad für eine Logdatei an // Standard Parameter static final String DEFAULT_GRAMMAR_FILE = "input/de/fuberlin/projectci/non-ambigous.txt"; static final String DEFAULT_TOKEN_DEFINITION_FILE = "input/de/fuberlin/bii/def/tokendefinition.rd"; static final String DEFAULT_SOURCE_FILE = "input/de/fuberlin/common/default.src"; // interne Daten static String generatedLLVMCode = ""; /* * Ideen für Konsolenparameter: * Datei für Programm in Quellsprache: -f "/path/to/inputProgram" * Lexer von Gruppe bi verwenden: -bi * Lexer von Gruppe bii verwenden: -bii * Parser von Gruppe ci verwenden: -lr [Optional]"/path/to/bnfGrammar" * Parser von Gruppe cii verwenden: -ll [Optional]"/path/to/bnfGrammar" * * Standardkonfiguration: -f "irgendeinBeispielprogramm" -bi -lr */ public static void main(String args[]) { HashMap<String,String> arguments = readParams(args); initLogging(arguments); boolean success = runFrontend(arguments); if (!success) { System.out.println("Failed to run frontend. Stop."); return; } success = runBackend(arguments); if (!success) { System.out.println("Failed to run backend. Stop."); } } private static void initLogging(HashMap<String,String> arguments){ Level logLevelConsole=null; Level logLevelFile=null; File logFile=null; String strLogLevelConsole=arguments.get(PARAM_LOG_LEVEL_CONSOLE); if (strLogLevelConsole!=null){ try { logLevelConsole=Level.parse(strLogLevelConsole); } catch (IllegalArgumentException e) { System.err.println("Failed to parse param "+PARAM_LOG_LEVEL_CONSOLE +": "+strLogLevelConsole); } } String strLogLevelFile=arguments.get(PARAM_LOG_LEVEL_FILE); if (strLogLevelFile!=null){ try { logLevelFile=Level.parse(strLogLevelFile); } catch (IllegalArgumentException e) { System.err.println("Failed to parse param "+PARAM_LOG_LEVEL_FILE +": "+strLogLevelFile); } } String logFilePath=arguments.get(PARAM_LOG_FILE); if (logFilePath!=null){ logFile=new File(logFilePath); // if (!logFile.canWrite()){ // System.err.println("Cannot write to log file configuted by param "+PARAM_LOG_FILE +": "+logFilePath); // logFile=null; // } } LogFactory.init(logLevelConsole, logLevelFile, logFile!=null?logFile.getAbsolutePath():null); } /** * Frontend-Phase * * Eingabe: Input-File * Ausgabe: Generierter LLVM-Code * * @see generatedLLVMCode */ public static boolean runFrontend(HashMap<String,String> arguments) { System.out.println("Starting frontend Phase"); // -d "/path/to/definitionFile" String defFile = arguments.get(PARAM_DEF_FILE); if( defFile == null ) defFile = DEFAULT_TOKEN_DEFINITION_FILE; // fall-back // -f "/path/to/inputProgram" String inputFile = arguments.get(PARAM_SOURCE_FILE); if (inputFile == null || inputFile.isEmpty()) { System.out.println("Warning: No input file! Use default input file"); inputFile = DEFAULT_SOURCE_FILE; } //-------------------------- /* * Lexer * input: Pfad zu der Datei mit den regulären Definitionen und Pfad zu der Programmdatei * output: IToken-Objekt beim aufruf von getNextToken */ File file = new File(inputFile); if (!file.canRead()) { System.out.println("Error: Invalid file: " + file.getAbsolutePath()); return false; } ILexer lexer = null; final boolean rebuildDFA = arguments.containsKey(PARAM_REBUILD_DFA); // Lexer from bii if( arguments.containsKey(PARAM_BII_LEXER) ) { try { lexer = new Lexergen(new File(defFile), file, BuilderType.directBuilder, CorrectionMode.PANIC_MODE, rebuildDFA); } catch (LexergeneratorException e) { e.printStackTrace(); return false; } // Lexer from bi } else if (arguments.containsKey(PARAM_BI_LEXER)) { try { lexer = new Lexergen(new File(defFile), file, BuilderType.indirectBuilder, CorrectionMode.PANIC_MODE, rebuildDFA); } catch (LexergeneratorException e) { e.printStackTrace(); return false; } } // Lexer from projecta, default else { ICharStream stream = new FileCharStream(inputFile); lexer = new Lexer(stream); } //-------------------------- //-------------------------- /* * Parser * input: ILexer lexerObject, String grammarFilePath * output: ISyntaxTreee parseTree */ IParser parser = null; String grammarFile = ""; // LR-Parser, -lr "/path/to/bnfGrammar" if( arguments.get(PARAM_LR_PARSER) != null ){ parser = new LRParserMain(); grammarFile = arguments.get(PARAM_LR_PARSER); // LL-Parser, -ll ["/path/to/bnfGrammar"] } else if( arguments.containsKey(PARAM_LL_PARSER) ) { parser = new LL1Parser(); grammarFile = arguments.get(PARAM_LL_PARSER); // Parser from projecta, default } else { parser = new Parser(); } ISyntaxTree parseTree = null; try { parseTree = parser.parse(lexer, grammarFile); } catch (ParseException e) { System.out.println(e.getMessage() + " (error at line: " + e.getLineNumber() + ", column: " + e.getOffset() + ", token: \"" + e.getText() + "\")" ); System.out.println("Details:"); System.out.println(e.getDetails()); } if (parseTree == null) { System.out.println("Failed to parse!"); return false; } //-------------------------- //-------------------------- /* * Semantic analysis * input: Parse tree * inter: Abstract Syntax Tree (AST) * output: LLVM-Code */ SemanticAnalyzer analyzer = new SemanticAnalyzer(parseTree); try { analyzer.analyze(); } catch (SemanticException e) { System.out.println("\nError: Failed to parse."); System.out.println(e.getMessage()); IToken token = DebuggingHelper.extractPosition(e.getNode()); if (token != null) System.out.println("Error near: '" + token.getText() + "' near line: " + token.getLineNumber() + ", column: " + token.getOffset()); // abort return false; } // debug System.out.println(); analyzer.getAST().printTree(); // Generate LLVM-Code generatedLLVMCode = analyzer.getAST().genCode(); return true; } /** * Backend-Phase * * Eingabe: Generierte LLVM Code * Ausgabe: Maschinencode */ public static boolean runBackend(HashMap<String,String> arguments) { System.out.println("Starting backend Phase"); //-------------------------- /* * Optimierung * input: String llvm_code * output: String optimized_llvm_code */ System.out.println(generatedLLVMCode); String optimized_llvm_code = ""; LLVM_Optimization llvm_optimizer = new LLVM_Optimization(); try{ if(arguments.containsKey(PARAM_LLVM_INPUT_FILE)) { optimized_llvm_code = llvm_optimizer.optimizeCodeFromFile(arguments.get(PARAM_LLVM_INPUT_FILE)); } else{ optimized_llvm_code = llvm_optimizer.optimizeCodeFromString(generatedLLVMCode); } System.out.println(optimized_llvm_code); }catch (LLVM_OptimizationException e){ // Fehlermeldung anzeigen System.err.println("; OPTIMIZATION-ERROR: " + e.getMessage()); // Nutze nicht optimierten Code optimized_llvm_code = generatedLLVMCode; }catch(Exception e){ System.err.println(e.getMessage()); System.err.println("Optimization not done!\nUse unoptimized code!\n"); // Nutze nicht optimierten Code optimized_llvm_code = generatedLLVMCode; } //-------------------------- //-------------------------- /* * Codegenerierung * input : String llvm_code * output: String machineCode */ boolean debug = false; boolean guiFlag = false; String outputFile = null; boolean exec = false; String configFile = "mc_config.cfg"; String asmType = "gnu"; if(arguments.containsKey(PARAM_CONFIG_FILE)) { configFile = arguments.get(PARAM_CONFIG_FILE); } if(arguments.containsKey(PARAM_OUTPUT_FILE)) { outputFile = arguments.get(PARAM_OUTPUT_FILE); } if (arguments.containsKey(PARAM_EXEC)) { exec = true; } if(arguments.containsKey(PARAM_ASM_TYPE)) { asmType = arguments.get(PARAM_ASM_TYPE); } // TODO Der Assemblertyp ('gnu' oder 'intel') (siehe de.fuberlin.projectF.CodeGenerator.Translator) sollte über die Kommandozeile definierbar sein können String machineCode = CodeGenerator.generateCode(optimized_llvm_code, asmType, debug, guiFlag); if (outputFile != null) { CodeGenerator.writeFile(exec, outputFile, machineCode); } if (exec) { CodeGenerator.exec(outputFile, configFile); } //-------------------------- return true; } // alle Parameter, die mit "-" beginnen als Key benutzen und eventueller // Folgeparameter(vielleicht nur mit Anführungszeichen akzeptieren) als Value // was nicht diesem Schema entspricht, ignorieren private static HashMap<String,String> readParams(String args[]){ HashMap<String,String> arguments = new HashMap<String,String>(); for(int i = 0; i < args.length; i++) { if(args[i].equalsIgnoreCase(PARAM_SOURCE_FILE)) { // Es wurde ein -f gelesen, erwarte nun Quelldatei if(i+1 < args.length && !args[i+1].trim().startsWith("-")) { // Eine Quelldatei wurde angegeben arguments.put(PARAM_SOURCE_FILE, args[i+1].trim()); i++; } else { // Keine Quelldatei angeben System.out.println("Keine Quelldatei angegeben!"); arguments.put(PARAM_SOURCE_FILE, null); } } else if(args[i].equalsIgnoreCase(PARAM_DEF_FILE)) { // Es wurde ein -d gelesen, erwarte nun Quelldatei System.out.println("Para: -d"); if(i+1 < args.length && !args[i+1].trim().startsWith("-")) { // Eine Quelldatei wurde angegeben System.out.println("-f Option: "+args[i+1].trim()); arguments.put(PARAM_DEF_FILE, args[i+1].trim()); i++; } else { // Keine Quelldatei angeben System.out.println("Keine Definitionsdatei angegeben!"); arguments.put(PARAM_DEF_FILE, null); } } else if(args[i].equalsIgnoreCase(PARAM_BI_LEXER) || args[i].equalsIgnoreCase(PARAM_BII_LEXER)) { String lexerType = args[i]; System.out.println("Benutze "+lexerType+" Lexer"); // Mehrfacheinträge vermeiden arguments.remove(PARAM_BI_LEXER); arguments.remove(PARAM_BII_LEXER); arguments.put(lexerType, null); } else if(args[i].equalsIgnoreCase(PARAM_REBUILD_DFA)) { String rebuild = args[i]; // Mehrfacheinträge vermeiden arguments.remove(PARAM_REBUILD_DFA); arguments.put(rebuild, null); } else if(args[i].equalsIgnoreCase(PARAM_LL_PARSER) || args[i].equalsIgnoreCase(PARAM_LR_PARSER)) { // Der Parser wird festgelegt Prüfe, ob eine Grammatik Datei eingelesen werden soll. String parserType = args[i]; // Mehrfacheinträge vermeiden arguments.remove(PARAM_LR_PARSER); arguments.remove(PARAM_LL_PARSER); System.out.println("Benutze "+parserType+" Parser"); if(i+1 < args.length && !args[i+1].trim().startsWith("-")) { String pathToGrammar = args[i+1].trim(); System.out.println("Benutze Grammatik: "+pathToGrammar); arguments.put(parserType, pathToGrammar); i++; } else { // Benutze Default Grammatik System.out.println("Benutze Default Grammatik: "+DEFAULT_GRAMMAR_FILE); arguments.put(parserType, DEFAULT_GRAMMAR_FILE); } } else if(args[i].equalsIgnoreCase(PARAM_LLVM_INPUT_FILE)) { System.out.println("Benutze LLVM Code Datei: "+args[++i]); arguments.put(PARAM_LLVM_INPUT_FILE, args[i]); } else if(args[i].equalsIgnoreCase(PARAM_OUTPUT_FILE)) { System.out.println("Schreibe Maschinen Code in Datei: "+args[++i]); arguments.put(PARAM_OUTPUT_FILE, args[i]); } else if(args[i].equalsIgnoreCase(PARAM_LOG_LEVEL_CONSOLE)) { System.out.println("Setze ConsoleLogLevel auf : "+args[++i]); arguments.put(PARAM_LOG_LEVEL_CONSOLE, args[i]); } else if(args[i].equalsIgnoreCase(PARAM_LOG_LEVEL_FILE)) { System.out.println("Setze LogLevel für Logdatei auf : "+args[++i]); arguments.put(PARAM_LOG_LEVEL_FILE, args[i]); } else if(args[i].equalsIgnoreCase(PARAM_LOG_FILE)) { System.out.println("Benutze Logdatei : "+args[++i]); arguments.put(PARAM_LOG_FILE, args[i]); // -e execute } else if(args[i].equalsIgnoreCase(PARAM_EXEC)) { arguments.put(PARAM_EXEC, "true"); // -C Configdatei } else if(args[i].equalsIgnoreCase(PARAM_CONFIG_FILE)) { if(i+1 < args.length && !args[i+1].trim().startsWith("-")) { String configFile = args[i+1].trim(); System.out.println("Benutze Configfile: "+ configFile); arguments.put(PARAM_CONFIG_FILE, configFile); i++; } else { System.out.println("Keine Configdatei angegeben!"); arguments.put(PARAM_CONFIG_FILE, null); } // -asmType [gnu intel] } else if(args[i].equalsIgnoreCase(PARAM_ASM_TYPE)) { if(i+1 < args.length && ((args[i+1].equalsIgnoreCase("gnu")) || (args[i+1].equalsIgnoreCase("intel")))) { String asmType = args[i+1].trim(); System.out.println("Benutze " + asmType + " Assembler"); arguments.put(PARAM_ASM_TYPE, asmType); i++; } else { System.out.println("AssemblerType nicht erkannt"); arguments.put(PARAM_ASM_TYPE, null); } } else { System.err.println("Unbekannte Option: "+args[i]); } } return arguments; } }