package de.fuberlin.projectci.lrparser; import java.io.File; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import de.fuberlin.commons.parser.IParser; import de.fuberlin.commons.util.LogFactory; import de.fuberlin.commons.lexer.ILexer; import de.fuberlin.commons.parser.ISyntaxTree; import de.fuberlin.projectci.grammar.BNFGrammarReader; import de.fuberlin.projectci.grammar.BNFParsingErrorException; import de.fuberlin.projectci.grammar.Grammar; import de.fuberlin.projectci.grammar.GrammarReader; import de.fuberlin.projectci.grammar.NonTerminalSymbol; import de.fuberlin.projectci.grammar.Production; import de.fuberlin.projectci.grammar.Symbol; import de.fuberlin.projectci.gui.ParseTableGui; import de.fuberlin.projectci.parseTable.InvalidGrammarException; import de.fuberlin.projectci.parseTable.ParseTable; import de.fuberlin.projectci.parseTable.ParseTableBuilder; /** * * */ public class LRParser implements IParser { private static Logger logger = LogFactory.getLogger(LRParser.class); // Optionen mit Default-Einstellungen private boolean reduceToAbstractSyntaxTree=false; private boolean removeEpsilonNodes=true; // Der SemanticAnalyzer erwartet einen Parsebaum ohne Epsilon-Knoten private boolean displayParseTableGui=false; public LRParser() { } /** * Ermöglicht das Reduzieren des SyntaxTree durch "Hochziehen aller Einzelkinder". * @param reduceToAbstractSyntaxTree */ public void setReduceToAbstractSyntaxTree(boolean reduceToAbstractSyntaxTree) { this.reduceToAbstractSyntaxTree = reduceToAbstractSyntaxTree; } /** * Ermöglicht das Entfernen aller Epsilon-Knoten (Blätter) aus dem SyntaxTree. * @param removeEpsilonNodes */ public void setRemoveEpsilonNodes(boolean removeEpsilonNodes) { this.removeEpsilonNodes = removeEpsilonNodes; } /** * Ermöglicht das Öffnen einer Swing-GUI zum Darstellen der Parsetabelle. * @param displayParseTableGui */ public void setDisplayParseTableGui(boolean displayParseTableGui) { this.displayParseTableGui = displayParseTableGui; } /** * Führt das Parsen durch * @param lexer ILexer für die Eingabe * @param grammarPath Pfad zur Grammtikdatei */ public ISyntaxTree parse(ILexer lexer, String grammarPath) { File grammarFile = new File(grammarPath); Grammar grammar = null; // Grammatik einlesen try { GrammarReader grammarReader=new BNFGrammarReader(); grammar=grammarReader.readGrammar(grammarFile.getAbsolutePath()); } catch (BNFParsingErrorException e) { logger.log(Level.WARNING, "Failed to read grammar from file: "+grammarFile.getAbsolutePath(), e); throw new LRParserException("Failed to read grammar from file: "+grammarFile.getAbsolutePath(),e); } return parse(lexer, grammar); } /** * Führt das Parsen durch * @param lexer ILexer für die Eingabe * @param grammar Grammtik */ public ISyntaxTree parse(ILexer lexer, Grammar grammar) { logger.fine("Start parsing..."); ParseTable parseTable = null; // Grammatik erweitern extendGrammar(grammar); // ParseTable erstellen ParseTableBuilder parseTableBuilder=ParseTableBuilder.createParseTableBuilder(grammar); try { parseTable=parseTableBuilder.buildParseTable(); } catch (InvalidGrammarException e) { logger.log(Level.WARNING, "Failed to build ParseTable for grammar.", e); throw new LRParserException("Failed to build ParseTable for grammar.", e); } if (displayParseTableGui){ ParseTableGui parseTableGui=new ParseTableGui(grammar, parseTable); parseTableGui.showActionTable(); parseTableGui.showGotoTable(); } Driver driver=new Driver(); ISyntaxTree syntaxTree= driver.parse(lexer, parseTable); if (syntaxTree==null){ logger.warning("LRParser failed to create syntax tree."); throw new LRParserException("LRParser failed to create syntax tree."); } if (reduceToAbstractSyntaxTree){ ((SyntaxTreeNode)syntaxTree).reduceToAbstractSyntaxTree(); } else if (removeEpsilonNodes){ ((SyntaxTreeNode)syntaxTree).removeAllEpsilonNodes(); } logger.info("Parsing succeed."); return syntaxTree; } /** * Erweitert die übergebene Grammatik mit einem neuen Startsymbol * und einer neuen Produktion vom neuen Startsymbol auf das alte Startsymbol. * @param grammar eine Grammatik, die erweitert werden soll */ private void extendGrammar(Grammar grammar){ // alle Namen der Nichtterminale aus der Grammatik holen Set<String> nonTerminalsNames = grammar.getAllNonterminalNames(); NonTerminalSymbol oldStartSymbol = grammar.getStartSymbol(); String freeName = null; // zuerst folgende Namen für neues Startsymbol probieren String[] firstChoiceStartSymbols = new String[]{"S0","S'","Start","Startsymbol","start"}; for(String s : firstChoiceStartSymbols){ if(!nonTerminalsNames.contains(s)){ freeName = s; break; } } // ansonsten Zufallsname "S"+TIMESTAMP probieren while(freeName == null){ String s = "S"+System.currentTimeMillis(); if(!nonTerminalsNames.contains(s)) freeName = s; } // neues Nichtterminal erzeugen und als Startsymbol festlegen NonTerminalSymbol startSymbol = grammar.createNonTerminalSymbol(freeName); // neue Produktion vom neuen zum alten Startsymbol anlegen List<Symbol> rhs = new LinkedList<Symbol>(); rhs.add(oldStartSymbol); Production production = new Production(startSymbol, rhs); grammar.addProduction(production); grammar.setStartSymbol(startSymbol); } }