From 37d302bbb3c6c24b4230ee432c84ead9aa25af69 Mon Sep 17 00:00:00 2001
From: Jan Travnicek <Jan.Travnicek@fit.cvut.cz>
Date: Mon, 30 Jun 2014 20:36:36 +0200
Subject: [PATCH] ENFA to StringParser and Composer

---
 alib2/src/alphabet/SymbolFromStringParser.h   |   7 +
 .../FSM/FiniteAutomatonFromStringLexer.cpp    |  85 ++++++++
 .../FSM/FiniteAutomatonFromStringLexer.h      |  42 ++++
 .../FSM/FiniteAutomatonFromStringParser.cpp   | 193 ++++++++++++++++++
 .../FSM/FiniteAutomatonFromStringParser.h     |  43 ++++
 .../FSM/FiniteAutomatonToStringComposer.cpp   | 125 ++++++++++++
 .../FSM/FiniteAutomatonToStringComposer.h     |  42 ++++
 alib2/src/label/LabelFromStringParser.h       |   7 +
 alib2/test-src/automaton/AutomatonTest.cpp    |  27 +++
 alib2/test-src/automaton/AutomatonTest.h      |   2 +
 10 files changed, 573 insertions(+)
 create mode 100644 alib2/src/automaton/FSM/FiniteAutomatonFromStringLexer.cpp
 create mode 100644 alib2/src/automaton/FSM/FiniteAutomatonFromStringLexer.h
 create mode 100644 alib2/src/automaton/FSM/FiniteAutomatonFromStringParser.cpp
 create mode 100644 alib2/src/automaton/FSM/FiniteAutomatonFromStringParser.h
 create mode 100644 alib2/src/automaton/FSM/FiniteAutomatonToStringComposer.cpp
 create mode 100644 alib2/src/automaton/FSM/FiniteAutomatonToStringComposer.h

diff --git a/alib2/src/alphabet/SymbolFromStringParser.h b/alib2/src/alphabet/SymbolFromStringParser.h
index 59050e04eb..f62d3b8fc5 100644
--- a/alib2/src/alphabet/SymbolFromStringParser.h
+++ b/alib2/src/alphabet/SymbolFromStringParser.h
@@ -27,6 +27,12 @@ class RegExpFromStringParser;
 
 } /* namespace regexp */
 
+namespace automaton {
+
+class FiniteAutomatonFromStringParser;
+
+} /* namespace automaton */
+
 namespace alphabet {
 
 class SymbolFromStringParser {
@@ -43,6 +49,7 @@ public:
 	Symbol parseValue();
 	friend class string::StringFromStringParser;
 	friend class regexp::RegExpFromStringParser;
+	friend class automaton::FiniteAutomatonFromStringParser;
 };
 
 } /* namespace alphabet */
