/* * * 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: Daniel Rotar * * Module: Softwareprojekt Übersetzerbau 2012 * * Created: Apr. 2012 * Version: 1.0 * */ package de.fuberlin.bii.regextodfaconverter; import java.io.Serializable; import de.fuberlin.bii.regextodfaconverter.fsm.FiniteStateMachine; import de.fuberlin.bii.regextodfaconverter.fsm.State; /** * Stellt einen Konverter dar, der aus einem regulären Ausdruck einen * nichtdeterministischen endlichen Automaten (nondeterministic finite * automaton, kurz NFA) erstellt. * * @author Daniel Rotar * * @param <StatePayloadType> * Der Typ des Inhalts der Zustände beim verwendeten endlichen * Automaten. */ public class RegexToNfaConverter<StatePayloadType extends Serializable> { /** * Erstellt aus dem angegebenen regulären Ausdruck einen * nichtdeterministischen endlichen Automaten (nondeterministic finite * automaton, kurz NFA). * * @param Regex * Der reguläre Ausdruck, aus dem der NFA erstellt werden soll. * @param payload * Der Inhalt, der in jedem Endzustand verknüpft werden soll. * @return Der NFA, der durch den regulären Ausdruck abgebildet wird. Bei * einem leeren Regex wird null zurückgegeben. * @remakrs Es werden nur die folgenden regulären Muster unterstützt: A|B, * AB, A* * @throws ConvertExecption * Wenn ein Fehler beim Übersetzen des regulären Asudrucks in * einen NFA auftritt. */ public FiniteStateMachine<Character, StatePayloadType> convertToNFA( String regex, StatePayloadType payload) throws ConvertExecption { // Angegebenen Regex aif Basisoperationen reduzieren und klammern. try { regex = Regex.reduceAndBracketRegex(regex); } catch (RegexInvalidException e) { throw new ConvertExecption( "Der verwendete reguläre Ausdruck '" + regex + "' ist ungültig oder verwendet nicht unterstütze Operatoren!"); } return convertRekursivToNFA(regex, payload); } /** * Erstellt aus dem angegebenen regulären Ausdruck einen * nichtdeterministischen endlichen Automaten (nondeterministic finite * automaton, kurz NFA). * * @param Regex * Der reguläre Ausdruck, aus dem der NFA erstellt werden soll. * @return Der NFA, der durch den regulären Ausdruck abgebildet wird. Bei * einem leeren Regex wird null zurückgegeben. * @remakrs Es werden nur die folgenden regulären Muster unterstützt: A|B, * AB, A* * @throws ConvertExecption * Wenn ein Fehler beim Übersetzen des regulären Asudrucks in * einen NFA auftritt. */ public FiniteStateMachine<Character, StatePayloadType> convertToNFA( String regex) throws ConvertExecption { return convertToNFA(regex, null); } /** * Erstellt aus dem angegebenen regulären Ausdruck rekursiv einen * nichtdeterministischen endlichen Automaten (nondeterministic finite * automaton, kurz NFA). * * @param Regex * Der reguläre Ausdruck, aus dem der NFA erstellt werden soll. * @param payload * Der Inhalt, der in jedem Endzustand verknüpft werden soll. * @return Der NFA, der durch den regulären Ausdruck abgebildet wird. Bei * einem leeren Regex wird null zurückgegeben. * @remakrs Es werden nur die folgenden regulären Muster unterstützt: A|B, * AB, A* * @throws ConvertExecption * Wenn ein Fehler beim Übersetzen des regulären Asudrucks in * einen NFA auftritt. */ private FiniteStateMachine<Character, StatePayloadType> convertRekursivToNFA( String regex, StatePayloadType payload) throws ConvertExecption { FiniteStateMachine<Character, StatePayloadType> fsm = null; if (regex.length() == 0) { fsm = createSimpleNfa(null, payload); } else if (regex.length() == 1) { fsm = createSimpleNfa(regex.charAt(0), payload); } else if (regex.length() == 2 && regex.startsWith("\\")) { fsm = createSimpleNfa(regex.charAt(1), payload); } else if (regex.charAt(regex.length() - 1) == '*') { fsm = convertRekursivToNFA(regex.substring(1, regex.length() - 2), payload); fsm.closure(); } else { // (...)|(...) oder (...)(...) oder (...) StringBuilder sb = new StringBuilder(""); int opened = 1; for (int i = 1; i < regex.length(); i++) { char c = regex.charAt(i); if (c == '\\') { sb.append(c); if (i + 1 < regex.length()) { sb.append(regex.charAt(i + 1)); } i++; } else if (c == '(') { sb.append(c); opened++; } else if (c == ')') { opened--; if (opened == 0) { break; } else { sb.append(c); } } else { sb.append(c); } } if (sb.toString().length() == regex.length() - 2) { // Fall (...) fsm = convertRekursivToNFA(sb.toString(), payload); } else { char c = regex.charAt(sb.toString().length() + 2); if (c == '|') { // Fall (...)|(...) fsm = convertRekursivToNFA(sb.toString(), payload); fsm.union(convertRekursivToNFA( regex.substring(sb.toString().length() + 4, regex.length() - 1), payload)); } else if (c == '(') { // Fall (...)(...) fsm = convertRekursivToNFA(sb.toString(), payload); fsm.concat(convertRekursivToNFA( regex.substring(sb.toString().length() + 3, regex.length() - 1), payload)); } } } NfaToDfaConverter<Character, StatePayloadType> converter = new NfaToDfaConverter<Character, StatePayloadType>(); fsm = converter.convertToDfa(fsm); return fsm; } /** * Erstellt eine einfachen nichtdeterministischen endlichen Automaten * (Startzustand,Bedingung,Endzustand) mit der angegebenen Bedingung für den * Übergang. * * @param condition * Die Bedingung für den Übergang. * @param payload * Der Inhalt des Endzustands. * @return Eine einfacher nichtdeterministischen endlichen Automaten * (Startzustand,Bedingung,Endzustand) mit der angegebenen Bedingung * für den Übergang. */ private FiniteStateMachine<Character, StatePayloadType> createSimpleNfa( Character condition, StatePayloadType payload) { FiniteStateMachine<Character, StatePayloadType> nfa = new FiniteStateMachine<Character, StatePayloadType>(); State<Character, StatePayloadType> state1; State<Character, StatePayloadType> state2; state1 = nfa.getCurrentState(); state2 = new State<Character, StatePayloadType>(payload, true); try { nfa.addTransition(state1, state2, condition); } catch (Exception e) { // Dieser Fall kann nicht eintreten! } return nfa; } }