From d421737734e481b2667fdfe2a4f9a97604b1ecde Mon Sep 17 00:00:00 2001
From: Jan Travnicek <Jan.Travnicek@fit.cvut.cz>
Date: Tue, 26 Aug 2014 23:15:52 +0200
Subject: [PATCH] new LabelPairLabel

---
 alib2data/src/Api.cpp                         | 12 ++++
 alib2data/src/Api.hpp                         |  7 ++
 alib2data/src/label/LabelBase.h               |  2 +-
 alib2data/src/label/LabelFeatures.h           |  3 +-
 alib2data/src/label/LabelFromStringLexer.cpp  |  8 +++
 alib2data/src/label/LabelFromStringLexer.h    |  2 +
 alib2data/src/label/LabelFromStringParser.cpp | 31 ++++++++-
 alib2data/src/label/LabelFromXMLParser.cpp    | 20 +++++-
 alib2data/src/label/LabelFromXMLParser.h      |  2 +
 alib2data/src/label/LabelPairLabel.cpp        | 66 +++++++++++++++++++
 alib2data/src/label/LabelPairLabel.h          | 63 ++++++++++++++++++
 alib2data/src/label/LabelToStringComposer.cpp | 11 ++++
 alib2data/src/label/LabelToStringComposer.h   |  1 +
 alib2data/src/label/LabelToXMLComposer.cpp    |  9 +++
 alib2data/src/label/LabelToXMLComposer.h      |  1 +
 alib2data/src/label/NextLabel.cpp             |  4 ++
 alib2data/src/label/NextLabel.h               |  1 +
 alib2data/src/object/ObjectBase.h             |  5 +-
 alib2data/test-src/label/LabelTest.cpp        | 18 +++++
 19 files changed, 256 insertions(+), 10 deletions(-)
 create mode 100644 alib2data/src/label/LabelPairLabel.cpp
 create mode 100644 alib2data/src/label/LabelPairLabel.h

diff --git a/alib2data/src/Api.cpp b/alib2data/src/Api.cpp
index 82f2854a2a..001a533c6d 100644
--- a/alib2data/src/Api.cpp
+++ b/alib2data/src/Api.cpp
@@ -346,6 +346,14 @@ std::list<sax::Token> api<label::LabelSetLabel>::compose(const label::LabelSetLa
 	return ToXMLComposers::labelComposer.compose(data);
 }
 
+label::LabelPairLabel api<label::LabelPairLabel>::parse(std::list<sax::Token>& input) {
+	return FromXMLParsers::labelParser.parseLabelPairLabel(input);
+}
+
+std::list<sax::Token> api<label::LabelPairLabel>::compose(const label::LabelPairLabel& data) {
+	return ToXMLComposers::labelComposer.compose(data);
+}
+
 
 regexp::RegExp api<regexp::RegExp>::parse(std::list<sax::Token>& input) {
 	return FromXMLParsers::regexpParser.parseRegExp(input);
@@ -532,6 +540,10 @@ void ToXMLComposers::Visit(void* data, const label::LabelSetLabel& label) const
 	*((std::list<sax::Token>*) data) = std::move(api<label::LabelSetLabel>::compose(label));
 }
 
+void ToXMLComposers::Visit(void* data, const label::LabelPairLabel& label) const {
+	*((std::list<sax::Token>*) data) = std::move(api<label::LabelPairLabel>::compose(label));
+}
+
 void ToXMLComposers::Visit(void* data, const label::StringLabel& label) const {
 	*((std::list<sax::Token>*) data) = std::move(api<label::StringLabel>::compose(label));
 }
diff --git a/alib2data/src/Api.hpp b/alib2data/src/Api.hpp
index fe40c1933c..3161b494ad 100644
--- a/alib2data/src/Api.hpp
+++ b/alib2data/src/Api.hpp
@@ -276,6 +276,12 @@ struct api<label::LabelSetLabel> {
 	static std::list<sax::Token> compose(const label::LabelSetLabel& data);
 };
 
