/* * * Copyright 2012 lexergen. * This file is part of lexergen. * * lexergen is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * lexergen is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with lexergen. If not, see <http://www.gnu.org/licenses/>. * * lexergen: * A tool to chunk source code into tokens for further processing in a compiler chain. * * Projectgroup: bi, bii * * Authors: Johannes Dahlke * Benjamin Weißenfels * * Module: Softwareprojekt Übersetzerbau 2012 * * Created: Apr. 2012 * Version: 1.0 * */ package de.fuberlin.bii.regextodfaconverter.directconverter.lrparser; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import de.fuberlin.bii.regextodfaconverter.directconverter.AutomatEventHandler; import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.ContextFreeGrammar; import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.Nonterminal; import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.ProductionRule; import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.RuleElement; import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.Symbol; import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.Terminal; import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.TerminalSet; import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.Terminator; import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.itemset.Lr0Closure; import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.itemset.Lr0Item; import de.fuberlin.bii.utils.Notification; import de.fuberlin.bii.utils.Test; /** * Ein SLR-Parser. Im Gegensatz zum LR(0)-Parser reduziert der SLR-Parser * die Wahrscheinlichkeit für das Auftreten von shift-reduce- und reduce-reduce-Konflikten, * indem die Followmengen bei * der Erzeugung der reduce-Eintrage betrachtet werden. Kommt es so zu keinen * Konflikten, dann liefert die Eigenschaftsabfrage mit * {@link ItemAutomata#isReduceConflictFree()} den Wert true. * * @author Johannes Dahlke * Benjamin Weißenfels * * @param <Element> * der Typ eines Elementes der zu verarbeitenden Eingabe. */ @SuppressWarnings("rawtypes") public class Slr1ItemAutomat<Element extends Symbol> extends Lr0ItemAutomat<Element> { /** * */ private static final long serialVersionUID = -3994075596507437910L; private boolean usePersistentParserTable = false; public Slr1ItemAutomat(ContextFreeGrammar grammar) { super(grammar); } public Slr1ItemAutomat(ContextFreeGrammar grammar, boolean usePersistentParserTable) { super(grammar); this.usePersistentParserTable = usePersistentParserTable; } @SuppressWarnings({ "unchecked"}) @Override protected void SetupParserTable(Lr0Closure startClosure) { if ( usePersistentParserTable && readPersistentParserTable()) { return; } HashSet<Lr0Closure> unhandledClosures = new HashSet<Lr0Closure>() { /** * */ private static final long serialVersionUID = 4319562418085155280L; @Override public boolean contains(Object o) { for (Lr0Closure closure : this) { if (closure.equals((Lr0Closure) o)) return true; } return false; } }; int closureCounter = 0; startClosure.setNumber(closureCounter++); unhandledClosures.add(startClosure); HashMap<Nonterminal, TerminalSet> followSets = grammar.getFollowSets(); RuleElement nextRuleElement; while (unhandledClosures.size() > 0) { Lr0Closure currentClosure = unhandledClosures.iterator().next(); Map<RuleElement, AutomatEventHandler> handlerMap = new HandlerMap(); for (Lr0Item item : currentClosure.getItemSet()) { nextRuleElement = item.peekNextRuleElement(); if (Test.isAssigned(nextRuleElement)) { if (nextRuleElement instanceof Terminal) { // add shift actions Lr0Closure toClosure = gotoNextClosure(currentClosure, nextRuleElement, this.grammar); if (!parserTable.containsKey(toClosure) && !unhandledClosures.contains(toClosure)) { toClosure.setNumber(closureCounter++); unhandledClosures.add(toClosure); } ShiftAction<Element, Lr0Closure> shiftAction = new ShiftAction<Element, Lr0Closure>( toClosure, (Terminal<Element>) nextRuleElement); if ((handlerMap.containsKey(nextRuleElement) && !handlerMap .get(nextRuleElement).equals(shiftAction))) { isReduceConflictFree = false; // Conflict happen. Store the conflicting action as // alternative for later error handling if (handlerMap.get(nextRuleElement) instanceof Action) { Action seniorAction = shiftAction; seniorAction.addAlternative((Action) handlerMap .get(nextRuleElement)); handlerMap.put(nextRuleElement, seniorAction); } } else { // otherwise there is not yet defined an action. So // we add one handlerMap.put(nextRuleElement, shiftAction); } } else if (nextRuleElement instanceof Nonterminal) { // add goto's Lr0Closure toClosure = gotoNextClosure(currentClosure, nextRuleElement, this.grammar); if (!parserTable.containsKey(toClosure) && !unhandledClosures.contains(toClosure)) { toClosure.setNumber(closureCounter++); unhandledClosures.add(toClosure); } Goto gotoHandler = new Goto(toClosure, (Nonterminal) nextRuleElement); // check for multiples with gotos not necessary handlerMap.put(nextRuleElement, gotoHandler); } } else { if (item.toProduction().equals(startProduction)) // add accept action handlerMap.put(new Terminator(), new AcceptAction()); else { // add reduce actions ProductionRule reduceProduction = item.toProduction(); Nonterminal nonterminal = reduceProduction .getLeftRuleSide(); Set<Terminal> followSet = followSets.get(nonterminal); ReduceAction<Element, Lr0Closure> reduceAction = new ReduceAction<Element, Lr0Closure>( reduceProduction); for (Terminal<Element> terminal : followSet) { if ((handlerMap.containsKey(terminal) && !handlerMap .get(terminal).equals(reduceAction))) { isReduceConflictFree = false; // Conflict happen. Store the conflicting action // as alternative for later error handling if (handlerMap.get(terminal) instanceof Action) { Action seniorAction = (Action) handlerMap .get(terminal); seniorAction.addAlternative(reduceAction); } } else { // otherwise there is not yet defined an action. // So we add one handlerMap.put(terminal, reduceAction); } } } } } unhandledClosures.remove(currentClosure); parserTable.put(currentClosure, handlerMap); } if ( usePersistentParserTable) { writePersistentParserTable(); } } @SuppressWarnings("unchecked") private boolean readPersistentParserTable() { File dir = new File("/tmp/lexergen/"); File parserTableObject = new File("/tmp/lexergen/parserTable"); long start; long end; long length; if (dir.exists() && dir.isDirectory() && parserTableObject.exists()) { try { FileInputStream fInp = new FileInputStream(parserTableObject); ObjectInputStream inp = new ObjectInputStream(fInp); // benchmark reading process start = System.currentTimeMillis(); Object o = inp.readObject(); inp.close(); end = System.currentTimeMillis(); length = (end - start); Notification.printDebugInfoMessage("Read parser table in " + (length / 1000) + "." + (length % 1000) + "s"); this.parserTable = (Map<Lr0Closure, Map<RuleElement, AutomatEventHandler>>) o; return true; } catch (Exception e) { Notification .printErrorMessage("could not find or read parser table object"); e.printStackTrace(); } } return false; } private void writePersistentParserTable() { File dir = new File("/tmp/lexergen/"); File parserTableObject = new File("/tmp/lexergen/parserTable"); dir.mkdirs(); ObjectOutputStream out; try { out = new ObjectOutputStream( new FileOutputStream(parserTableObject)); out.writeObject(parserTable); out.flush(); out.close(); } catch (Exception e) { Notification .printErrorMessage("problems with writing back parser table"); parserTableObject.delete(); e.printStackTrace(); } } }