[icinga-checkins] icinga.org: icinga2/master: Implement line continuation for " icinga2 console"

git at icinga.org git at icinga.org
Tue Feb 10 13:27:37 CET 2015


Module: icinga2
Branch: master
Commit: 0490fe73f7aa5b789bb975e234e7cadae12a1d98
URL:    https://git.icinga.org/?p=icinga2.git;a=commit;h=0490fe73f7aa5b789bb975e234e7cadae12a1d98

Author: Gunnar Beutner <gunnar at beutner.name>
Date:   Tue Feb 10 13:27:02 2015 +0100

Implement line continuation for "icinga2 console"

fixes #8133

---

 lib/base/exception.cpp        |    9 +++++--
 lib/base/exception.hpp        |    4 +++-
 lib/cli/consolecommand.cpp    |   30 ++++++++++++++++++++----
 lib/config/config_parser.yy   |   52 +++++++++++++++++++++++++++++------------
 lib/config/configcompiler.cpp |    2 +-
 lib/config/configcompiler.hpp |    1 +
 6 files changed, 74 insertions(+), 24 deletions(-)

diff --git a/lib/base/exception.cpp b/lib/base/exception.cpp
index da5be3e..5a13d2e 100644
--- a/lib/base/exception.cpp
+++ b/lib/base/exception.cpp
@@ -208,8 +208,8 @@ ScriptError::ScriptError(const String& message)
 	: m_Message(message)
 { }
 
-ScriptError::ScriptError(const String& message, const DebugInfo& di)
-	: m_Message(message), m_DebugInfo(di)
+ScriptError::ScriptError(const String& message, const DebugInfo& di, bool incompleteExpr)
+	: m_Message(message), m_DebugInfo(di), m_IncompleteExpr(incompleteExpr)
 { }
 
 ScriptError::~ScriptError(void) throw()
@@ -225,6 +225,11 @@ DebugInfo ScriptError::GetDebugInfo(void) const
 	return m_DebugInfo;
 }
 
+bool ScriptError::IsIncompleteExpression(void) const
+{
+	return m_IncompleteExpr;;
+}
+
 posix_error::posix_error(void)
 	: m_Message(NULL)
 { }
diff --git a/lib/base/exception.hpp b/lib/base/exception.hpp
index a74aa04..3270a33 100644
--- a/lib/base/exception.hpp
+++ b/lib/base/exception.hpp
@@ -50,16 +50,18 @@ class I2_BASE_API ScriptError : virtual public user_error
 {
 public:
 	ScriptError(const String& message);
-	ScriptError(const String& message, const DebugInfo& di);
+	ScriptError(const String& message, const DebugInfo& di, bool incompleteExpr = false);
 	~ScriptError(void) throw();
 
 	virtual const char *what(void) const throw();
 
 	DebugInfo GetDebugInfo(void) const;
+	bool IsIncompleteExpression(void) const;
 
 private:
 	String m_Message;
 	DebugInfo m_DebugInfo;
+	bool m_IncompleteExpr;
 };
 
 I2_BASE_API StackTrace *GetLastExceptionStack(void);
diff --git a/lib/cli/consolecommand.cpp b/lib/cli/consolecommand.cpp
index 3f37dae..7e2c2c2 100644
--- a/lib/cli/consolecommand.cpp
+++ b/lib/cli/consolecommand.cpp
@@ -80,22 +80,37 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vector<std::stri
 		String fileName = "<" + Convert::ToString(next_line) + ">";
 		next_line++;
 
+		bool continuation = false;
+		std::string command;
+
+incomplete:
+
 		std::cout << ConsoleColorTag(Console_ForegroundCyan)
 		    << fileName
-		    << ConsoleColorTag(Console_ForegroundRed)
-		    << " => "
-		    << ConsoleColorTag(Console_Normal);
+		    << ConsoleColorTag(Console_ForegroundRed);
+
+		if (!continuation)
+			std::cout << " => ";
+		else
+			std::cout << " .. ";
+
+		std::cout << ConsoleColorTag(Console_Normal);
 
 		std::string line;
 		std::getline(std::cin, line);
 
