diff --git a/alib2cli/src/lexer/Lexer.cpp b/alib2cli/src/lexer/Lexer.cpp index 7cb42321e02db1a2fc46306058abad373e94f6a3..3654066d9aa86f2b0c775f391c8ff532e2a49e1a 100644 --- a/alib2cli/src/lexer/Lexer.cpp +++ b/alib2cli/src/lexer/Lexer.cpp @@ -4,17 +4,21 @@ namespace cli { -Lexer::Token Lexer::nextToken () { +Lexer::Token Lexer::nextToken ( bool readNextLine ) { Token res { "", TokenType::ERROR }; + std::string newLine; q0: if ( m_index >= m_line.size ( ) ) { - res.m_type = TokenType::END; - return res; + goto qReadLine; } if ( isspace ( m_line [ m_index ] ) ) { m_index ++; goto q0; } + if ( m_line [ m_index ] == '\\' ) { + m_index ++; + goto qReadLineForce; + } if ( m_line [ m_index ] == '<' ) { m_index ++; res.m_type = TokenType::IN_REDIRECT; @@ -125,6 +129,23 @@ q0: if ( m_index >= m_line.size ( ) ) { res.m_type = TokenType::ERROR; return res; +qReadLine: + if ( ! readNextLine ) { + res.m_type = TokenType::EOL; + return res; + } + goto qReadLineForce; + +qReadLineForce: + newLine = m_lineCallback ( ); + if ( newLine.size ( ) == 0 ) { + res.m_type = TokenType::END; + return res; + } + + m_line += std::move ( newLine ); + goto q0; + q1: if ( m_index >= m_line.size ( ) ) { res.m_type = TokenType::INTEGER; return res; diff --git a/alib2cli/src/lexer/Lexer.h b/alib2cli/src/lexer/Lexer.h index cf57b93c42ba4c5fa32eec7a0a6d5229d85e366b..74d556df210b2d11200ede5730e048727f804407 100644 --- a/alib2cli/src/lexer/Lexer.h +++ b/alib2cli/src/lexer/Lexer.h @@ -11,6 +11,7 @@ namespace cli { class Lexer { unsigned m_index; std::string m_line; + std::function < std::string ( ) > m_lineCallback; public: enum class TokenType { @@ -35,7 +36,8 @@ public: EQUAL_SIGN, HASH_SIGN, ERROR, - END + END, + EOL }; static std::string tokenTypeToString ( TokenType type ) { @@ -84,8 +86,10 @@ public: return "error"; case TokenType::END : return "end"; + case TokenType::EOL : + return "eol"; default: - throw exception::CommonException ( "Unhandler case in Lexer::tokenTypeToString" ); + throw exception::CommonException ( "Unhandled case in Lexer::tokenTypeToString" ); } } @@ -117,10 +121,17 @@ public: }; - Lexer ( std::string line ) : m_index ( 0 ), m_line ( std::move ( line ) ) { + Lexer ( std::function < std::string ( ) > lineCallback ) : m_index ( 0 ), m_line ( "" ), m_lineCallback ( lineCallback ) { } - Token nextToken ( ); + Lexer ( std::string line ) : Lexer ( [ line { std::move ( line ) } ] ( ) mutable { + std::string res = std::move ( line ); + line = ""; + return res; + } ) { + } + + Token nextToken ( bool readNextLine = false ); const std::string & getLine ( ) const { return m_line; diff --git a/alib2cli/src/parser/Parser.cpp b/alib2cli/src/parser/Parser.cpp index 6a3c512f77b694c19e2169a3e309d6c3e1275138..e8ebe68b254778cace7ddd1d5d9ad3ecf2d2223a 100644 --- a/alib2cli/src/parser/Parser.cpp +++ b/alib2cli/src/parser/Parser.cpp @@ -185,7 +185,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 ) && ! check ( cli::Lexer::TokenType::PIPE_SIGN ) && ! check ( cli::Lexer::TokenType::END ) && ! check ( cli::Lexer::TokenType::RIGHT_PAREN ) ) { + while ( ! check ( cli::Lexer::TokenType::OUT_REDIRECT ) && ! check ( cli::Lexer::TokenType::PIPE_SIGN ) && ! check ( cli::Lexer::TokenType::EOL ) && ! check ( cli::Lexer::TokenType::RIGHT_PAREN ) ) { moves.push_back ( move_arg ( ) ); params.emplace_back ( param ( ) ); } @@ -232,7 +232,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::END ) ) { + } else if ( check ( cli::Lexer::TokenType::EOL ) ) { return; } else { out_redirect_file ( list ); @@ -243,7 +243,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::END ) ) { + } else if ( check ( cli::Lexer::TokenType::EOL ) ) { list->append ( std::make_unique < ResultPrintStatement > ( ) ); } else { return; @@ -302,21 +302,21 @@ std::unique_ptr < Command > Parser::parse ( ) { match_nonreserved_kw ( "execute" ); std::shared_ptr < StatementList > res = statement_list ( ); result ( res ); - match ( cli::Lexer::TokenType::END ); + match ( cli::Lexer::TokenType::EOL ); return std::make_unique < ExecuteCommand > ( res ); } else if ( check_nonreserved_kw ( "quit" ) ) { match_nonreserved_kw ( "quit" ); - match ( cli::Lexer::TokenType::END ); + match ( cli::Lexer::TokenType::EOL ); return std::make_unique < QuitCommand > ( ); } else if ( check_nonreserved_kw ( "help" ) ) { match_nonreserved_kw ( "help" ); std::unique_ptr < cli::Arg > command = optional_arg ( ); - match ( cli::Lexer::TokenType::END ); + match ( cli::Lexer::TokenType::EOL ); return std::make_unique < HelpCommand > ( std::move ( command ) ); } else if ( check_nonreserved_kw ( "introspect" ) ) { match_nonreserved_kw ( "introspect" ); std::unique_ptr < Command > command = introspect_command ( ); - match ( cli::Lexer::TokenType::END ); + match ( cli::Lexer::TokenType::EOL ); return command; } else if ( check_nonreserved_kw ( "set" ) ) { match_nonreserved_kw ( "set" ); @@ -324,14 +324,16 @@ std::unique_ptr < Command > Parser::parse ( ) { match ( cli::Lexer::TokenType::INTEGER, cli::Lexer::TokenType::IDENTIFIER ); std::string value = getTokenValue ( ); match ( cli::Lexer::TokenType::INTEGER, cli::Lexer::TokenType::IDENTIFIER, cli::Lexer::TokenType::STRING ); - match ( cli::Lexer::TokenType::END ); + match ( cli::Lexer::TokenType::EOL ); return std::make_unique < SetCommand > ( std::move ( param ), std::move ( value ) ); } else if ( check_nonreserved_kw ( "load" ) ) { match_nonreserved_kw ( "load" ); std::string libraryName = getTokenValue ( ); match ( cli::Lexer::TokenType::INTEGER, cli::Lexer::TokenType::IDENTIFIER, cli::Lexer::TokenType::STRING ); - match ( cli::Lexer::TokenType::END ); + match ( cli::Lexer::TokenType::EOL ); return std::make_unique < LoadCommand > ( std::move ( libraryName ) ); + } else if ( check ( cli::Lexer::TokenType::END ) ) { + return std::make_unique < QuitCommand > ( ); } else { throw exception::CommonException ( "Mismatched set while expanding parse rule." ); } diff --git a/alib2cli/src/parser/Parser.h b/alib2cli/src/parser/Parser.h index 4d769dad9f5b43c0265b7fd5068832e195dc6a98..151f1d1cc201ae04636bdaabef1008f8a213ddf5 100644 --- a/alib2cli/src/parser/Parser.h +++ b/alib2cli/src/parser/Parser.h @@ -25,7 +25,11 @@ class Parser { cli::Lexer::Token m_current; public: - Parser ( cli::Lexer lexer ) : m_lexer ( std::move ( lexer ) ), m_current ( m_lexer.nextToken ( ) ) { + Parser ( cli::Lexer lexer ) : m_lexer ( lexer ), m_current ( m_lexer.nextToken ( true ) ) { + } + + const cli::Lexer & getLexer ( ) const { + return m_lexer; } template < class ... Tokens > @@ -43,14 +47,14 @@ public: bool match ( Token token, Tokens ... tokens ) { if ( ! check ( token, tokens ... ) ) throw exception::CommonException ( std::string ( "Mismatched token while matching a token " ) + Lexer::tokenTypeToString ( token ) + "." ); - m_current = m_lexer.nextToken ( ); + m_current = m_lexer.nextToken ( false ); return true; } bool match_nonreserved_kw ( const std::string & kw ) { if ( ! check_nonreserved_kw ( kw ) ) throw exception::CommonException ( "Mismatched token while matching a token." ); - m_current = m_lexer.nextToken ( ); + m_current = m_lexer.nextToken ( false ); return true; } @@ -58,7 +62,7 @@ public: if ( ! check ( Lexer::TokenType::IDENTIFIER ) ) throw exception::CommonException ( "Mismatched token while matching a token." ); std::string res = m_current.m_value; - m_current = m_lexer.nextToken ( ); + m_current = m_lexer.nextToken ( false ); return res; } @@ -66,7 +70,7 @@ public: if ( ! check ( Lexer::TokenType::STRING ) ) throw exception::CommonException ( "Mismatched token while matching a token." ); std::string res = m_current.m_value; - m_current = m_lexer.nextToken ( ); + m_current = m_lexer.nextToken ( false ); return res; } @@ -74,7 +78,7 @@ public: if ( ! check ( Lexer::TokenType::INTEGER ) ) throw exception::CommonException ( "Mismatched token while matching a token." ); int res = ext::from_string < int > ( m_current.m_value ); - m_current = m_lexer.nextToken ( ); + m_current = m_lexer.nextToken ( false ); return res; } diff --git a/aql2/src/prompt/Prompt.cpp b/aql2/src/prompt/Prompt.cpp index 31bcdb496fb9234af772eda1b3363e17fa1997bd..35c2c7c0a9fd615024304299bb8b0504d4ac4a6a 100644 --- a/aql2/src/prompt/Prompt.cpp +++ b/aql2/src/prompt/Prompt.cpp @@ -20,20 +20,6 @@ #include <factory/XmlDataFactory.hpp> #include <global/GlobalData.h> -char * Prompt::stripwhite ( char * begin ) { - while ( isspace ( * begin ) ) - ++ begin; - - char * end = begin + strlen ( begin ) - 1; - while ( end >= begin && isspace ( * end ) ) - -- end; - - if ( end >= begin ) - * ++ end = '\0'; - - return begin; -} - Prompt::Prompt ( cli::Environment environment ) : m_prefix ( "> " ), m_history_file ( std::string ( std::getenv ( "HOME" ) ) + "/.aql_history" ), m_environment ( std::move ( environment ) ) { read_history ( m_history_file.c_str ( ) ); } @@ -46,28 +32,32 @@ cli::Command::Result Prompt::run ( ) { cli::Command::Result state = cli::Command::Result::OK; while ( state != cli::Command::Result::QUIT && state != cli::Command::Result::ERROR ) { - char * line = readline ( m_prefix.c_str ( ) ); + auto callback = [ first { true } ] ( ) mutable -> std::string { + char * line = readline ( first ? "> ": "+ " ); - if ( ! line ) - break; + if ( ! line ) + return ""; - char * s = stripwhite ( line ); + std::string res ( line ); + res += "\n"; + free ( line ); - if ( * s ) { - state = execute_line ( std::string ( s ) ); - } + if ( ! std::all_of ( res.begin ( ), res.end ( ), isspace ) ) + first = false; - free ( line ); + return res; + }; + state = execute_line ( callback ); } return state; } class HistoryRegister { - cli::Lexer & m_lexer; + const cli::Lexer & m_lexer; public: - HistoryRegister ( cli::Lexer & lexer ) : m_lexer ( lexer ) { + HistoryRegister ( const cli::Lexer & lexer ) : m_lexer ( lexer ) { } ~HistoryRegister ( ) { @@ -77,9 +67,22 @@ public: cli::Command::Result Prompt::execute_line ( std::string line ) { try { - cli::Lexer lexer ( line ); - HistoryRegister historyRegister ( lexer ); - cli::Parser parser = cli::Parser ( lexer ); + cli::Parser parser = cli::Parser ( cli::Lexer ( line ) ); + HistoryRegister historyRegister ( parser.getLexer ( ) ); + return parser.parse ( )->run ( m_environment ); + } catch ( const exception::CommonException & exception ) { + factory::XmlDataFactory::toStdout ( exception ); + return cli::Command::Result::EXCEPTION; + } catch ( const std::exception & exception ) { + common::Streams::err << "Exception caught: " << exception.what ( ) << std::endl; + return cli::Command::Result::EXCEPTION; + } +} + +cli::Command::Result Prompt::execute_line ( std::function < std::string ( ) > callback ) { + try { + cli::Parser parser = cli::Parser ( cli::Lexer ( callback ) ); + HistoryRegister historyRegister ( parser.getLexer ( ) ); return parser.parse ( )->run ( m_environment ); } catch ( const exception::CommonException & exception ) { factory::XmlDataFactory::toStdout ( exception ); diff --git a/aql2/src/prompt/Prompt.h b/aql2/src/prompt/Prompt.h index 9304076ab8bd4c6918d969d3206a1be0f48bb417..505406f6fd24c6d0fcfbea8113a7550971075b6e 100644 --- a/aql2/src/prompt/Prompt.h +++ b/aql2/src/prompt/Prompt.h @@ -30,6 +30,7 @@ public: cli::Command::Result run ( ); cli::Command::Result execute_line ( std::string line ); + cli::Command::Result execute_line ( std::function < std::string ( ) > callback ); }; #endif /* _AQL_PROMPT_H_ */