+template<>
+struct api<label::LabelPairLabel> {
+	static label::LabelPairLabel parse(std::list<sax::Token>& input);
+	static std::list<sax::Token> compose(const label::LabelPairLabel& data);
+};
+
 
 template<>
 struct api<regexp::RegExp> {
@@ -389,6 +395,7 @@ class ToXMLComposers : public VisitableObjectBase::const_visitor_type {
 	void Visit(void*, const label::IntegerLabel& label) const;
 	void Visit(void*, const label::CharacterLabel& label) const;
 	void Visit(void*, const label::LabelSetLabel& label) const;
+	void Visit(void*, const label::LabelPairLabel& label) const;
 
 	void Visit(void*, const regexp::UnboundedRegExp& regexp) const;
 	void Visit(void*, const regexp::FormalRegExp& regexp) const;
diff --git a/alib2data/src/label/LabelBase.h b/alib2data/src/label/LabelBase.h
index 17b71f1855..708a41f71d 100644
--- a/alib2data/src/label/LabelBase.h
+++ b/alib2data/src/label/LabelBase.h
@@ -14,7 +14,7 @@
 namespace label {
 
 typedef std::acceptor_base<
-			label::StringLabel, label::IntegerLabel, label::CharacterLabel, label::LabelSetLabel
+			label::StringLabel, label::IntegerLabel, label::CharacterLabel, label::LabelSetLabel, label::LabelPairLabel
 	> VisitableLabelBase;
 
 /**
diff --git a/alib2data/src/label/LabelFeatures.h b/alib2data/src/label/LabelFeatures.h
index c5d036bfb8..0e9ddc62c0 100644
--- a/alib2data/src/label/LabelFeatures.h
+++ b/alib2data/src/label/LabelFeatures.h
@@ -14,7 +14,8 @@ enum class FEATURES {
 	STRING,
 	CHAR,
 	INTEGER,
-	LABEL_SET
+	LABEL_SET,
+	LABEL_PAIR
 };
 
 } /* namespace label */
diff --git a/alib2data/src/label/LabelFromStringLexer.cpp b/alib2data/src/label/LabelFromStringLexer.cpp
index fe8126be95..e54528d05a 100644
--- a/alib2data/src/label/LabelFromStringLexer.cpp
+++ b/alib2data/src/label/LabelFromStringLexer.cpp
@@ -36,6 +36,14 @@ L0:
 		m_Current.type = TokenType::SET_END;
 		m_Current.value += character;
 		return *this;
+	} else if(character == '<') {
+		m_Current.type = TokenType::PAIR_BEGIN;
+		m_Current.value += character;
+		return *this;
+	} else if(character == '>') {
+		m_Current.type = TokenType::PAIR_END;
+		m_Current.value += character;
+		return *this;
 	} else if(character == ',') {
 		m_Current.type = TokenType::COMMA;
 		m_Current.value += character;
diff --git a/alib2data/src/label/LabelFromStringLexer.h b/alib2data/src/label/LabelFromStringLexer.h
index 484128f9e9..56e7947181 100644
--- a/alib2data/src/label/LabelFromStringLexer.h
+++ b/alib2data/src/label/LabelFromStringLexer.h
@@ -21,6 +21,8 @@ public:
 		INTEGER,
 		SET_BEGIN,
 		SET_END,
+		PAIR_BEGIN,
+		PAIR_END,
 		COMMA,
 		TEOF,
 		ERROR
diff --git a/alib2data/src/label/LabelFromStringParser.cpp b/alib2data/src/label/LabelFromStringParser.cpp
index 1ad496298b..165749dbb1 100644
--- a/alib2data/src/label/LabelFromStringParser.cpp
+++ b/alib2data/src/label/LabelFromStringParser.cpp
@@ -11,6 +11,7 @@
 #include "IntegerLabel.h"
 #include "CharacterLabel.h"
 #include "LabelSetLabel.h"
+#include "LabelPairLabel.h"
 #include <algorithm>
 
 namespace label {
@@ -20,7 +21,7 @@ LabelFromStringParser::LabelFromStringParser(std::stringstream& input) : m_Lexer
 }
 
 Label LabelFromStringParser::parse() {
-	return parse(std::set<FEATURES>({FEATURES::STRING, FEATURES::CHAR, FEATURES::INTEGER, FEATURES::LABEL_SET}));
+	return parse(std::set<FEATURES>({FEATURES::STRING, FEATURES::CHAR, FEATURES::INTEGER, FEATURES::LABEL_SET, FEATURES::LABEL_PAIR}));
 }
 
 Label LabelFromStringParser::parse(const std::set<FEATURES>& features) {
@@ -60,7 +61,31 @@ Label LabelFromStringParser::parse(const std::set<FEATURES>& features) {
 			throw exception::AlibException("Expected SET_END token.");
 		return Label(label::LabelSetLabel(labelSet));
 	}
+	case LabelFromStringLexer::TokenType::PAIR_BEGIN: {
+		if(!features.count(FEATURES::LABEL_PAIR)) throw exception::AlibException();
+		next();
+		token = m_Lexer.token();
+
+		Label firstLabel = this->parse();
+		next();
+		token = m_Lexer.token();
+
+		if(token.type == LabelFromStringLexer::TokenType::COMMA) {
+			next();
+			token = m_Lexer.token();
+		} else
+			throw exception::AlibException("Excepted COMMA token.");
+
+		Label secondLabel = this->parse();
+		next();
+		token = m_Lexer.token();
+
+		if(token.type != LabelFromStringLexer::TokenType::PAIR_END)
+			throw exception::AlibException("Expected PAIR_END token.");
+		return Label(label::LabelPairLabel(std::make_pair(firstLabel, secondLabel)));
+	}
 	case LabelFromStringLexer::TokenType::SET_END:
+	case LabelFromStringLexer::TokenType::PAIR_END:
 	case LabelFromStringLexer::TokenType::COMMA:
 		throw exception::AlibException("Unexpected start of Label.");
 	case LabelFromStringLexer::TokenType::ERROR:
@@ -71,7 +96,7 @@ Label LabelFromStringParser::parse(const std::set<FEATURES>& features) {
 
 bool LabelFromStringParser::next() {
 	LabelFromStringLexer::Token token = m_Lexer.next().token();
-	if(token.type == LabelFromStringLexer::TokenType::STRING || token.type == LabelFromStringLexer::TokenType::CHAR || token.type == LabelFromStringLexer::TokenType::INTEGER || token.type == LabelFromStringLexer::TokenType::SET_BEGIN || token.type == LabelFromStringLexer::TokenType::SET_END) {
+	if(token.type == LabelFromStringLexer::TokenType::STRING || token.type == LabelFromStringLexer::TokenType::CHAR || token.type == LabelFromStringLexer::TokenType::INTEGER || token.type == LabelFromStringLexer::TokenType::SET_BEGIN || token.type == LabelFromStringLexer::TokenType::SET_END || token.type == LabelFromStringLexer::TokenType::PAIR_BEGIN || token.type == LabelFromStringLexer::TokenType::PAIR_END || token.type == LabelFromStringLexer::TokenType::COMMA) {
 		return true;
 	} else {
 		return false;
@@ -80,7 +105,7 @@ bool LabelFromStringParser::next() {
 
 bool LabelFromStringParser::first() {
 	LabelFromStringLexer::Token token = m_Lexer.next().token();
-	if(token.type == LabelFromStringLexer::TokenType::STRING || token.type == LabelFromStringLexer::TokenType::CHAR || token.type == LabelFromStringLexer::TokenType::INTEGER || token.type == LabelFromStringLexer::TokenType::SET_BEGIN) {
+	if(token.type == LabelFromStringLexer::TokenType::STRING || token.type == LabelFromStringLexer::TokenType::CHAR || token.type == LabelFromStringLexer::TokenType::INTEGER || token.type == LabelFromStringLexer::TokenType::SET_BEGIN || token.type == LabelFromStringLexer::TokenType::PAIR_BEGIN) {
 		return true;
 	} else {
 		return false;
diff --git a/alib2data/src/label/LabelFromXMLParser.cpp b/alib2data/src/label/LabelFromXMLParser.cpp
index 1aa0dabe80..e17fd7453a 100644
--- a/alib2data/src/label/LabelFromXMLParser.cpp
+++ b/alib2data/src/label/LabelFromXMLParser.cpp
@@ -12,7 +12,7 @@
 namespace label {
 
 Label LabelFromXMLParser::parseLabel(std::list<sax::Token>& input) const {
-	return parseLabel(input, std::set<FEATURES>({FEATURES::STRING, FEATURES::CHAR, FEATURES::INTEGER, FEATURES::LABEL_SET}));
+	return parseLabel(input, std::set<FEATURES>({FEATURES::STRING, FEATURES::CHAR, FEATURES::INTEGER, FEATURES::LABEL_SET, FEATURES::LABEL_PAIR}));
 }
 
 Label LabelFromXMLParser::parseLabel(std::list<sax::Token>& input, const std::set<FEATURES>& features) const {
@@ -28,13 +28,16 @@ Label LabelFromXMLParser::parseLabel(std::list<sax::Token>& input, const std::se
 	} else if(isToken(input, sax::Token::TokenType::START_ELEMENT, "LabelSetLabel")) {
 		if(!features.count(FEATURES::LABEL_SET)) throw exception::AlibException();
 		return Label(parseLabelSetLabel(input));
+	} else if(isToken(input, sax::Token::TokenType::START_ELEMENT, "LabelPairLabel")) {
+		if(!features.count(FEATURES::LABEL_PAIR)) throw exception::AlibException();
+		return Label(parseLabelPairLabel(input));
 	} else {
-		throw sax::ParserException(sax::Token("IntegerLabel, StringLabel, CharacterLabel", sax::Token::TokenType::START_ELEMENT), input.front());
+		throw sax::ParserException(sax::Token("IntegerLabel, StringLabel, CharacterLabel, LabelSetLabel, LabelPairLabel", sax::Token::TokenType::START_ELEMENT), input.front());
 	}
 }
 
 bool LabelFromXMLParser::first(std::list<sax::Token>& input) const {
-	if(isToken(input, sax::Token::TokenType::START_ELEMENT, "IntegerLabel") || isToken(input, sax::Token::TokenType::START_ELEMENT, "StringLabel") || isToken(input, sax::Token::TokenType::START_ELEMENT, "CharacterLabel") || isToken(input, sax::Token::TokenType::START_ELEMENT, "LabelSetLabel")) {
+	if(isToken(input, sax::Token::TokenType::START_ELEMENT, "IntegerLabel") || isToken(input, sax::Token::TokenType::START_ELEMENT, "StringLabel") || isToken(input, sax::Token::TokenType::START_ELEMENT, "CharacterLabel") || isToken(input, sax::Token::TokenType::START_ELEMENT, "LabelSetLabel") || isToken(input, sax::Token::TokenType::START_ELEMENT, "LabelPairLabel")) {
 		return true;
 	} else {
 		return false;
@@ -73,4 +76,15 @@ LabelSetLabel LabelFromXMLParser::parseLabelSetLabel(std::list<sax::Token>& inpu
 	return data;
 }
 
+LabelPairLabel LabelFromXMLParser::parseLabelPairLabel(std::list<sax::Token>& input) const {
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "LabelPairLabel");
+	
+	Label firstLabel = parseLabel(input);
+	Label secondLabel = parseLabel(input);
+
+	LabelPairLabel data(std::make_pair(firstLabel, secondLabel));
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "LabelPairLabel");
+	return data;
+}
+
 } /* namespace label */
diff --git a/alib2data/src/label/LabelFromXMLParser.h b/alib2data/src/label/LabelFromXMLParser.h
index 5c8338a900..41d065aae1 100644
--- a/alib2data/src/label/LabelFromXMLParser.h
+++ b/alib2data/src/label/LabelFromXMLParser.h
@@ -17,6 +17,7 @@
 #include "IntegerLabel.h"
 #include "CharacterLabel.h"
 #include "LabelSetLabel.h"
+#include "LabelPairLabel.h"
 #include "../sax/Token.h"
 #include "../label/Label.h"
 
@@ -40,6 +41,7 @@ class LabelFromXMLParser : public sax::FromXMLParserHelper {
 	CharacterLabel parseCharacterLabel(std::list<sax::Token>& input) const;
 	StringLabel parseStringLabel(std::list<sax::Token>& input) const;
 	LabelSetLabel parseLabelSetLabel(std::list<sax::Token>& input) const;
+	LabelPairLabel parseLabelPairLabel(std::list<sax::Token>& input) const;
 
 	template<typename T> friend class alib::api;
 public:
diff --git a/alib2data/src/label/LabelPairLabel.cpp b/alib2data/src/label/LabelPairLabel.cpp
new file mode 100644
index 0000000000..1685ae410a
--- /dev/null
+++ b/alib2data/src/label/LabelPairLabel.cpp
@@ -0,0 +1,66 @@
+/*
+ * LabelPairLabel.cpp
+ *
+ *  Created on: Mar 26, 2013
+ *      Author: Jan Travicek
+ */
+
+#include "LabelPairLabel.h"
+#include "../std/pair.hpp"
+#include <sstream>
+
+namespace label {
+
+LabelPairLabel::LabelPairLabel(std::pair<Label, Label> label) : label(label) {
+
+}
+
+LabelBase* LabelPairLabel::clone() const {
+	return new LabelPairLabel(*this);
+}
+
+LabelBase* LabelPairLabel::plunder() && {
+	return new LabelPairLabel(std::move(*this));
+}
+
+std::pair<Label, Label> LabelPairLabel::getData() const {
+	return label;
+}
+
+bool LabelPairLabel::operator<(const alib::ObjectBase& other) const {
+	return other > *this;
+}
+
+bool LabelPairLabel::operator==(const alib::ObjectBase& other) const {
+	return other == *this;
+}
+
+bool LabelPairLabel::operator >(const alib::ObjectBase& other) const {
+	return other < *this;
+}
+
+
+bool LabelPairLabel::operator <(const LabelPairLabel& other) const {
+	return label < other.label;
+}
+
+bool LabelPairLabel::operator ==(const LabelPairLabel& other) const {
+	return label == other.label;
+}
+
+void LabelPairLabel::operator>>(std::ostream& out) const {
+	out << "(LabelPairLabel " << label << ")";
+}
+
+LabelPairLabel::operator std::string() const {
+	std::stringstream ss;
+	ss << "<";
+	ss << (std::string) label.first;
+	ss << ",";
+	ss << (std::string) label.second;
+	ss << "}";
+	return std::move(ss).str();
+}
+
+} /* namespace label */
+
diff --git a/alib2data/src/label/LabelPairLabel.h b/alib2data/src/label/LabelPairLabel.h
new file mode 100644
index 0000000000..b1032020cd
--- /dev/null
+++ b/alib2data/src/label/LabelPairLabel.h
@@ -0,0 +1,63 @@
+/*
+ * LabelPairLabel.h
+ *
+ *  Created on: Mar 26, 2013
+ *      Author: Jan Travnicek
+ */
+
+#ifndef LABEL_PAIR_LABEL_H_
+#define LABEL_PAIR_LABEL_H_
+
+#include <string>
+#include <ostream>
+
+#include "Label.h"
+#include "LabelBase.h"
+#include <set>
+
+namespace label {
+
+/**
+ * Represents symbol in an alphabet.
+ */
+class LabelPairLabel : public std::acceptor<LabelPairLabel, VisitableLabelBase, std::acceptor<LabelPairLabel, alib::VisitableObjectBase, LabelBase> > {
+protected:
+	std::pair<Label, Label> label;
+public:
+	virtual LabelBase* clone() const;
+
+	virtual LabelBase* plunder() &&;
+
+	/**
+	 * Creates new symbol with given name.
+	 * @param symbol name of the symbol
+	 */
+	explicit LabelPairLabel(std::pair<Label, Label> label);
+
+	virtual bool operator<(const alib::ObjectBase& other) const;
+
+	virtual bool operator>(const alib::ObjectBase& other) const;
+
+	virtual bool operator==(const alib::ObjectBase& other) const;
+
+	/**
+	 * @return name of the symbol
+	 */
+	std::pair<Label, Label> getData() const;
+
+	virtual bool operator <(const LabelPairLabel& other) const;
+	virtual bool operator ==(const LabelPairLabel& other) const;
+
+	virtual void operator>>(std::ostream&) const;
+
+	virtual operator std::string () const;
+
+	virtual int selfTypeId() const {
+		return typeId(*this);
+	}
+};
+
+} /* namespace label */
+
+#endif /* LABEL_PAIR_LABEL_H_ */
+
diff --git a/alib2data/src/label/LabelToStringComposer.cpp b/alib2data/src/label/LabelToStringComposer.cpp
index 3dba0cea0d..9023693bfa 100644
--- a/alib2data/src/label/LabelToStringComposer.cpp
+++ b/alib2data/src/label/LabelToStringComposer.cpp
@@ -11,6 +11,7 @@
 #include "StringLabel.h"
 #include "CharacterLabel.h"
 #include "LabelSetLabel.h"
+#include "LabelPairLabel.h"
 
 namespace label {
 
@@ -57,6 +58,16 @@ void LabelToStringComposer::Visit(void* userData, const LabelSetLabel& label) {
 	out << ']';
 }
 
+void LabelToStringComposer::Visit(void* userData, const LabelPairLabel& label) {
+	std::stringstream &out = *((std::stringstream*) userData);
+
+	out << '<';
+	label.getData().first.getData().Accept(userData, *this);
+	out << ", ";
+	label.getData().second.getData().Accept(userData, *this);
+	out << '>';
+}
+
 std::string LabelToStringComposer::compose(const Label& label) {
 	std::stringstream out;
 	label.getData().Accept((void*) &out, *this);
diff --git a/alib2data/src/label/LabelToStringComposer.h b/alib2data/src/label/LabelToStringComposer.h
index 6f9629363e..2bccf49464 100644
--- a/alib2data/src/label/LabelToStringComposer.h
+++ b/alib2data/src/label/LabelToStringComposer.h
@@ -23,6 +23,7 @@ class LabelToStringComposer : public VisitableLabelBase::visitor_type {
 	void Visit(void*, const StringLabel& label);
 	void Visit(void*, const CharacterLabel& label);
 	void Visit(void*, const LabelSetLabel& label);
+	void Visit(void*, const LabelPairLabel& label);
 public:
 	/**
 	 * Prints text representation of Label to the output stream.
diff --git a/alib2data/src/label/LabelToXMLComposer.cpp b/alib2data/src/label/LabelToXMLComposer.cpp
index 90f5109623..ba7131b9b4 100644
--- a/alib2data/src/label/LabelToXMLComposer.cpp
+++ b/alib2data/src/label/LabelToXMLComposer.cpp
@@ -61,4 +61,13 @@ std::list<sax::Token> LabelToXMLComposer::compose(const LabelSetLabel& label) co
 	return out;
 }
 
+std::list<sax::Token> LabelToXMLComposer::compose(const LabelPairLabel& label) const {
+	std::list<sax::Token> out;
+	out.push_back(sax::Token("LabelPairLabel", sax::Token::TokenType::START_ELEMENT));
+	out.splice(out.end(), alib::api<label::Label>::compose(label.getData().first));
+	out.splice(out.end(), alib::api<label::Label>::compose(label.getData().second));
+	out.push_back(sax::Token("LabelPairLabel", sax::Token::TokenType::END_ELEMENT));
+	return out;
+}
+
 } /* namespace label */
diff --git a/alib2data/src/label/LabelToXMLComposer.h b/alib2data/src/label/LabelToXMLComposer.h
index f85d7652fd..7259b30c4e 100644
--- a/alib2data/src/label/LabelToXMLComposer.h
+++ b/alib2data/src/label/LabelToXMLComposer.h
@@ -43,6 +43,7 @@ class LabelToXMLComposer {
 	std::list<sax::Token> compose(const IntegerLabel& label) const;
 	std::list<sax::Token> compose(const CharacterLabel& label) const;
 	std::list<sax::Token> compose(const LabelSetLabel& label) const;
+	std::list<sax::Token> compose(const LabelPairLabel& label) const;
 
 	template<typename T> friend class alib::api;
 };
diff --git a/alib2data/src/label/NextLabel.cpp b/alib2data/src/label/NextLabel.cpp
index 3cc0d3bf6d..377ff424f3 100644
--- a/alib2data/src/label/NextLabel.cpp
+++ b/alib2data/src/label/NextLabel.cpp
@@ -36,6 +36,10 @@ void NextLabel::Visit(void*, const LabelSetLabel&) {
 	throw exception::AlibException("LabelSet has no next Label.");
 }
 
+void NextLabel::Visit(void*, const LabelPairLabel&) {
+	throw exception::AlibException("LabelPair has no next Label.");
+}
+
 Label NextLabel::nextLabel(const Label& label) {
 	Label* out;
 	label.getData().Accept((void*) &out, *this);
diff --git a/alib2data/src/label/NextLabel.h b/alib2data/src/label/NextLabel.h
index 130bd621c6..67a4fdc610 100644
--- a/alib2data/src/label/NextLabel.h
+++ b/alib2data/src/label/NextLabel.h
@@ -22,6 +22,7 @@ class NextLabel : public VisitableLabelBase::visitor_type {
 	void Visit(void*, const IntegerLabel& label);
 	void Visit(void*, const CharacterLabel& label);
 	void Visit(void*, const LabelSetLabel& label);
+	void Visit(void*, const LabelPairLabel& label);
 
 public:
 	/**
diff --git a/alib2data/src/object/ObjectBase.h b/alib2data/src/object/ObjectBase.h
index fd06230539..140edebb7a 100644
--- a/alib2data/src/object/ObjectBase.h
+++ b/alib2data/src/object/ObjectBase.h
@@ -56,6 +56,7 @@ class StringLabel;
 class IntegerLabel;
 class CharacterLabel;
 class LabelSetLabel;
+class LabelPairLabel;
 
 }
 
@@ -95,7 +96,7 @@ typedef std::acceptor_base<
 			exception::AlibException,
 			automaton::UnknownAutomaton, automaton::DFA, automaton::NFA, automaton::EpsilonNFA, automaton::CompactNFA, automaton::ExtendedNFA, automaton::NPDA, automaton::SinglePopNPDA, automaton::OneTapeDTM,
 			grammar::UnknownGrammar, grammar::LeftLG, grammar::LeftRG, grammar::RightLG, grammar::RightRG, grammar::LG, grammar::CFG, grammar::EpsilonFreeCFG, grammar::CNF, grammar::GNF, grammar::CSG, grammar::NonContractingGrammar, grammar::ContextPreservingUnrestrictedGrammar, grammar::UnrestrictedGrammar,
-			label::StringLabel, label::IntegerLabel, label::CharacterLabel, label::LabelSetLabel,
+			label::StringLabel, label::IntegerLabel, label::CharacterLabel, label::LabelSetLabel, label::LabelPairLabel,
 			regexp::UnboundedRegExp, regexp::FormalRegExp,
 			string::Epsilon, string::LinearString, string::CyclicString,
 			alphabet::LabeledSymbol, alphabet::BlankSymbol, alphabet::BottomOfTheStackSymbol, alphabet::EndSymbol,
@@ -108,7 +109,7 @@ class ObjectBase :
 			exception::AlibException,
 			automaton::UnknownAutomaton, automaton::DFA, automaton::NFA, automaton::EpsilonNFA, automaton::CompactNFA, automaton::ExtendedNFA, automaton::NPDA, automaton::SinglePopNPDA, automaton::OneTapeDTM,
 			grammar::UnknownGrammar, grammar::LeftLG, grammar::LeftRG, grammar::RightLG, grammar::RightRG, grammar::LG, grammar::CFG, grammar::EpsilonFreeCFG, grammar::CNF, grammar::GNF, grammar::CSG, grammar::NonContractingGrammar, grammar::ContextPreservingUnrestrictedGrammar, grammar::UnrestrictedGrammar,
-			label::StringLabel, label::IntegerLabel, label::CharacterLabel, label::LabelSetLabel,
+			label::StringLabel, label::IntegerLabel, label::CharacterLabel, label::LabelSetLabel, label::LabelPairLabel,
 			regexp::UnboundedRegExp, regexp::FormalRegExp,
 			string::Epsilon, string::LinearString, string::CyclicString,
 			alphabet::LabeledSymbol, alphabet::BlankSymbol, alphabet::BottomOfTheStackSymbol, alphabet::EndSymbol,
diff --git a/alib2data/test-src/label/LabelTest.cpp b/alib2data/test-src/label/LabelTest.cpp
index d6bac92868..f675c23be7 100644
--- a/alib2data/test-src/label/LabelTest.cpp
+++ b/alib2data/test-src/label/LabelTest.cpp
@@ -90,6 +90,24 @@ void LabelTest::testEqual() {
     label::LabelFromStringParser parser2(outputs);
     label::Label label2 = parser2.parseValue();
 
+    CPPUNIT_ASSERT( label == label2 );
+  }
+  {
+    std::string input = "<1, 2>";
+    std::stringstream inputs(input);
+
+    label::LabelFromStringParser parser(inputs);
+    label::Label label = parser.parseValue();
+
+    label::LabelToStringComposer composer;
+    std::string output = composer.compose(label);
+    std::stringstream outputs(output);
+
+    CPPUNIT_ASSERT( input == output );
+
+    label::LabelFromStringParser parser2(outputs);
+    label::Label label2 = parser2.parseValue();
+
     CPPUNIT_ASSERT( label == label2 );
   }
 }
-- 
GitLab