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