From 6abe0a878351fbb82b20998c46eb63ce76a51cf0 Mon Sep 17 00:00:00 2001
From: Tomas Pecka <tomas.pecka@fit.cvut.cz>
Date: Sun, 27 Oct 2019 12:52:47 +0100
Subject: [PATCH] refactor aql executable, redesign -f, c, and -i flags

---
 alib2cli/src/command/Command.h                |   3 +-
 alib2cli/src/command/EOTCommand.h             |  23 ++
 alib2cli/src/lexer/CharSequence.h             |  92 +++++
 alib2cli/src/lexer/CharSequenceBase.h         |  68 ----
 alib2cli/src/lexer/Lexer.cpp                  | 356 +++++++++---------
 alib2cli/src/lexer/Lexer.h                    |  24 +-
 alib2cli/src/lexer/StringCharSequence.h       |  41 --
 alib2cli/src/parser/Parser.cpp                |  13 +-
 alib2cli/src/readline/IstreamLineReader.h     |  37 ++
 alib2cli/src/readline/LineReader.h            |  43 +++
 alib2cli/src/readline/StringLineReader.h      |  35 ++
 alib2cli/test-src/cli/CliTest.cpp             |   4 +-
 .../test-src/testing/TimeoutAqlTest.cpp       |   8 +-
 aql2/src/aql.cpp                              | 105 ++++--
 aql2/src/prompt/Prompt.cpp                    |  43 ++-
 aql2/src/prompt/Prompt.h                      |  31 +-
 aql2/src/prompt/ReadlineCharSequence.h        |  76 ----
 aql2/src/prompt/ReadlineLineReader.h          |  39 ++
 18 files changed, 599 insertions(+), 442 deletions(-)
 create mode 100644 alib2cli/src/command/EOTCommand.h
 create mode 100644 alib2cli/src/lexer/CharSequence.h
 delete mode 100644 alib2cli/src/lexer/CharSequenceBase.h
 delete mode 100644 alib2cli/src/lexer/StringCharSequence.h
 create mode 100644 alib2cli/src/readline/IstreamLineReader.h
 create mode 100644 alib2cli/src/readline/LineReader.h
 create mode 100644 alib2cli/src/readline/StringLineReader.h
 delete mode 100644 aql2/src/prompt/ReadlineCharSequence.h
 create mode 100644 aql2/src/prompt/ReadlineLineReader.h

diff --git a/alib2cli/src/command/Command.h b/alib2cli/src/command/Command.h
index 5f248d4e9a..d7daf36e57 100644
--- a/alib2cli/src/command/Command.h
+++ b/alib2cli/src/command/Command.h
@@ -11,7 +11,8 @@ public:
 		OK,
 		QUIT,
 		EXCEPTION,
-		ERROR
+		ERROR,
+		EOT
 	};
 
 	virtual ~Command ( ) noexcept = default;
diff --git a/alib2cli/src/command/EOTCommand.h b/alib2cli/src/command/EOTCommand.h
new file mode 100644
index 0000000000..16bf1952f7
--- /dev/null
+++ b/alib2cli/src/command/EOTCommand.h
@@ -0,0 +1,23 @@
+#ifndef _CLI_EOT_COMMAND_H_
+#define _CLI_EOT_COMMAND_H_
+
+#include <command/Command.h>
+#include <environment/Environment.h>
+#include <ast/Statement.h>
+
+namespace cli {
+
+class EOTCommand : public Command {
+	std::shared_ptr < Statement > m_command;
+
+public:
+	EOTCommand ( ) { }
+
+	Command::Result run ( Environment & /* environment */ ) const override {
+		return Command::Result::EOT;
+	}
+};
+
+} /* namespace cli */
+
+#endif /* _CLI_EOT_COMMAND_H_ */
diff --git a/alib2cli/src/lexer/CharSequence.h b/alib2cli/src/lexer/CharSequence.h
new file mode 100644
index 0000000000..c7d9a391e9
--- /dev/null
+++ b/alib2cli/src/lexer/CharSequence.h
@@ -0,0 +1,92 @@
+#ifndef __CHAR_SEQUENCE_H_
+#define __CHAR_SEQUENCE_H_
+
+#include <string>
+#include <vector>
+
+#include <readline/LineReader.h>
+
+namespace cli {
+
+class CharSequence {
+	std::unique_ptr < LineReader > m_lineReader;
+	std::string putbackBuffer;
+
+	const char * linePtr = nullptr;
+	std::vector < std::string > m_lines;
+
+	void fetch ( bool readNextLine ) {
+		if ( ! readNextLine ) {
+			return;
+		}
+
+		std::string line;
+		for ( std::string tmpLine; m_lineReader->readline ( tmpLine ); ) {
+			if ( ! tmpLine.empty ( ) && tmpLine.back ( ) == '\\' ) {
+				tmpLine.back ( ) = '\n';
+				line += tmpLine;
+			} else {
+				line += tmpLine;
+				line += '\n';
+				break;
+			}
+		}
+
+		if ( line.empty ( ) )
+			return;
+
+		m_lines.push_back ( std::move ( line ) );
+
+		this->linePtr = m_lines.back ( ).c_str ( );
+	}
+
+public:
+	template < class Reader >
+	CharSequence ( Reader && reader ) : m_lineReader ( new Reader ( std::move ( reader ) ) ) {
+	}
+
+	int getCharacter ( ) const {
+		if ( ! putbackBuffer.empty ( ) )
+			return ( int ) putbackBuffer.back ( );
+		else if ( linePtr == nullptr )
+			return EOF;
+		else
+			return ( int ) * linePtr;
+	}
+
+	void advance ( bool readNextLine ) {
+		if ( ! putbackBuffer.empty ( ) ) {
+			putbackBuffer.pop_back ( );
+		} else if ( linePtr == nullptr ) {
+				fetch ( readNextLine );
+		} else {
+			if ( * linePtr != '\0' )
+				++ linePtr;
+			if ( * linePtr == '\0' )
+				fetch ( readNextLine );
+		}
+	}
+
+	std::string getLine ( ) const {
+		std::string res;
+		for ( const std::string & line : m_lines ) {
+			res += line;
+		}
+		return res;
+	}
+
+	void putback ( std::string string ) {
+		putbackBuffer.insert ( putbackBuffer.end ( ), string.rbegin ( ), string.rend ( ) );
+	}
+
+	void reset ( ) {
+		putbackBuffer.clear ( );
+		linePtr = nullptr;
+		m_lines.clear ( );
+		m_lineReader->reset ( );
+	}
+};
+
+} /* namespace cli */
+
+#endif /* __CHAR_SEQUENCE_H_ */
diff --git a/alib2cli/src/lexer/CharSequenceBase.h b/alib2cli/src/lexer/CharSequenceBase.h
deleted file mode 100644
index 8ec74b42bf..0000000000
--- a/alib2cli/src/lexer/CharSequenceBase.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifndef __CHAR_SEQUENCE_ADAPTOR_H_
-#define __CHAR_SEQUENCE_ADAPTOR_H_
-
-#include <string>
-
-namespace cli {
-
-class CharSequenceBase {
-	std::string putbackBuffer;
-
-protected:
-	const char * linePtr;
-	bool endOfTransmition = false;
-	bool endOfSequence = false;
-
-	virtual void fetch ( bool readNextLine ) = 0;
-
-public:
-	CharSequenceBase ( ) = default;
-
-	CharSequenceBase ( CharSequenceBase && ) noexcept = default;
-
-	CharSequenceBase ( const CharSequenceBase & ) = delete;
-
-	CharSequenceBase & operator = ( CharSequenceBase && ) = delete;
-
-	CharSequenceBase & operator = ( const CharSequenceBase & ) = delete;
-
-	virtual ~CharSequenceBase ( ) noexcept = default;
-
-	int getCharacter ( ) const {
-		if ( ! putbackBuffer.empty ( ) )
-			return ( int ) putbackBuffer.back ( );
-		else if ( * linePtr != 0 )
-			return ( int ) * linePtr;
-		else
-			return EOF;
-	}
-
-	void advance ( bool readNextLine ) {
-		if ( ! putbackBuffer.empty ( ) ) {
-			putbackBuffer.pop_back ( );
-		} else {
-			if ( * linePtr != 0 )
-				++ linePtr;
-			if ( * linePtr == '\0' )
-				fetch ( readNextLine );
-		}
-	}
-
-	virtual std::string getLine ( ) const = 0;
-
-	bool isEndOfTransmition ( ) const {
-		return endOfTransmition;
-	}
-
-	bool isEndOfSequence ( ) const {
-		return endOfSequence;
-	}
-
-	void putback ( std::string string ) {
-		putbackBuffer.insert ( putbackBuffer.end ( ), string.rbegin ( ), string.rend ( ) );
-	}
-};
-
-} /* namespace cli */
-
-#endif /* __CHAR_SEQUENCE_ADAPTOR_H_ */
diff --git a/alib2cli/src/lexer/Lexer.cpp b/alib2cli/src/lexer/Lexer.cpp
index c1f0b02176..f6504a6b0a 100644
--- a/alib2cli/src/lexer/Lexer.cpp
+++ b/alib2cli/src/lexer/Lexer.cpp
@@ -16,195 +16,195 @@ Lexer::Token Lexer::nextToken ( bool readNextLine ) {
 		goto qType;
 	}
 