+		if (!command.empty())
+			command += "\n";
+
+		command += line;
+
 		if (addr.IsEmpty()) {
 			Expression *expr;
 
 			try {
-				lines[fileName] = line;
+				lines[fileName] = command;
 
-				expr = ConfigCompiler::CompileText(fileName, line);
+				expr = ConfigCompiler::CompileText(fileName, command);
 
 				if (expr) {
 					Value result = expr->Evaluate(frame);
@@ -107,6 +122,11 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vector<std::stri
 					std::cout << ConsoleColorTag(Console_Normal) << "\n";
 				}
 			} catch (const ScriptError& ex) {
+				if (ex.IsIncompleteExpression()) {
+					continuation = true;
+					goto incomplete;
+				}
+
 				DebugInfo di = ex.GetDebugInfo();
 
 				if (lines.find(di.Path) != lines.end()) {
diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy
index 7b17164..817f6aa 100644
--- a/lib/config/config_parser.yy
+++ b/lib/config/config_parser.yy
@@ -236,9 +236,10 @@ int yylex(YYSTYPE *lvalp, YYLTYPE *llocp, void *scanner);
 
 extern int yydebug;
 
-void yyerror(const YYLTYPE *locp, std::vector<std::pair<Expression *, EItemInfo> > *, ConfigCompiler *, const char *err)
+void yyerror(const YYLTYPE *locp, std::vector<std::pair<Expression *, EItemInfo> > *, ConfigCompiler *context, const char *err)
 {
-	BOOST_THROW_EXCEPTION(ScriptError(err, *locp));
+	bool incomplete = context->m_Eof && (context->m_OpenBraces > 0);
+	BOOST_THROW_EXCEPTION(ScriptError(err, *locp, incomplete));
 }
 
 int yyparse(std::vector<std::pair<Expression *, EItemInfo> > *llist, ConfigCompiler *context);
@@ -372,11 +373,13 @@ type: T_TYPE identifier
 
 typerulelist: '{'
 	{
+		context->m_OpenBraces++;
 		context->m_RuleLists.push(new TypeRuleList());
 	}
 	typerules
 	'}'
 	{
+		context->m_OpenBraces--;
 		$$ = new Value(context->m_RuleLists.top());
 		context->m_RuleLists.pop();
 	}
@@ -681,46 +684,65 @@ rterm_items_inner: rterm
 	}
 	;
 
-rterm_array: '[' newlines rterm_items ']'
+rterm_array: '['
 	{
-		$$ = new ArrayExpression(*$3, @$);
-		delete $3;
+		context->m_OpenBraces++;
 	}
-	| '[' rterm_items ']'
+	newlines rterm_items ']'
 	{
-		$$ = new ArrayExpression(*$2, @$);
-		delete $2;
+		context->m_OpenBraces--;
+		$$ = new ArrayExpression(*$4, @$);
+		delete $4;
+	}
+	| '['
+	{
+		context->m_OpenBraces++;
+	}
+	rterm_items ']'
+	{
+		$$ = new ArrayExpression(*$3, @$);
+		delete $3;
 	}
 	;
 
-rterm_scope_require_side_effect: '{' statements '}'
+rterm_scope_require_side_effect: '{'
 	{
+		context->m_OpenBraces++;
+	}
+	statements '}'
+	{
+		context->m_OpenBraces--;
 		std::vector<Expression *> dlist;
 		typedef std::pair<Expression *, EItemInfo> EListItem;
 		int num = 0;
-		BOOST_FOREACH(const EListItem& litem, *$2) {
+		BOOST_FOREACH(const EListItem& litem, *$3) {
 			if (!litem.second.SideEffect)
 				yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used.");
 			dlist.push_back(litem.first);
 			num++;
 		}
-		delete $2;
+		delete $3;
 		$$ = new DictExpression(dlist, @$);
 	}
 	;
 
-rterm_scope: '{' statements '}'
+rterm_scope: '{'
+	{
+		context->m_OpenBraces++;
+	}
+	statements '}'
 	{
+		context->m_OpenBraces--;
 		std::vector<Expression *> dlist;
 		typedef std::pair<Expression *, EItemInfo> EListItem;
 		int num = 0;
-		BOOST_FOREACH(const EListItem& litem, *$2) {
-			if (!litem.second.SideEffect && num != $2->size() - 1)
+		BOOST_FOREACH(const EListItem& litem, *$3) {
+			if (!litem.second.SideEffect && num != $3->size() - 1)
 				yyerror(&litem.second.DebugInfo, NULL, NULL, "Value computed is not used.");
 			dlist.push_back(litem.first);
 			num++;
 		}
-		delete $2;
+		delete $3;
 		$$ = new DictExpression(dlist, @$);
 	}
 	;
diff --git a/lib/config/configcompiler.cpp b/lib/config/configcompiler.cpp
index 6882065..8b087c7 100644
--- a/lib/config/configcompiler.cpp
+++ b/lib/config/configcompiler.cpp
@@ -39,7 +39,7 @@ std::vector<String> ConfigCompiler::m_IncludeSearchDirs;
  * @param zone The zone.
  */
 ConfigCompiler::ConfigCompiler(const String& path, std::istream *input, const String& zone)
-	: m_Path(path), m_Input(input), m_Zone(zone), m_Eof(false), m_IgnoreNewlines(0)
+	: m_Path(path), m_Input(input), m_Zone(zone), m_Eof(false), m_IgnoreNewlines(0), m_OpenBraces(0)
 {
 	InitializeScanner();
 }
diff --git a/lib/config/configcompiler.hpp b/lib/config/configcompiler.hpp
index dec3239..b1f7697 100644
--- a/lib/config/configcompiler.hpp
+++ b/lib/config/configcompiler.hpp
@@ -118,6 +118,7 @@ private:
 
 public:
 	bool m_Eof;
+	int m_OpenBraces;
 
 	int m_IgnoreNewlines;
 	std::ostringstream m_LexBuffer;



More information about the icinga-checkins mailing list