/* * Copyright (C) 2013-2018 Daniel Dietsch (dietsch@informatik.uni-freiburg.de) * Copyright (C) 2013-2015 Jochen Hoenicke (hoenicke@informatik.uni-freiburg.de) * Copyright (C) 2013-2018 University of Freiburg * * This file is part of the ULTIMATE PEAtoBoogie plug-in. * * The ULTIMATE PEAtoBoogie plug-in is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ULTIMATE PEAtoBoogie plug-in 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the ULTIMATE PEAtoBoogie plug-in. If not, see . * * Additional permission under GNU GPL version 3 section 7: * If you modify the ULTIMATE ReqParser plug-in, or any covered work, by linking * or combining it with Eclipse RCP (or a modified version of Eclipse RCP), * containing parts covered by the terms of the Eclipse Public License, the * licensors of the ULTIMATE ReqParer plug-in grant you additional permission * to convey the resulting work. */ package de.uni_freiburg.informatik.ultimate.reqparser; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import de.uni_freiburg.informatik.ultimate.core.lib.models.ObjectContainer; import de.uni_freiburg.informatik.ultimate.core.lib.models.annotation.DefaultLocation; import de.uni_freiburg.informatik.ultimate.core.model.ISource; import de.uni_freiburg.informatik.ultimate.core.model.models.IElement; import de.uni_freiburg.informatik.ultimate.core.model.models.ModelType; import de.uni_freiburg.informatik.ultimate.core.model.preferences.IPreferenceInitializer; import de.uni_freiburg.informatik.ultimate.core.model.services.ILogger; import de.uni_freiburg.informatik.ultimate.core.model.services.IUltimateServiceProvider; import de.uni_freiburg.informatik.ultimate.lib.srparse.Durations; import de.uni_freiburg.informatik.ultimate.lib.srparse.ReqParserResult; import de.uni_freiburg.informatik.ultimate.lib.srparse.pattern.DeclarationPattern; import de.uni_freiburg.informatik.ultimate.lib.srparse.pattern.PatternBuilder; import de.uni_freiburg.informatik.ultimate.lib.srparse.pattern.PatternType; import de.uni_freiburg.informatik.ultimate.util.datastructures.UnionFind; public class ReqParser implements ISource { private ILogger mLogger; private final List mFileNames = new ArrayList<>(); private IUltimateServiceProvider mServices; private ReqParserResultUtil mReporter; @Override public void init() { // not necessary } @Override public String getPluginName() { return Activator.PLUGIN_NAME; } @Override public String getPluginID() { return Activator.PLUGIN_ID; } @Override public File[] parseable(final File[] files) { final List rtrList = Arrays.stream(files).filter(this::parseable).collect(Collectors.toList()); return rtrList.toArray(new File[rtrList.size()]); } public boolean parseable(final File file) { return file.getName().endsWith(".req"); } @Override public IElement parseAST(final File[] files) throws Exception { final List> rawPatterns = new ArrayList<>(); final List durations = new ArrayList<>(); for (final File file : files) { final String filePath = file.getAbsolutePath(); mLogger.info("Parsing file " + filePath); try { final ReqParserResult parserResult = de.uni_freiburg.informatik.ultimate.lib.srparse.ReqParser.parseFile(mLogger, filePath); durations.add(parserResult.getDurations()); final List> pattern = parserResult.getPatterns(); final List> nonNullPatterns = pattern.stream().filter(Objects::nonNull).collect(Collectors.toList()); if (nonNullPatterns.size() != pattern.size()) { int i = 0; PatternType last = null; final StringBuilder sb = new StringBuilder(); for (final PatternType p : pattern) { if (p == null) { sb.append(i).append(' '); if (last == null) { sb.append("(at the beginning)"); } else { sb.append("(after ").append(last.getId()).append("),"); } } else { last = p; } ++i; } mReporter.unexpectedParserFailure(filePath, String.format("%s of %s patterns could not be parsed, positions %s", pattern.size() - nonNullPatterns.size(), pattern.size(), sb.deleteCharAt(sb.length() - 1))); } rawPatterns.addAll(nonNullPatterns); } catch (final Exception ex) { mReporter.unexpectedParserFailure(filePath, String.format("%s: %s", ex.getClass().getSimpleName(), ex.getMessage())); throw ex; } } if (mReporter.isAlreadyAborted()) { return null; } logPatternSize(rawPatterns, "in total"); final List> unifiedPatterns = unify(rawPatterns); logPatternSize(unifiedPatterns, "after unification"); final List> durationNormalizedPatterns = normalizeDurations(unifiedPatterns, new Durations().merge(durations)); return new ObjectContainer<>(durationNormalizedPatterns); } private List> normalizeDurations(final List> patterns, final Durations durations) { mLogger.info("Scaling all durations to integer by multiplying with %s", durations.computeScalingFactor()); return patterns.stream().map(a -> PatternBuilder.normalize(a, durations)).collect(Collectors.toList()); } private void logPatternSize(final List> patterns, final String suffix) { final long patternWithId = patterns.stream().filter(a -> !(a instanceof DeclarationPattern)).count(); if (suffix == null) { mLogger.info(String.format("%s requirements (%s non-initialization)", patterns.size(), patternWithId)); } else { mLogger.info(String.format("%s requirements (%s non-initialization) %s", patterns.size(), patternWithId, suffix)); } } private List> unify(final List> patterns) { final List> rtr = new ArrayList<>(); final List init = patterns.stream().filter(DeclarationPattern.class::isInstance) .map(DeclarationPattern.class::cast).collect(Collectors.toList()); final UnionFind uf = createUnionFind(init); checkTypeConflicts(uf.getAllRepresentatives()); rtr.addAll(merge(uf)); final List> reqs = patterns.stream() .filter(a -> a != null && !(a instanceof DeclarationPattern)).collect(Collectors.toList()); if (reqs.isEmpty()) { return rtr; } final UnionFind> ufreq = createUnionFind(reqs); rtr.addAll(merge(ufreq)); return rtr; } private void checkTypeConflicts(final Collection inits) { final Map seen = new HashMap<>(); for (final DeclarationPattern init : inits) { final DeclarationPattern otherInit = seen.put(init.getId(), init); if (otherInit == null) { continue; } if (!Objects.equals(init.getType(), otherInit.getType())) { mReporter.unsupportedSyntaxError(new DummyLocation(), String.format("The initialization patterns %s and %s have conflicting types: %s vs. %s", init.getId(), otherInit.getId(), init.getType(), otherInit.getType())); } } } private List> merge(final UnionFind> ufreq) { final List> rtr = new ArrayList<>(); for (final Set> eqclass : ufreq.getAllEquivalenceClasses()) { if (eqclass.size() == 1) { rtr.addAll(eqclass); continue; } mReporter.mergedRequirements(eqclass); rtr.add(merge(eqclass)); } return rtr; } /** * Create a new pattern from a set of patterns by concatenating their ids. */ private static PatternType merge(final Set> eqclass) { assert eqclass != null && eqclass.size() > 1; final StringBuilder newName = new StringBuilder(); final Iterator> iter = eqclass.iterator(); PatternType current = iter.next(); final Class last = current.getClass(); newName.append(current.getId()); while (iter.hasNext()) { current = iter.next(); if (last != current.getClass()) { throw new AssertionError("Patterns with different types are assumed to be equivalent"); } newName.append('_').append(current.getId()); } return current.rename(newName.toString()); } private > UnionFind createUnionFind(final List patterns) { final UnionFind uf = new UnionFind<>(); for (final T pattern : patterns) { final T rep = uf.findAndConstructEquivalenceClassIfNeeded(pattern); if (rep != pattern) { mReporter.mergedRequirements(pattern, rep); } } return uf; } @Override public String[] getFileTypes() { return new String[] { ".req" }; } @Override public ModelType getOutputDefinition() { try { return new ModelType(getPluginID(), ModelType.Type.OTHER, mFileNames); } catch (final Exception ex) { mLogger.fatal("syntax error: " + ex.getMessage()); return null; } } @Override public void setServices(final IUltimateServiceProvider services) { mServices = services; mLogger = services.getLoggingService().getLogger(Activator.PLUGIN_ID); mReporter = new ReqParserResultUtil(mLogger, mServices); } @Override public void finish() { // not necessary } @Override public IPreferenceInitializer getPreferences() { return null; } private static final class DummyLocation extends DefaultLocation { private static final long serialVersionUID = 1L; public DummyLocation() { super("", -1, 0, 0, 0); } } }