diff --git a/alib2/src/automaton/FSM/FiniteAutomatonFromStringLexer.cpp b/alib2/src/automaton/FSM/FiniteAutomatonFromStringLexer.cpp
new file mode 100644
index 0000000000..11a14a6360
--- /dev/null
+++ b/alib2/src/automaton/FSM/FiniteAutomatonFromStringLexer.cpp
@@ -0,0 +1,85 @@
+#include "FiniteAutomatonFromStringLexer.h"
+
+namespace automaton {
+
+FiniteAutomatonFromStringLexer::FiniteAutomatonFromStringLexer(std::stringstream& in) : m_In(in) {
+	m_Current.type = TokenType::ERROR;
+	m_Current.value = "";
+}
+
+FiniteAutomatonFromStringLexer& FiniteAutomatonFromStringLexer::next() {
+	char character;
+	m_Current.value = "";
+
+	std::streampos pos = m_In.tellg();
+L0:
+	character = m_In.get();
+	if(m_In.eof()) {
+		m_In.seekg(pos);
+		m_Current.type = TokenType::TEOF;
+		return *this;
+	} else if(character == ' ' || character == '\t') {
+		goto L0;
+	} else if(character == '\n') {
+		m_Current.type = TokenType::NEW_LINE;
+		m_Current.value += character;
+		return *this;
+	} else if(character == '<') {
+		m_Current.type = TokenType::OUT;
+		m_Current.value += character;
+		return *this;
+	} else if(character == '>') {
+		m_Current.type = TokenType::IN;
+		m_Current.value += character;
+		return *this;
+	} else if(character == '|') {
+		m_Current.type = TokenType::SEPARATOR;
+		m_Current.value += character;
+		return *this;
+	} else if(character == '-') {
+		m_Current.type = TokenType::NONE;
+		m_Current.value += character;
+		return *this;
+	} else if(character == '#') {
+		m_Current.value += character;
+		goto L1;
+	} else if(m_In.seekg(pos), m_In >> "ENFA") {
+		m_Current.type = TokenType::EPSILON_NFA;
+		m_Current.value = "ENFA";
+		return *this;
+	} else if(m_In.clear(), m_In >> "NFA") {
+		m_Current.type = TokenType::NFA;
+		m_Current.value = "NFA";
+		return *this;
+	} else if(m_In.clear(), m_In >> "DFA") {
+		m_Current.type = TokenType::DFA;
+		m_Current.value = "DFA";
+		return *this;
+	} else {
+		m_In.clear();
+		m_In.seekg(pos);
+		m_Current.type = TokenType::ERROR;
+		return *this;
+	}
+L1:
+	character = m_In.get();
+	if(m_In.eof()) {
+		m_In.seekg(pos);
+		m_Current.type = TokenType::ERROR;
+		return *this;
+	} else if(character == 'E') {
+		m_Current.value += character;
+		m_Current.type = TokenType::EPSILON;
+		return *this;
+	} else {
+		m_In.seekg(pos);
+		m_Current.type = TokenType::ERROR;
+		return *this;
+	}
+}
+
+FiniteAutomatonFromStringLexer::Token FiniteAutomatonFromStringLexer::token() {
+	return m_Current;
+}
+
+} /* namespace automaton */
diff --git a/alib2/src/automaton/FSM/FiniteAutomatonFromStringLexer.h b/alib2/src/automaton/FSM/FiniteAutomatonFromStringLexer.h
new file mode 100644
index 0000000000..1683ee5c15
--- /dev/null
+++ b/alib2/src/automaton/FSM/FiniteAutomatonFromStringLexer.h
@@ -0,0 +1,42 @@
+#ifndef FINITE_AUTOMATON_FROM_STRING_LEXER_H_
+#define FINITE_AUTOMATON_FROM_STRING_LEXER_H_
+
+#include <string>
+#include <sstream>
+#include "../../std/istream.h"
+
+namespace automaton {
+
+class FiniteAutomatonFromStringLexer {
+public:
+	enum class TokenType {
+		EPSILON_NFA,
+		NFA,
+		DFA,
+		OUT,
+		IN,
+		EPSILON,
+		SEPARATOR,
+		NONE,
+		NEW_LINE,
+		TEOF,
+		ERROR
+	};
+
+	struct Token {
+		TokenType type;
+		std::string value;
+	};
+private:
+	std::stringstream& m_In;
+	Token m_Current;
+public:
+
+	FiniteAutomatonFromStringLexer(std::stringstream&);
+	FiniteAutomatonFromStringLexer& next();
+	Token token();
+};
+
+} /* namepsace automaton */
+
+#endif /* FINITE_AUTOMATON_FROM_STRING_LEXER_H_ */
diff --git a/alib2/src/automaton/FSM/FiniteAutomatonFromStringParser.cpp b/alib2/src/automaton/FSM/FiniteAutomatonFromStringParser.cpp
new file mode 100644
index 0000000000..f2bcd8e838
--- /dev/null
+++ b/alib2/src/automaton/FSM/FiniteAutomatonFromStringParser.cpp
@@ -0,0 +1,193 @@
+#include "FiniteAutomatonFromStringParser.h"
+
+#include "../../AlibException.h"
+
+#include "EpsilonNFA.h"
+#include "NFA.h"
+#include "DFA.h"
+
+#include "../../label/Label.h"
+#include "../../label/IntegerLabel.h"
+
+namespace automaton {
+
+FiniteAutomatonFromStringParser::FiniteAutomatonFromStringParser(std::stringstream& input) : m_FiniteAutomatonLexer(input), m_SymbolParser(input), m_LabelParser(input) {
+
+}
+
+Automaton FiniteAutomatonFromStringParser::parse() {
+	return parse(std::set<FEATURES>({FEATURES::EPSILON_NFA, FEATURES::NFA, FEATURES::DFA}));
+}
+
+Automaton FiniteAutomatonFromStringParser::parse(const std::set<FEATURES>& features) {
+	FiniteAutomatonFromStringLexer::Token token = m_FiniteAutomatonLexer.token();
+	if(token.type == FiniteAutomatonFromStringLexer::TokenType::EPSILON_NFA) {
+		if(!features.count(FEATURES::EPSILON_NFA)) throw alib::AlibException();
+		return Automaton(parseEpsilonNFA());
+	} else if(token.type == FiniteAutomatonFromStringLexer::TokenType::NFA) {
+		if(!features.count(FEATURES::NFA)) throw alib::AlibException();
+		return Automaton(parseNFA());
+	} else if(token.type == FiniteAutomatonFromStringLexer::TokenType::DFA) {
+		if(!features.count(FEATURES::DFA)) throw alib::AlibException();
+		return Automaton(parseDFA());
+	} else {
+		throw alib::AlibException();
+	}
+}
+
+Automaton FiniteAutomatonFromStringParser::parseValue() {
+	first();
+	Automaton res = parse();
+
+	FiniteAutomatonFromStringLexer::Token token = m_FiniteAutomatonLexer.token();
+	if(token.type == FiniteAutomatonFromStringLexer::TokenType::TEOF) {
+		return std::move(res);
+	} else {
+		throw alib::AlibException();
+	}
+}
+
+bool FiniteAutomatonFromStringParser::first() {
+	FiniteAutomatonFromStringLexer::Token token = m_FiniteAutomatonLexer.next().token();
+	if(token.type == FiniteAutomatonFromStringLexer::TokenType::EPSILON_NFA || token.type == FiniteAutomatonFromStringLexer::TokenType::NFA || token.type == FiniteAutomatonFromStringLexer::TokenType::DFA) {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+bool FiniteAutomatonFromStringParser::next() {
+	FiniteAutomatonFromStringLexer::Token token = m_FiniteAutomatonLexer.next().token();
+	if(token.type == FiniteAutomatonFromStringLexer::TokenType::EPSILON_NFA || token.type == FiniteAutomatonFromStringLexer::TokenType::NFA || token.type == FiniteAutomatonFromStringLexer::TokenType::DFA || token.type == FiniteAutomatonFromStringLexer::TokenType::OUT || token.type == FiniteAutomatonFromStringLexer::TokenType::IN || token.type == FiniteAutomatonFromStringLexer::TokenType::EPSILON || token.type == FiniteAutomatonFromStringLexer::TokenType::SEPARATOR || token.type == FiniteAutomatonFromStringLexer::TokenType::NONE || token.type == FiniteAutomatonFromStringLexer::TokenType::NEW_LINE) {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+Automaton* FiniteAutomatonFromStringParser::parsePointer() {
+	if(first()) {
+		return new Automaton(parse());
+	} else {
+		return NULL;
+	}
+}
+
+EpsilonNFA FiniteAutomatonFromStringParser::parseEpsilonNFA() {
+	EpsilonNFA res;
+
+	FiniteAutomatonFromStringLexer::Token token = m_FiniteAutomatonLexer.token();
+	if(token.type != FiniteAutomatonFromStringLexer::TokenType::EPSILON_NFA) {
+		throw alib::AlibException();
+	}
+	std::vector<std::variant<string::Epsilon, alphabet::Symbol> > symbols;
+
+	next() || m_SymbolParser.first() || m_SymbolParser.m_LabelParser.first();
+	token = m_FiniteAutomatonLexer.token();
+	while(token.type != FiniteAutomatonFromStringLexer::TokenType::NEW_LINE) {
+		if(token.type == FiniteAutomatonFromStringLexer::TokenType::ERROR) {
+			alphabet::Symbol symbol = m_SymbolParser.parse();
+			res.addInputSymbol(symbol);
+
+			std::variant<string::Epsilon, alphabet::Symbol> symbolVariant;
+			symbolVariant.set<alphabet::Symbol>(symbol);
+			symbols.push_back(symbolVariant);
+		} else if(token.type == FiniteAutomatonFromStringLexer::TokenType::EPSILON) {
+			symbols.push_back(std::variant<string::Epsilon, alphabet::Symbol>());
+		}
+
+		next() || m_SymbolParser.first() || m_SymbolParser.m_LabelParser.first();
+		token = m_FiniteAutomatonLexer.token();
+	}
+
+	token = m_FiniteAutomatonLexer.token();
+	if(token.type != FiniteAutomatonFromStringLexer::TokenType::NEW_LINE) throw alib::AlibException();
+	
+	next() || m_LabelParser.first();
+	parseEpsilonNFATransition(res, symbols);
+	token = m_FiniteAutomatonLexer.token();
+
+	while(token.type == FiniteAutomatonFromStringLexer::TokenType::NEW_LINE) {
+		next() || m_LabelParser.first();
+		token = m_FiniteAutomatonLexer.token();
+		if(token.type == FiniteAutomatonFromStringLexer::TokenType::TEOF) break;
+
+		parseEpsilonNFATransition(res, symbols);
+		token = m_FiniteAutomatonLexer.token();
+	}
+
+	return  res;
+}
+
+NFA FiniteAutomatonFromStringParser::parseNFA() {
+	NFA res;
+
+	return res;
+}
+
+DFA FiniteAutomatonFromStringParser::parseDFA() {
+	DFA res(State(label::Label(label::IntegerLabel(0))));;
+
+	return res;
+}
+
+void FiniteAutomatonFromStringParser::parseEpsilonNFATransition(EpsilonNFA& res, const std::vector<std::variant<string::Epsilon, alphabet::Symbol> >& symbols) {
+	bool initial = false;
+	bool final = false;
+
+	FiniteAutomatonFromStringLexer::Token token = m_FiniteAutomatonLexer.token();
+	if(token.type == FiniteAutomatonFromStringLexer::TokenType::IN) {
+		initial = true;
+		next() || m_LabelParser.first();
+		token = m_FiniteAutomatonLexer.token();
+
+		if(token.type == FiniteAutomatonFromStringLexer::TokenType::OUT) {
+			final = true;
+			next() || m_LabelParser.first();
+		}
+	} else if(token.type == FiniteAutomatonFromStringLexer::TokenType::OUT) {
+		final = true;
+		next() || m_LabelParser.first();
+		token = m_FiniteAutomatonLexer.token();
+
+		if(token.type == FiniteAutomatonFromStringLexer::TokenType::IN) {
+			initial = true;
+			next() || m_LabelParser.first();
+		}
+	}
+
+	State from(m_LabelParser.parse());
+	res.addState(from);
+	if(initial) res.addInitialState(from);
+	if(final) res.addFinalState(from);
+
+	next() || m_LabelParser.first();
+
+	std::vector<std::variant<string::Epsilon, alphabet::Symbol>>::const_iterator iter = symbols.begin();
+	do {
+		if(iter == symbols.end()) throw alib::AlibException();
+
+		token = m_FiniteAutomatonLexer.token();
+		if(token.type != FiniteAutomatonFromStringLexer::TokenType::NONE) {
+		
+			do {
+				State to(m_LabelParser.parse());
+				res.addState(to);
+				res.addTransition(from, *iter, to);
+
+				next() || m_LabelParser.first();
+				token = m_FiniteAutomatonLexer.token();
+				if(token.type != FiniteAutomatonFromStringLexer::TokenType::SEPARATOR) break;
+
+				next() || m_LabelParser.first();
+			} while(true);
+		} else {
+			next() || m_LabelParser.first();
+			token = m_FiniteAutomatonLexer.token();
+		}
+		iter++;
+	} while(token.type != FiniteAutomatonFromStringLexer::TokenType::NEW_LINE);
+	if(iter != symbols.end()) throw alib::AlibException();
+}
+
+} /* namespace automaton */
diff --git a/alib2/src/automaton/FSM/FiniteAutomatonFromStringParser.h b/alib2/src/automaton/FSM/FiniteAutomatonFromStringParser.h
new file mode 100644
index 0000000000..9b35c28dd7
--- /dev/null
+++ b/alib2/src/automaton/FSM/FiniteAutomatonFromStringParser.h
@@ -0,0 +1,43 @@
+#ifndef FINITE_AUTOMATON_FROM_STRING_PARSER_H_
+#define FINITE_AUTOMATON_FROM_STRING_PARSER_H_
+
+#include "FiniteAutomatonFromStringLexer.h"
+#include "../../alphabet/SymbolFromStringParser.h"
+#include "../../label/LabelFromStringParser.h"
+
+#include "../Automaton.h"
+#include "../AutomatonFeatures.h"
+
+#include "../../std/variant.hpp"
+
+#include "../../string/Epsilon.h"
+
+namespace automaton {
+
+class FiniteAutomatonFromStringParser {
+	FiniteAutomatonFromStringLexer m_FiniteAutomatonLexer;
+	alphabet::SymbolFromStringParser m_SymbolParser;
+	label::LabelFromStringParser m_LabelParser;
+
+	Automaton parse();
+	Automaton parse(const std::set<FEATURES>& features);
+	bool next();
+
+	EpsilonNFA parseEpsilonNFA();
+	NFA parseNFA();
+	DFA parseDFA();
+	void parseEpsilonNFATransition(EpsilonNFA& res, const std::vector<std::variant<string::Epsilon, alphabet::Symbol> >& symbols);
+	void parseNFATransition(NFA& res, const std::vector<alphabet::Symbol> symbols);
+	void parseDFATransition(DFA& res, const std::vector<alphabet::Symbol> symbols);
+public:
+	FiniteAutomatonFromStringParser(std::stringstream& input);
+
+	Automaton parseValue();
+	bool first();
+	Automaton* parsePointer();
+
+};
+
+} /* namespace automaton */
+
+#endif /* FINITE_AUTOMATON_FROM_STRING_PARSER_H_ */
diff --git a/alib2/src/automaton/FSM/FiniteAutomatonToStringComposer.cpp b/alib2/src/automaton/FSM/FiniteAutomatonToStringComposer.cpp
new file mode 100644
index 0000000000..c024932d7a
--- /dev/null
+++ b/alib2/src/automaton/FSM/FiniteAutomatonToStringComposer.cpp
@@ -0,0 +1,125 @@
+#include "FiniteAutomatonToStringComposer.h"
+#include "../../AlibException.h"
+
+#include "../../alphabet/SymbolToStringComposer.h"
+#include "../../label/LabelToStringComposer.h"
+
+namespace automaton {
+
+void FiniteAutomatonToStringComposer::composeTransitionsFromState(std::stringstream& out, const EpsilonNFA& automaton, const State& from) const {
+	std::map<std::pair<State, alphabet::Symbol>, std::set<State> > symbolTransitionsFromState = automaton.getSymbolTransitionsFromState(from);
+
+	for(const alphabet::Symbol& inputSymbol : automaton.getInputAlphabet()) {
+		const std::map<std::pair<State, alphabet::Symbol>, std::set<State> >::iterator toStates = symbolTransitionsFromState.find(std::make_pair(from, inputSymbol));
+		if(toStates == symbolTransitionsFromState.end() || toStates->second.size() == 0) {
+			out << " -";
+		} else {
+			bool sign = false;
+			for(const State& to : toStates->second) {
+				label::LabelToStringComposer composer;
+				out << (sign ? "|" : " ") << composer.compose(to.getName());
+				sign = true;
+			}
+		}
+	}
+
+	std::map<State, std::set<State> > epsilonTransitionsFromState = automaton.getEpsilonTransitionsFromState(from);
+	if(epsilonTransitionsFromState.size() == 0 || epsilonTransitionsFromState.begin()->second.size() == 0) {
+		out << " -";
+	} else {
+		bool sign = false;
+		for(const State& to : epsilonTransitionsFromState.begin()->second) {
+			label::LabelToStringComposer composer;
+			out << (sign ? "|" : " ") << composer.compose(to.getName());
+			sign = true;
+		}
+	}
+}
+
+std::string FiniteAutomatonToStringComposer::compose(const DFA& automaton) const {
+	std::stringstream out;
+
+
+	return out.str();
+}
+
+std::string FiniteAutomatonToStringComposer::compose(const NFA& automaton) const {
+	std::stringstream out;
+
+
+	return out.str();
+}
+
+std::string FiniteAutomatonToStringComposer::compose(const EpsilonNFA& automaton) const {
+	std::stringstream out;
+
+	out << "ENFA";
+	for(auto iterSymbol = automaton.getInputAlphabet().begin(); iterSymbol != automaton.getInputAlphabet().end(); iterSymbol++) {
+		alphabet::SymbolToStringComposer composer;
+		out << " " << composer.compose(*iterSymbol);
+	}
+
+	out << " #E";
+	out << std::endl;
+
+	for(auto iterState = automaton.getStates().begin(); iterState != automaton.getStates().end(); iterState++) {
+		if(automaton.getInitialStates().find(*iterState) != automaton.getInitialStates().end()) {
+			out << ">";
+		}
+		if(automaton.getFinalStates().find(*iterState) != automaton.getFinalStates().end()) {
+			out << "<";
+		}
+		label::LabelToStringComposer composer;
+		out << composer.compose(iterState->getName());
+
+		composeTransitionsFromState(out, automaton, *iterState);
+
+		out << std::endl;
+	}
+
+	return out.str();
+}
+
+std::string FiniteAutomatonToStringComposer::compose(const Automaton& automaton) const {
+	std::stringstream out;
+	automaton.getAutomaton().Accept((void*) &out, *this);
+	return out.str();
+}
+
+void FiniteAutomatonToStringComposer::Visit(void* data, const Automaton& automaton) const {
+	*((std::stringstream*) data) << this->compose(automaton);
+}
+
+void FiniteAutomatonToStringComposer::Visit(void*, const UnknownAutomaton&) const {
+	throw alib::AlibException();
+}
+
+void FiniteAutomatonToStringComposer::Visit(void* data, const EpsilonNFA& automaton) const {
+	*((std::stringstream*) data) << this->compose(automaton);
+}
+
+void FiniteAutomatonToStringComposer::Visit(void* data, const NFA& automaton) const {
+	*((std::stringstream*) data) << this->compose(automaton);
+}
+
+void FiniteAutomatonToStringComposer::Visit(void* data, const DFA& automaton) const {
+	*((std::stringstream*) data) << this->compose(automaton);
+}
+
+void FiniteAutomatonToStringComposer::Visit(void*, const ExtendedNFA&) const {
+	throw alib::AlibException();
+}
+
+void FiniteAutomatonToStringComposer::Visit(void*, const CompactNFA&) const {
+	throw alib::AlibException();
+}
+
+void FiniteAutomatonToStringComposer::Visit(void*, const PDA&) const {
+	throw alib::AlibException();
+}
+
+void FiniteAutomatonToStringComposer::Visit(void*, const OneTapeDTM&) const {
+	throw alib::AlibException();
+}
+
+} /* namespace automaton */
diff --git a/alib2/src/automaton/FSM/FiniteAutomatonToStringComposer.h b/alib2/src/automaton/FSM/FiniteAutomatonToStringComposer.h
new file mode 100644
index 0000000000..aa5ef2872b
--- /dev/null
+++ b/alib2/src/automaton/FSM/FiniteAutomatonToStringComposer.h
@@ -0,0 +1,42 @@
+#ifndef AUTOMATON_PRINTER_H_
+#define AUTOMATON_PRINTER_H_
+
+#include <sstream>
+#include "../Automaton.h"
+#include "DFA.h"
+#include "NFA.h"
+#include "EpsilonNFA.h"
+
+namespace automaton {
+
+class FiniteAutomatonToStringComposer : public AutomatonBase::const_visitor_type {
+	void Visit(void*, const Automaton& automaton) const;
+	void Visit(void*, const UnknownAutomaton& automaton) const;
+	void Visit(void*, const EpsilonNFA& automaton) const;
+	void Visit(void*, const NFA& automaton) const;
+	void Visit(void*, const DFA& automaton) const;
+	void Visit(void*, const ExtendedNFA& automaton) const;
+	void Visit(void*, const CompactNFA& automaton) const;
+	void Visit(void*, const PDA& automaton) const;
+	void Visit(void*, const OneTapeDTM& automaton) const;
+
+	void composeTransitionsFromState(std::stringstream& out, const EpsilonNFA& automaton, const State& from) const;
+
+public:
+	/**
+	 * Prints XML representation of UnknownAutomaton to the output stream.
+	 * @param automaton automaton to print
+	 * @return list of xml tokens representing the automaton
+	 */
+	std::string compose(const AutomatonBase& automaton) const;
+
+	std::string compose(const Automaton& automaton) const;
+	
+	std::string compose(const DFA& automaton) const;
+	std::string compose(const NFA& automaton) const;
+	std::string compose(const EpsilonNFA& automaton) const;
+};
+
+} /* namespace automaton */
+
+#endif /* AUTOMATON_PRINTER_H_ */
diff --git a/alib2/src/label/LabelFromStringParser.h b/alib2/src/label/LabelFromStringParser.h
index 4cb0787acc..8ba1cf79c1 100644
--- a/alib2/src/label/LabelFromStringParser.h
+++ b/alib2/src/label/LabelFromStringParser.h
@@ -33,6 +33,12 @@ class StringFromStringParser;
 
 } /* namespace string */
 
+namespace automaton {
+
+class FiniteAutomatonFromStringParser;
+
+} /* namespace automaton */
+
 namespace label {
 
 class LabelFromStringParser {
@@ -48,6 +54,7 @@ public:
 	friend class alphabet::SymbolFromStringParser;
 	friend class regexp::RegExpFromStringParser;
 	friend class string::StringFromStringParser;
+	friend class automaton::FiniteAutomatonFromStringParser;
 };
 
 } /* namespace label */
diff --git a/alib2/test-src/automaton/AutomatonTest.cpp b/alib2/test-src/automaton/AutomatonTest.cpp
index 301afd6563..11c938d218 100644
--- a/alib2/test-src/automaton/AutomatonTest.cpp
+++ b/alib2/test-src/automaton/AutomatonTest.cpp
@@ -17,6 +17,9 @@
 #include "label/Label.h"
 #include "alphabet/LabeledSymbol.h"
 
+#include "automaton/FSM/FiniteAutomatonFromStringParser.h"
+#include "automaton/FSM/FiniteAutomatonToStringComposer.h"
+
 #define CPPUNIT_IMPLY(x, y) CPPUNIT_ASSERT(!(x) || (y))
 
 CPPUNIT_TEST_SUITE_REGISTRATION( AutomatonTest );
@@ -85,3 +88,27 @@ void AutomatonTest::testDFAParser() {
   }
 }
 
+void AutomatonTest::FSMStringParserTest() {
+	std::string input = "ENFA a b c d #E\n"
+			    ">0 3|4 5 1|3|4 - 2\n"
+			    "1 2 - - - -\n"
+			    "2 3 - - - -\n"
+			    "3 - - 4 - -\n"
+			    "4 - 5 - - 5\n"
+			    "<5 - - - - 3\n";
+	std::stringstream inputs(input);
+
+	automaton::FiniteAutomatonFromStringParser parser(inputs);
+	automaton::Automaton automaton = parser.parseValue();
+
+	automaton::FiniteAutomatonToStringComposer composer;
+	std::string output = composer.compose(automaton);
+	std::stringstream outputs(output);
+
+	CPPUNIT_ASSERT( input == output );
+
+	automaton::FiniteAutomatonFromStringParser parser2(outputs);
+	automaton::Automaton automaton2 = parser2.parseValue();
+
+	CPPUNIT_ASSERT( automaton == automaton2 );
+}
diff --git a/alib2/test-src/automaton/AutomatonTest.h b/alib2/test-src/automaton/AutomatonTest.h
index 261c2901bb..3984de6d8c 100644
--- a/alib2/test-src/automaton/AutomatonTest.h
+++ b/alib2/test-src/automaton/AutomatonTest.h
@@ -8,6 +8,7 @@ class AutomatonTest : public CppUnit::TestFixture
   CPPUNIT_TEST_SUITE( AutomatonTest );
   CPPUNIT_TEST( testXMLParser );
   CPPUNIT_TEST( testDFAParser );
+  CPPUNIT_TEST( FSMStringParserTest );
   CPPUNIT_TEST_SUITE_END();
 
 public:
@@ -16,6 +17,7 @@ public:
 
   void testXMLParser();
   void testDFAParser();
+  void FSMStringParserTest();
 };
 
 #endif  // AUTOMATON_TEST_H_
-- 
GitLab