-q0:	if ( m_source->isEndOfTransmition ( ) ) {
+q0:	if ( m_source.getCharacter ( ) == EOF ) {
 		res.m_type = TokenType::EOT;
 		return res;
 	}
-	if ( m_source->isEndOfSequence ( ) ) {
+	if ( m_source.getCharacter ( ) == '\0' ) {
 		res.m_type = TokenType::EOS;
 		return res;
 	}
-	if ( isspace ( m_source->getCharacter ( ) ) ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( isspace ( m_source.getCharacter ( ) ) ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto q0;
 	}
-	if ( m_source->getCharacter ( ) == '<' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '<' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::IN_REDIRECT;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '>' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '>' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::OUT_REDIRECT;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '(' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '(' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::LEFT_PAREN;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == ')' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == ')' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::RIGHT_PAREN;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '{' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '{' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::LEFT_BRACE;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '}' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '}' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::RIGHT_BRACE;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '[' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '[' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::LEFT_BRACKET;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == ']' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == ']' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::RIGHT_BRACKET;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '@' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '@' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::AT_SIGN;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '$' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '$' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::DOLAR_SIGN;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '&' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '&' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::AMPERSAND_SIGN;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '|' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '|' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::PIPE_SIGN;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '^' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '^' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::CARET_SIGN;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == ':' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == ':' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::COLON_SIGN;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == ';' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == ';' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::SEMICOLON_SIGN;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '=' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '=' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::EQUAL_SIGN;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '#' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '#' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		res.m_type = TokenType::HASH_SIGN;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '-' ) {
-		res.m_raw += m_source->getCharacter ( );
-		res.m_value += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '-' ) {
+		res.m_raw += m_source.getCharacter ( );
+		res.m_value += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto q2;
 	}
 
-	if ( m_source->getCharacter ( ) == '"' ) {
-		res.m_raw += m_source->getCharacter ( );
+	if ( m_source.getCharacter ( ) == '"' ) {
+		res.m_raw += m_source.getCharacter ( );
 		res.m_type = TokenType::STRING;
-		m_source->advance ( true );
+		m_source.advance ( true );
 		goto q4;
 	}
 
-	if ( ( m_source->getCharacter ( ) >= '0' && m_source->getCharacter ( ) <= '9' ) ) {
-		res.m_raw += m_source->getCharacter ( );
-		res.m_value += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( ( m_source.getCharacter ( ) >= '0' && m_source.getCharacter ( ) <= '9' ) ) {
+		res.m_raw += m_source.getCharacter ( );
+		res.m_value += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto q1;
 	}
 
-	if ( ( m_source->getCharacter ( ) >= 'a' && m_source->getCharacter ( ) <= 'z' )
-	  || ( m_source->getCharacter ( ) >= 'A' && m_source->getCharacter ( ) <= 'Z' ) ) {
-		res.m_raw += m_source->getCharacter ( );
-		res.m_value += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( ( m_source.getCharacter ( ) >= 'a' && m_source.getCharacter ( ) <= 'z' )
+	  || ( m_source.getCharacter ( ) >= 'A' && m_source.getCharacter ( ) <= 'Z' ) ) {
+		res.m_raw += m_source.getCharacter ( );
+		res.m_value += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto q3;
 	}
 
-	if ( m_source->getCharacter ( ) == '\\' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( true );
+	if ( m_source.getCharacter ( ) == '\\' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( true );
 		goto q3Escape;
 	}
 
 	res.m_type = TokenType::ERROR;
 	return res;
 
-q1:	if ( m_source->isEndOfSequence ( ) ) {
+q1:	if ( m_source.getCharacter ( ) == '\0' ) {
 		res.m_type = TokenType::INTEGER;
 		return res;
 	}
-	if ( ( m_source->getCharacter ( ) >= '0' && m_source->getCharacter ( ) <= '9' ) ) {
-		res.m_raw += m_source->getCharacter ( );
-		res.m_value += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( ( m_source.getCharacter ( ) >= '0' && m_source.getCharacter ( ) <= '9' ) ) {
+		res.m_raw += m_source.getCharacter ( );
+		res.m_value += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto q1;
 	} else {
 		res.m_type = TokenType::INTEGER;
 		return res;
 	}
 
-q2:	if ( m_source->isEndOfSequence ( ) ) {
+q2:	if ( m_source.getCharacter ( ) == '\0' ) {
 		res.m_value = "";
 		res.m_type = TokenType::DASH_SIGN;
 		return res;
 	}
-	if ( ( m_source->getCharacter ( ) >= '0' && m_source->getCharacter ( ) <= '9' ) ) {
-		res.m_raw += m_source->getCharacter ( );
-		res.m_value += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( ( m_source.getCharacter ( ) >= '0' && m_source.getCharacter ( ) <= '9' ) ) {
+		res.m_raw += m_source.getCharacter ( );
+		res.m_value += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto q1;
 	}
 
-	if ( ( m_source->getCharacter ( ) >= 'a' && m_source->getCharacter ( ) <= 'z' )
-	  || ( m_source->getCharacter ( ) >= 'A' && m_source->getCharacter ( ) <= 'Z' )
-	  || ( m_source->getCharacter ( ) == ':' ) ) {
-		res.m_raw += m_source->getCharacter ( );
-		res.m_value += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( ( m_source.getCharacter ( ) >= 'a' && m_source.getCharacter ( ) <= 'z' )
+	  || ( m_source.getCharacter ( ) >= 'A' && m_source.getCharacter ( ) <= 'Z' )
+	  || ( m_source.getCharacter ( ) == ':' ) ) {
+		res.m_raw += m_source.getCharacter ( );
+		res.m_value += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto q3;
-	} else if ( m_source->getCharacter ( ) == '\\' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( true );
+	} else if ( m_source.getCharacter ( ) == '\\' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( true );
 		goto q3Escape;
 	}
 
@@ -212,21 +212,21 @@ q2:	if ( m_source->isEndOfSequence ( ) ) {
 	res.m_type = TokenType::DASH_SIGN;
 	return res;
 
-q3:	if ( m_source->isEndOfSequence ( ) ) {
+q3:	if ( m_source.getCharacter ( ) == '\0' ) {
 		res.m_type = is_kw ( res.m_value );
 		return res;
 	}
-	if ( ( m_source->getCharacter ( ) >= '0' && m_source->getCharacter ( ) <= '9' )
-	  || ( m_source->getCharacter ( ) >= 'a' && m_source->getCharacter ( ) <= 'z' )
-	  || ( m_source->getCharacter ( ) >= 'A' && m_source->getCharacter ( ) <= 'Z' )
-	  || ( m_source->getCharacter ( ) == ':' ) ) {
-		res.m_raw += m_source->getCharacter ( );
-		res.m_value += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( ( m_source.getCharacter ( ) >= '0' && m_source.getCharacter ( ) <= '9' )
+	  || ( m_source.getCharacter ( ) >= 'a' && m_source.getCharacter ( ) <= 'z' )
+	  || ( m_source.getCharacter ( ) >= 'A' && m_source.getCharacter ( ) <= 'Z' )
+	  || ( m_source.getCharacter ( ) == ':' ) ) {
+		res.m_raw += m_source.getCharacter ( );
+		res.m_value += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto q3;
-	} else if ( m_source->getCharacter ( ) == '\\' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( true );
+	} else if ( m_source.getCharacter ( ) == '\\' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( true );
 		goto q3Escape;
 	} else {
 		res.m_type = is_kw ( res.m_value );
@@ -234,118 +234,118 @@ q3:	if ( m_source->isEndOfSequence ( ) ) {
 	}
 
 q3Escape:
-	if ( m_source->isEndOfSequence ( ) ) {
+	if ( m_source.getCharacter ( ) == '\0' ) {
 		res.m_type = TokenType::ERROR;
 		return res;
 	}
 
-	res.m_raw += m_source->getCharacter ( );
-	res.m_value += m_source->getCharacter ( );
-	m_source->advance ( readNextLine );
+	res.m_raw += m_source.getCharacter ( );
+	res.m_value += m_source.getCharacter ( );
+	m_source.advance ( readNextLine );
 	goto q3;
 
-q4:	if ( m_source->isEndOfSequence ( ) ) {
+q4:	if ( m_source.getCharacter ( ) == '\0' ) {
 		res.m_type = TokenType::ERROR;
 		return res;
 	}
-	if ( m_source->getCharacter ( ) == '"' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( m_source.getCharacter ( ) == '"' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto q6;
 	}
-	if ( m_source->getCharacter ( ) == '\\' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( true );
+	if ( m_source.getCharacter ( ) == '\\' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( true );
 		goto q5;
 	} else {
-		res.m_raw += m_source->getCharacter ( );
-		res.m_value += m_source->getCharacter ( );
-		m_source->advance ( true );
+		res.m_raw += m_source.getCharacter ( );
+		res.m_value += m_source.getCharacter ( );
+		m_source.advance ( true );
 		goto q4;
 	}
 
-q5:	if ( m_source->isEndOfSequence ( ) ) {
+q5:	if ( m_source.getCharacter ( ) == '\0' ) {
 		res.m_type = TokenType::ERROR;
 		return res;
 	}
 
-	if ( m_source->getCharacter ( ) == 'n' ) {
-		res.m_raw += m_source->getCharacter ( );
+	if ( m_source.getCharacter ( ) == 'n' ) {
+		res.m_raw += m_source.getCharacter ( );
 		res.m_value += '\n';
-	} else if ( m_source->getCharacter ( ) == 't' ) {
-		res.m_raw += m_source->getCharacter ( );
+	} else if ( m_source.getCharacter ( ) == 't' ) {
+		res.m_raw += m_source.getCharacter ( );
 		res.m_value += '\t';
-	} else if ( m_source->getCharacter ( ) == '"' ) {
-		res.m_raw += m_source->getCharacter ( );
+	} else if ( m_source.getCharacter ( ) == '"' ) {
+		res.m_raw += m_source.getCharacter ( );
 		res.m_value += '"';
 	} else {
-		res.m_raw += m_source->getCharacter ( );
-		res.m_value += m_source->getCharacter ( );
+		res.m_raw += m_source.getCharacter ( );
+		res.m_value += m_source.getCharacter ( );
 	}
 
-	m_source->advance ( true );
+	m_source.advance ( true );
 	goto q4;
 
-q6:	if ( m_source->isEndOfSequence ( ) ) {
+q6:	if ( m_source.getCharacter ( ) == '\0' ) {
 		return res;
 	}
-	if ( isspace ( m_source->getCharacter ( ) ) ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( isspace ( m_source.getCharacter ( ) ) ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto q6;
 	}
-	if ( m_source->getCharacter ( ) == '"' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( true );
+	if ( m_source.getCharacter ( ) == '"' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( true );
 		goto q4;
 	} else {
 		return res;
 	}
 
 qFile:
-	if ( m_source->isEndOfTransmition ( ) ) {
+	if ( m_source.getCharacter ( ) == EOF ) {
 		res.m_type = TokenType::EOT;
 		return res;
 	}
-	if ( m_source->isEndOfSequence ( ) ) {
+	if ( m_source.getCharacter ( ) == '\0' ) {
 		res.m_type = TokenType::EOS;
 		return res;
 	}
-	if ( isspace ( m_source->getCharacter ( ) ) ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( isspace ( m_source.getCharacter ( ) ) ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto qFile;
 	}
-	if ( ( m_source->getCharacter ( ) >= '0' && m_source->getCharacter ( ) <= '9' )
-	  || ( m_source->getCharacter ( ) >= 'a' && m_source->getCharacter ( ) <= 'z' )
-	  || ( m_source->getCharacter ( ) >= 'A' && m_source->getCharacter ( ) <= 'Z' )
-	  ||   m_source->getCharacter ( ) == '/' || m_source->getCharacter ( ) == '.' || m_source->getCharacter ( ) == '-'
-	  ||   m_source->getCharacter ( ) == '~' || m_source->getCharacter ( ) == '_' || m_source->getCharacter ( ) == ':' ) {
-		res.m_raw += m_source->getCharacter ( );
-		res.m_value += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( ( m_source.getCharacter ( ) >= '0' && m_source.getCharacter ( ) <= '9' )
+	  || ( m_source.getCharacter ( ) >= 'a' && m_source.getCharacter ( ) <= 'z' )
+	  || ( m_source.getCharacter ( ) >= 'A' && m_source.getCharacter ( ) <= 'Z' )
+	  ||   m_source.getCharacter ( ) == '/' || m_source.getCharacter ( ) == '.' || m_source.getCharacter ( ) == '-'
+	  ||   m_source.getCharacter ( ) == '~' || m_source.getCharacter ( ) == '_' || m_source.getCharacter ( ) == ':' ) {
+		res.m_raw += m_source.getCharacter ( );
+		res.m_value += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto qFile2;
 	} else {
 		goto q0;
 	}
 
 qFile2:
-	if ( m_source->isEndOfSequence ( ) ) {
+	if ( m_source.getCharacter ( ) == '\0' ) {
 		res.m_type = TokenType::FILE;
 		return res;
 	}
-	if ( ( m_source->getCharacter ( ) >= '0' && m_source->getCharacter ( ) <= '9' )
-	  || ( m_source->getCharacter ( ) >= 'a' && m_source->getCharacter ( ) <= 'z' )
-	  || ( m_source->getCharacter ( ) >= 'A' && m_source->getCharacter ( ) <= 'Z' )
-	  ||   m_source->getCharacter ( ) == '/' || m_source->getCharacter ( ) == '.' || m_source->getCharacter ( ) == '-'
-	  ||   m_source->getCharacter ( ) == '~' || m_source->getCharacter ( ) == '_' || m_source->getCharacter ( ) == ':' ) {
-		res.m_raw += m_source->getCharacter ( );
-		res.m_value += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( ( m_source.getCharacter ( ) >= '0' && m_source.getCharacter ( ) <= '9' )
+	  || ( m_source.getCharacter ( ) >= 'a' && m_source.getCharacter ( ) <= 'z' )
+	  || ( m_source.getCharacter ( ) >= 'A' && m_source.getCharacter ( ) <= 'Z' )
+	  ||   m_source.getCharacter ( ) == '/' || m_source.getCharacter ( ) == '.' || m_source.getCharacter ( ) == '-'
+	  ||   m_source.getCharacter ( ) == '~' || m_source.getCharacter ( ) == '_' || m_source.getCharacter ( ) == ':' ) {
+		res.m_raw += m_source.getCharacter ( );
+		res.m_value += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto qFile2;
-	} else if ( m_source->getCharacter ( ) == '\\' ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( true );
+	} else if ( m_source.getCharacter ( ) == '\\' ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( true );
 		goto qFileEscape;
 	} else {
 		res.m_type = TokenType::FILE;
@@ -353,47 +353,47 @@ qFile2:
 	}
 
 qFileEscape:
-	if ( m_source->isEndOfSequence ( ) ) {
+	if ( m_source.getCharacter ( ) == '\0' ) {
 		res.m_type = TokenType::ERROR;
 		return res;
 	}
 
-	res.m_raw += m_source->getCharacter ( );
-	res.m_value += m_source->getCharacter ( );
-	m_source->advance ( readNextLine );
+	res.m_raw += m_source.getCharacter ( );
+	res.m_value += m_source.getCharacter ( );
+	m_source.advance ( readNextLine );
 	goto qFile2;
 
 qType:
-	if ( m_source->isEndOfTransmition ( ) ) {
+	if ( m_source.getCharacter ( ) == EOF ) {
 		res.m_type = TokenType::EOT;
 		return res;
 	}
-	if ( m_source->isEndOfSequence ( ) ) {
+	if ( m_source.getCharacter ( ) == '\0' ) {
 		res.m_type = TokenType::EOS;
 		return res;
 	}
-	if ( isspace ( m_source->getCharacter ( ) ) ) {
-		res.m_raw += m_source->getCharacter ( );
-		m_source->advance ( readNextLine );
+	if ( isspace ( m_source.getCharacter ( ) ) ) {
+		res.m_raw += m_source.getCharacter ( );
+		m_source.advance ( readNextLine );
 		goto qType;
 	}
-	if ( m_source->getCharacter ( ) == ')' ) {
+	if ( m_source.getCharacter ( ) == ')' ) {
 		goto q0;
 	}
 
 	{
 		unsigned lparens = 0;
-		while ( ! m_source->isEndOfSequence ( ) ) {
-			if ( m_source->getCharacter ( ) == '(' )
+		while ( m_source.getCharacter ( ) != '\0' ) {
+			if ( m_source.getCharacter ( ) == '(' )
 				++ lparens;
-			else if ( m_source->getCharacter ( ) == ')' && lparens > 0 )
+			else if ( m_source.getCharacter ( ) == ')' && lparens > 0 )
 				-- lparens;
-			else if ( m_source->getCharacter ( ) == ')' ) {
+			else if ( m_source.getCharacter ( ) == ')' ) {
 				break;
 			}
-			res.m_raw += m_source->getCharacter ( );
-			res.m_value += m_source->getCharacter ( );
-			m_source->advance ( readNextLine );
+			res.m_raw += m_source.getCharacter ( );
+			res.m_value += m_source.getCharacter ( );
+			m_source.advance ( readNextLine );
 		}
 		res.m_value = ext::rtrim ( res.m_value );
 
diff --git a/alib2cli/src/lexer/Lexer.h b/alib2cli/src/lexer/Lexer.h
index 9a6c9baf9f..d123bb6236 100644
--- a/alib2cli/src/lexer/Lexer.h
+++ b/alib2cli/src/lexer/Lexer.h
@@ -5,11 +5,12 @@
 
 #include <alib/string>
 #include <alib/iostream>
+#include <sstream>
 
 #include <exception/CommonException.h>
 
-#include "CharSequenceBase.h"
-#include "StringCharSequence.h"
+#include "CharSequence.h"
+#include <readline/StringLineReader.h>
 
 namespace cli {
 
@@ -22,7 +23,7 @@ public:
 	};
 
 private:
-	std::unique_ptr < CharSequenceBase > m_source;
+	CharSequence & m_source;
 	Hint m_hint;
 
 public:
@@ -143,23 +144,14 @@ public:
 
 	};
 
-	template < class T, typename Enable = std::enable_if < ! std::is_same_v < std::decay_t < T >, Lexer >, void > >
-	explicit Lexer ( T && source ) : Lexer ( std::unique_ptr < CharSequenceBase > ( new T ( std::forward < T > ( source ) ) ) ) {
-	}
-
-	explicit Lexer ( std::unique_ptr < CharSequenceBase > source ) : m_source ( std::move ( source ) ), m_hint ( Hint::NONE ) {
-	}
-
-	explicit Lexer ( std::string source ) : Lexer ( std::unique_ptr < CharSequenceBase > ( new StringCharSequence ( std::move ( source ) ) ) ) {
-	}
-
-	explicit Lexer ( const char * source ) : Lexer ( std::string ( source ) ) {
+	explicit Lexer ( CharSequence & source ) : m_source ( source ), m_hint ( Hint::NONE ) {
+		m_source.advance ( true );
 	}
 
 	Token nextToken ( bool readNextLine = false );
 
 	void putback ( Token && token ) {
-		m_source->putback ( std::move ( token.m_raw ) );
+		m_source.putback ( std::move ( token.m_raw ) );
 	}
 
 	void setHint ( Hint hint ) {
@@ -167,7 +159,7 @@ public:
 	}
 
 	std::string getLine ( ) const {
-		return m_source->getLine ( );
+		return m_source.getLine ( );
 	}
 };
 
diff --git a/alib2cli/src/lexer/StringCharSequence.h b/alib2cli/src/lexer/StringCharSequence.h
deleted file mode 100644
index 14ba8d6d87..0000000000
--- a/alib2cli/src/lexer/StringCharSequence.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef __STRING_CHAR_SEQUENCE_H_
-#define __STRING_CHAR_SEQUENCE_H_
-
-#include <string>
-
-#include "CharSequenceBase.h"
-
-namespace cli {
-
-class StringCharSequence : public CharSequenceBase {
-	std::string m_line;
-
-	void fetch ( bool /* readNextLine */ ) override {
-		this->endOfSequence = true;
-	}
-
-public:
-	explicit StringCharSequence ( std::string line ) : m_line ( std::move ( line ) ) {
-		this->linePtr = m_line.c_str ( );
-	}
-
-	StringCharSequence ( StringCharSequence && other ) noexcept : CharSequenceBase ( std::move ( other ) ) {
-		size_t dist = this->linePtr - other.m_line.c_str ( );
-		m_line = std::move ( other.m_line );
-		this->linePtr = m_line.c_str ( ) + dist;
-	}
-
-	StringCharSequence ( const StringCharSequence & ) = delete;
-
-	StringCharSequence & operator = ( StringCharSequence && ) = delete;
-
-	StringCharSequence & operator = ( const StringCharSequence & ) = delete;
-
-	std::string getLine ( ) const override {
-		return m_line;
-	}
-};
-
-} /* namespace cli */
-
-#endif /* __STRING_CHAR_SEQUENCE_H_ */
diff --git a/alib2cli/src/parser/Parser.cpp b/alib2cli/src/parser/Parser.cpp
index 07e429e8de..94a1c5c456 100644
--- a/alib2cli/src/parser/Parser.cpp
+++ b/alib2cli/src/parser/Parser.cpp
@@ -17,6 +17,7 @@
 
 #include <command/ExecuteCommand.h>
 #include <command/QuitCommand.h>
+#include <command/EOTCommand.h>
 #include <command/HelpCommand.h>
 #include <command/AlgorithmsIntrospectionCommand.h>
 #include <command/OverloadsIntrospectionCommand.h>
@@ -240,7 +241,7 @@ std::shared_ptr < Statement > Parser::statement ( ) {
 		std::unique_ptr < CategoryOption > category = category_option ( );
 		ext::vector < std::shared_ptr < Statement > > params;
 		ext::vector < bool > moves;
-		while ( ! check ( cli::Lexer::TokenType::OUT_REDIRECT, cli::Lexer::TokenType::PIPE_SIGN, cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::RIGHT_PAREN, cli::Lexer::TokenType::SEMICOLON_SIGN ) ) {
+		while ( ! check ( cli::Lexer::TokenType::OUT_REDIRECT, cli::Lexer::TokenType::PIPE_SIGN, cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::EOT, cli::Lexer::TokenType::RIGHT_PAREN, cli::Lexer::TokenType::SEMICOLON_SIGN ) ) {
 			moves.push_back ( move_arg ( ) );
 			params.emplace_back ( param ( ) );
 		}
@@ -286,7 +287,7 @@ void Parser::out_redirect ( std::shared_ptr < StatementList > & list ) {
 		match ( cli::Lexer::TokenType::DOLAR_SIGN );
 		std::unique_ptr < Arg > name = arg ( );
 		list->append ( std::make_unique < ResultVariableStatement > ( std::move ( name ) ) );
-	} else if ( check ( cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::SEMICOLON_SIGN ) ) {
+	} else if ( check ( cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::EOT, cli::Lexer::TokenType::SEMICOLON_SIGN ) ) {
 		return;
 	} else {
 		out_redirect_file ( list );
@@ -297,7 +298,7 @@ void Parser::result ( std::shared_ptr < StatementList > & list ) {
 	if ( check ( cli::Lexer::TokenType::OUT_REDIRECT ) ) {
 		match ( cli::Lexer::TokenType::OUT_REDIRECT );
 		out_redirect ( list );
-	} else if ( check ( cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::SEMICOLON_SIGN ) ) {
+	} else if ( check ( cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::EOT, cli::Lexer::TokenType::SEMICOLON_SIGN ) ) {
 		list->append ( std::make_unique < ResultPrintStatement > ( ) );
 	} else {
 		return;
@@ -380,7 +381,7 @@ std::unique_ptr < Command > Parser::command ( ) {
 		match_nonreserved_kw ( "quit" );
 
 		std::shared_ptr < StatementList > res;
-		if ( ! check ( cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::SEMICOLON_SIGN ) )
+		if ( ! check ( cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::EOT, cli::Lexer::TokenType::SEMICOLON_SIGN ) )
 			res = statement_list ( );
 
 		return std::make_unique < QuitCommand > ( res );
@@ -388,7 +389,7 @@ std::unique_ptr < Command > Parser::command ( ) {
 		match_nonreserved_kw ( "exit" );
 
 		std::shared_ptr < StatementList > res;
-		if ( ! check ( cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::SEMICOLON_SIGN ) )
+		if ( ! check ( cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::EOT, cli::Lexer::TokenType::SEMICOLON_SIGN ) )
 			res = statement_list ( );
 
 		return std::make_unique < QuitCommand > ( res );
@@ -418,7 +419,7 @@ std::unique_ptr < Command > Parser::command ( ) {
 		match ( cli::Lexer::TokenType::INTEGER, cli::Lexer::TokenType::IDENTIFIER, cli::Lexer::TokenType::STRING );
 		return std::make_unique < UnloadCommand > ( std::move ( libraryName ) );
 	} else if ( check ( cli::Lexer::TokenType::EOT ) ) {
-		return std::make_unique < QuitCommand > ( nullptr );
+		return std::make_unique < EOTCommand > ( );
 	} else {
 		throw exception::CommonException ( "Mismatched set while expanding parse rule. Token is " + Lexer::tokenTypeToString ( m_current.m_type ) + "." );
 	}
diff --git a/alib2cli/src/readline/IstreamLineReader.h b/alib2cli/src/readline/IstreamLineReader.h
new file mode 100644
index 0000000000..b65d94467e
--- /dev/null
+++ b/alib2cli/src/readline/IstreamLineReader.h
@@ -0,0 +1,37 @@
+/*
+ * IstreamLineReader.h
+ *
+ *  Created on: 20. 3. 2017
+ *	  Author: Jan Travnicek
+ */
+
+#ifndef _ISTREAM_LINE_READER_H_
+#define _ISTREAM_LINE_READER_H_
+
+#include <string>
+
+#include <readline/LineReader.h>
+
+namespace cli {
+
+/**
+ * Stream line reader serves as an adaptor to read from arbitrary stream.
+ *
+ * \tparam Stream the type of the stream to read from. It can be either reference or value.
+ */
+template < class Stream >
+class IstreamLineReader final : public cli::LineReader {
+	Stream m_is;
+
+	bool readline ( std::string & line ) override {
+		return ( bool ) std::getline ( m_is, line );
+	}
+
+public:
+	IstreamLineReader ( Stream ifs ) : m_is ( std::forward < Stream > ( ifs ) ) {
+	}
+};
+
+} // namespace cli
+
+#endif /* _ISTREAM_LINE_READER_H_ */
diff --git a/alib2cli/src/readline/LineReader.h b/alib2cli/src/readline/LineReader.h
new file mode 100644
index 0000000000..676a876513
--- /dev/null
+++ b/alib2cli/src/readline/LineReader.h
@@ -0,0 +1,43 @@
+/*
+ * LineReader.h
+ *
+ *  Created on: 20. 3. 2017
+ *	  Author: Jan Travnicek
+ */
+
+#ifndef _LINE_READER_H_
+#define _LINE_READER_H_
+
+#include <string>
+
+#include <readline/LineReader.h>
+
+namespace cli {
+
+/**
+ * Line reader serves as a base class for adaptors reding from various source.
+ */
+class LineReader {
+public:
+	virtual bool readline ( std::string & line ) = 0;
+
+	virtual void reset ( ) {
+	}
+
+public:
+	LineReader ( ) = default;
+
+	LineReader ( LineReader && ) noexcept = default;
+
+	LineReader ( const LineReader & ) = delete;
+
+	LineReader & operator = ( LineReader && ) noexcept = delete;
+
+	LineReader & operator = ( const LineReader & ) = delete;
+
+	virtual ~LineReader ( ) noexcept = default;
+};
+
+} // namespace cli
+
+#endif /* _LINE_READER_H_ */
diff --git a/alib2cli/src/readline/StringLineReader.h b/alib2cli/src/readline/StringLineReader.h
new file mode 100644
index 0000000000..1b632377c9
--- /dev/null
+++ b/alib2cli/src/readline/StringLineReader.h
@@ -0,0 +1,35 @@
+/*
+ * StringLineReader.h
+ *
+ *  Created on: 20. 3. 2017
+ *	  Author: Jan Travnicek
+ */
+
+#ifndef _STRING_LINE_READER_H_
+#define _STRING_LINE_READER_H_
+
+#include <string>
+
+#include <readline/LineReader.h>
+
+namespace cli {
+
+class StringLineReader final : public cli::LineReader {
+	std::string m_string;
+
+	bool readline ( std::string & line ) override {
+		if ( m_string == "" )
+			return false;
+
+		line = std::exchange ( m_string, "" );
+		return true;
+	}
+
+public:
+	StringLineReader ( std::string string ) : m_string ( std::move ( string ) ) {
+	}
+};
+
+} // namespace cli
+
+#endif /* _STRING_LINE_READER_H_ */
diff --git a/alib2cli/test-src/cli/CliTest.cpp b/alib2cli/test-src/cli/CliTest.cpp
index 4a332e96a5..8b1bfc6d9b 100644
--- a/alib2cli/test-src/cli/CliTest.cpp
+++ b/alib2cli/test-src/cli/CliTest.cpp
@@ -10,8 +10,8 @@
 #include <registration/AlgoRegistration.hpp>
 
 void testLine ( std::string line, cli::Environment & environment ) {
-	cli::Parser parser { cli::Lexer ( line ) };
-	parser.parse ( )->run ( environment );
+	cli::CharSequence sequence { cli::StringLineReader ( line ) };
+	cli::Parser ( cli::Lexer ( sequence ) ).parse ( )->run ( environment );
 }
 
 class Foo {
diff --git a/alib2integrationtest/test-src/testing/TimeoutAqlTest.cpp b/alib2integrationtest/test-src/testing/TimeoutAqlTest.cpp
index f9d6cf6a80..dd487e298c 100644
--- a/alib2integrationtest/test-src/testing/TimeoutAqlTest.cpp
+++ b/alib2integrationtest/test-src/testing/TimeoutAqlTest.cpp
@@ -56,12 +56,12 @@ int aqlTest ( int fd, const ext::vector < std::string > & queries, unsigned seed
 		common::Streams::out = ss;
 		common::Streams::err = ss;
 
-		cli::Parser parser = cli::Parser ( cli::Lexer ( "set seed " + ext::to_string ( seed ) ) );
-		parser.parse ( ) -> run ( environment );
+		cli::CharSequence sequence { cli::StringLineReader ( "set seed " + ext::to_string ( seed ) ) };
+		cli::Parser ( cli::Lexer ( sequence ) ).parse ( ) -> run ( environment );
 
 		for ( const std::string & q : queries ) {
-			parser = cli::Parser ( cli::Lexer ( q ) );
-			parser.parse ( ) -> run ( environment );
+			cli::CharSequence querySequence { cli::StringLineReader ( q ) };
+			cli::Parser ( cli::Lexer ( querySequence ) ).parse ( ) -> run ( environment );
 
 			if ( write ( fd, ss.str ( ).c_str ( ), ss.str ( ).length ( ) ) != ( ssize_t ) ss.str ( ).length ( ) )
 				throw std::runtime_error ( "TimeoutAqlTest: child output write() failure (child to parent communication)" );
diff --git a/aql2/src/aql.cpp b/aql2/src/aql.cpp
index 66cf4b5fec..fd27edbfd4 100644
--- a/aql2/src/aql.cpp
+++ b/aql2/src/aql.cpp
@@ -9,6 +9,7 @@
 
 #include <istream>
 #include <iostream>
+#include <fstream>
 
 template < class T, class U >
 std::istream& operator>> ( std::istream & in, std::pair < T, U > & value ) {
@@ -37,6 +38,10 @@ std::istream& operator>> ( std::istream & in, std::pair < T, U > & value ) {
 #include <global/GlobalData.h>
 
 #include "prompt/Prompt.h"
+#include "prompt/ReadlineLineReader.h"
+
+#include <readline/IstreamLineReader.h>
+#include <readline/StringLineReader.h>
 
 namespace TCLAP {
 
@@ -47,6 +52,42 @@ struct ArgTraits < std::pair < T, U > > {
 
 } /* namespace TCLAP */
 
+class InteractiveVisitor : public TCLAP::Visitor {
+public:
+	void visit ( ) override {
+		Prompt::getPrompt ( ).appendCharSequence ( ReadlineLineReader ( ), true );
+	}
+};
+
+class FileVisitor : public TCLAP::Visitor {
+	TCLAP::MultiArg < std::string > * m_arg;
+
+public:
+	FileVisitor ( TCLAP::MultiArg < std::string > * arg ) : m_arg ( arg ) {
+	}
+
+	void visit ( ) override {
+		std::ifstream ifs ( m_arg->getValue ( ).back ( ) );
+
+		if ( ! ifs.is_open ( ) )
+			throw exception::CommonException ( "File '" + m_arg->getValue ( ).back ( ) + "' not found." );
+
+		Prompt::getPrompt ( ).appendCharSequence ( cli::IstreamLineReader < std::ifstream > ( std::move ( ifs ) ), false );
+	}
+};
+
+class QueriesVisitor : public TCLAP::Visitor {
+	TCLAP::MultiArg < std::string > * m_arg;
+
+public:
+	QueriesVisitor ( TCLAP::MultiArg < std::string > * arg ) : m_arg ( arg ) {
+	}
+
+	void visit ( ) override {
+		Prompt::getPrompt ( ).appendCharSequence ( cli::StringLineReader ( m_arg->getValue ( ).back ( ) ), false );
+	}
+};
+
 int main ( int argc, char * argv[] ) {
 	alib::ExceptionHandler::addHandler < 2 > ( [] ( alib::ExceptionHandler::NestedExceptionContainer & exceptions, const TCLAP::ArgException & exception ) {
 		exceptions.push_back ( exception.error ( ) );
@@ -56,31 +97,38 @@ int main ( int argc, char * argv[] ) {
 		common::GlobalData::argc = argc;
 		common::GlobalData::argv = argv;
 
-		TCLAP::CmdLine cmd ( "Algorithms query language binary", ' ', ALIB_VERSION_INFO ); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
-
-		TCLAP::MultiArg < std::string > queries ( "q", "query", "Query string", false, "string" );
-		cmd.add ( queries );
+		TCLAP::CmdLine cmd ( "Algorithms Query Language shell", ' ', ALIB_VERSION_INFO ); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
 
-		TCLAP::SwitchArg measure ( "m", "measure", "Measure times", false );
+		TCLAP::SwitchArg measure ( "m", "measure", "Sets measure mode", false );
 		cmd.add ( measure );
 
-		TCLAP::SwitchArg verbose ( "v", "verbose", "Be verbose", false );
+		TCLAP::SwitchArg verbose ( "v", "verbose", "Sets verbose mode (set verbose 1)", false );
 		cmd.add ( verbose );
 
-		TCLAP::MultiArg < std::pair < std::string, std::string > > params ( "p", "params", "Query index", false, "pair < string, string >" );
+		TCLAP::MultiArg < std::pair < std::string, std::string > > params ( "e", "env", "Environment variable", false, "key = val");
 		cmd.add ( params );
 
+		TCLAP::SwitchArg interactive ( "i", "interactive", "Stay in interactive mode after -c or -f", false, new InteractiveVisitor ( ) );
+		cmd.add ( interactive );
+
+		TCLAP::MultiArg < std::string > files ( "f", "file", "Loads an aql file", false, "FILE", new FileVisitor ( & files ) );
+		cmd.add ( files );
+
+		TCLAP::MultiArg < std::string > queries ( "c", "command", "Query to execute. Multiple queries execute in sequential order.", false, "string", new QueriesVisitor ( & queries ) );
+		cmd.add ( queries );
+
 		cmd.parse ( argc, argv );
 
-		if(verbose.isSet())
-			common::GlobalData::verbose = true;
-		if(measure.isSet())
-			common::GlobalData::measure = true;
+		common::GlobalData::verbose = verbose.isSet();
+		common::GlobalData::measure = measure.isSet();
+
+		/* --------------------------------------------------------------------------------------------------------- */
 
-		measurements::start ( "Overal", measurements::Type::OVERALL );
+		measurements::start ( "Overall", measurements::Type::OVERALL );
 
-		Prompt & p = Prompt::getPrompt ( );
-		cli::Environment & environment = p.getEnvironment ( );
+		/* --------------------------------------------------------------------------------------------------------- */
+
+		cli::Environment & environment = Prompt::getPrompt ( ).getEnvironment ( );
 
 		environment.setBinding ( "stdin", "-" );
 		environment.setBinding ( "stdout", "-" );
@@ -89,27 +137,28 @@ int main ( int argc, char * argv[] ) {
 			environment.setBinding ( param.first, param.second );
 		}
 
-		cli::Command::Result result = cli::Command::Result::OK;
-		if ( queries.getValue ( ).empty ( ) ) {
-			result = p.run ( );
-		} else {
-			for ( const std::string & query : queries.getValue ( ) ) {
-				result = p.execute_line ( cli::StringCharSequence ( query ), false );
-				if ( result != cli::Command::Result::OK )
-					break;
-			}
-
-			if ( result == cli::Command::Result::OK )
-				result = cli::Command::Result::QUIT;
+		/* --------------------------------------------------------------------------------------------------------- */
+
+		// if no -f, -c, or -i, go interactive.
+		if ( ! queries.isSet ( ) && ! files.isSet ( ) && ! interactive.isSet ( ) ) {
+			Prompt::getPrompt ( ).appendCharSequence ( ReadlineLineReader ( ), true );
 		}
 
+		cli::Command::Result res = Prompt::getPrompt ( ).run ( );
+
+		/* --------------------------------------------------------------------------------------------------------- */
+
 		measurements::end ( );
 
 		if ( measure.getValue ( ) )
 			common::Streams::measure << measurements::results ( ) << std::endl;
 
-		if ( result == cli::Command::Result::QUIT )
-			return p.getEnvironment ( ).getResult ( );
+		/* --------------------------------------------------------------------------------------------------------- */
+
+		if ( res == cli::Command::Result::QUIT )
+			return Prompt::getPrompt ( ).getEnvironment ( ).getResult ( );
+		else if ( res == cli::Command::Result::EOT )
+			return 0;
 		else
 			return 4;
 	} catch ( ... ) {
diff --git a/aql2/src/prompt/Prompt.cpp b/aql2/src/prompt/Prompt.cpp
index cc8a8b18bf..8d04b5a9d4 100644
--- a/aql2/src/prompt/Prompt.cpp
+++ b/aql2/src/prompt/Prompt.cpp
@@ -9,6 +9,15 @@
 #include "ReadlinePromptCompletion.h"
 #include "ReadlinePromptHistory.h"
 
+#include <lexer/Lexer.h>
+#include <parser/Parser.h>
+
+#include "HistoryRegister.h"
+
+#include <alib/exception>
+
+#include <global/GlobalData.h>
+
 Prompt::Prompt ( cli::Environment environment ) : m_history_file ( std::string ( std::getenv ( "HOME" ) ) + "/.aql_history" ), m_environment ( std::move ( environment ) ) {
 	ReadlinePromptHistory::readHistory ( m_history_file );
 }
@@ -17,12 +26,42 @@ Prompt::~Prompt ( ) {
 	ReadlinePromptHistory::writeHistory ( m_history_file );
 }
 
+void Prompt::appendCharSequence ( cli::CharSequence && charSequence, bool allowHistory ) {
+	m_charSequences.push_back ( std::make_pair ( std::move ( charSequence ), allowHistory ) );
+}
+
+void Prompt::prependCharSequence ( cli::CharSequence && charSequence, bool allowHistory ) {
+	m_charSequences.push_front ( std::make_pair ( std::move ( charSequence ), allowHistory ) );
+}
+
 cli::Command::Result Prompt::run ( ) {
 	cli::Command::Result state = cli::Command::Result::OK;
 
-	while ( state != cli::Command::Result::QUIT && state != cli::Command::Result::ERROR ) {
-		state = execute_line ( ReadlineCharSequence ( ), true );
+	while ( ! m_charSequences.empty ( ) ) {
+
+		while ( state == cli::Command::Result::OK || state == cli::Command::Result::ERROR || state == cli::Command::Result::EXCEPTION ) {
+			state = execute_line ( m_charSequences.front ( ).first, m_charSequences.front ( ).second );
+			m_charSequences.front ( ).first.reset ( );
+		}
+
+		if ( state == cli::Command::Result::QUIT )
+			return state;
+
+		state = cli::Command::Result::OK;
+
+		m_charSequences.pop_front ( );
 	}
 
 	return state;
 }
+
+cli::Command::Result Prompt::execute_line ( cli::CharSequence & charSequence, bool allowHistory ) {
+	try {
+		cli::Parser parser = cli::Parser ( cli::Lexer ( charSequence ) );
+		HistoryRegister historyRegister ( parser.getLexer ( ), allowHistory );
+		return parser.parse ( )->run ( m_environment );
+	} catch ( ... ) {
+		alib::ExceptionHandler::handle ( common::Streams::err );
+		return cli::Command::Result::EXCEPTION;
+	}
+}
diff --git a/aql2/src/prompt/Prompt.h b/aql2/src/prompt/Prompt.h
index 7ba301e867..c3e28471c7 100644
--- a/aql2/src/prompt/Prompt.h
+++ b/aql2/src/prompt/Prompt.h
@@ -9,18 +9,15 @@
 #define _AQL_PROMPT_H_
 
 #include <string>
+#include <deque>
 
-#include <alib/exception>
-
-#include <lexer/Lexer.h>
-#include <parser/Parser.h>
-
-#include <global/GlobalData.h>
-
-#include "HistoryRegister.h"
-#include "ReadlineCharSequence.h"
+#include <command/Command.h>
+#include <environment/Environment.h>
+#include <lexer/CharSequence.h>
 
 class Prompt {
+	std::deque < std::pair < cli::CharSequence, bool > > m_charSequences;
+
 	std::string m_history_file;
 
 	cli::Environment m_environment;
@@ -43,19 +40,13 @@ public:
 		return instance;
 	}
 
+	void appendCharSequence ( cli::CharSequence && charSequence, bool allowHistory );
+
+	void prependCharSequence ( cli::CharSequence && charSequence, bool allowHistory );
+
 	cli::Command::Result run ( );
 
-	template < class T >
-	cli::Command::Result execute_line ( T && charSequence, bool allowHistory ) {
-		try {
-			cli::Parser parser = cli::Parser ( cli::Lexer ( std::forward < T && > ( charSequence ) ) );
-			HistoryRegister historyRegister ( parser.getLexer ( ), allowHistory );
-			return parser.parse ( )->run ( m_environment );
-		} catch ( ... ) {
-			alib::ExceptionHandler::handle ( common::Streams::err );
-			return cli::Command::Result::EXCEPTION;
-		}
-	}
+	cli::Command::Result execute_line ( cli::CharSequence & charSequence, bool allowHistory );
 
 	cli::Environment & getEnvironment ( ) {
 		return m_environment;
diff --git a/aql2/src/prompt/ReadlineCharSequence.h b/aql2/src/prompt/ReadlineCharSequence.h
deleted file mode 100644
index a7fd8c914a..0000000000
--- a/aql2/src/prompt/ReadlineCharSequence.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * ReadlineCharSequence.h
- *
- *  Created on: 20. 3. 2017
- *	  Author: Jan Travnicek
- */
-
-#ifndef _READLINE_CHAR_SEQUENCE_H_
-#define _READLINE_CHAR_SEQUENCE_H_
-
-#include <cctype>
-#include <string>
-#include <vector>
-#include <algorithm>
-
-#include <readline/readline.h>
-
-#include <lexer/CharSequenceBase.h>
-
-class ReadlineCharSequence final : public cli::CharSequenceBase {
-	std::vector < std::string > m_lines;
-	bool first = true;
-	bool forceReadLine = false;
-
-	void fetch ( bool readNextLine ) override {
-		if ( ! readNextLine && ! forceReadLine ) {
-			this->endOfSequence = true;
-			return;
-		}
-
-		char * read = ::readline ( first ? "> ": "+ " );
-		if ( read == nullptr ) {
-			this->endOfTransmition = true;
-			return;
-		}
-
-		std::string line ( read );
-		free ( read );
-
-		forceReadLine = !line.empty ( ) && line.at ( line.size ( ) - 1 ) == '\\';
-		if ( forceReadLine )
-			line.back ( ) = '\n';
-		else
-			line += '\n';
-
-		if ( ! std::all_of ( line.begin ( ), line.end ( ), isspace ) )
-			first = false;
-
-		m_lines.push_back ( std::move ( line ) );
-
-		this->linePtr = m_lines.back ( ).c_str ( );
-	}
-
-public:
-	ReadlineCharSequence ( ) {
-		fetch ( true );
-	}
-
-	ReadlineCharSequence ( ReadlineCharSequence && ) = default;
-
-	ReadlineCharSequence ( const ReadlineCharSequence & ) = delete;
-
-	ReadlineCharSequence & operator = ( ReadlineCharSequence && ) = delete;
-
-	ReadlineCharSequence & operator = ( const ReadlineCharSequence & ) = delete;
-
-	std::string getLine ( ) const override {
-		std::string res;
-		for ( const std::string & line : m_lines ) {
-			res += line;
-		}
-		return res;
-	}
-};
-
-#endif /* _READLINE_CHAR_SEQUENCE_H_ */
diff --git a/aql2/src/prompt/ReadlineLineReader.h b/aql2/src/prompt/ReadlineLineReader.h
new file mode 100644
index 0000000000..9359bf0711
--- /dev/null
+++ b/aql2/src/prompt/ReadlineLineReader.h
@@ -0,0 +1,39 @@
+/*
+ * ReadlineLineReader.h
+ *
+ *  Created on: 20. 3. 2017
+ *	  Author: Jan Travnicek
+ */
+
+#ifndef _READLINE_LINE_READER_H_
+#define _READLINE_LINE_READER_H_
+
+#include <cctype>
+#include <string>
+
+#include <readline/readline.h>
+
+class ReadlineLineReader final : public cli::LineReader {
+	bool first = true;
+
+	bool readline ( std::string & line ) override {
+		char * read = ::readline ( first ? "> ": "+ " );
+		if ( read == nullptr ) {
+			return false;
+		}
+
+		line = read;
+		free ( read );
+
+		if ( ! std::all_of ( line.begin ( ), line.end ( ), isspace ) )
+			first = false;
+
+		return true;
+	}
+
+	void reset ( ) override {
+		first = true;
+	}
+};
+
+#endif /* _READLINE_LINE_READER_H_ */
-- 
GitLab